Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2026-06-11 10:58:19 +02:00
commit 12e7ea038f
28 changed files with 2650 additions and 3146 deletions

View File

@ -39,8 +39,8 @@ Identify:
| Field | Source | Rule |
|-------|--------|------|
| **Title** | PR title | Rewrite from user perspective. Strip leading emoji prefixes (`:bug:`, `:sparkles:`, `:tada:`). Focus on observable behavior. Use imperative mood. |
| **Labels** | PR labels | Copy user-facing labels (`bug`, `enhancement`, `community contribution`). Skip workflow labels (`backport candidate`, `team-qa`). |
| **Title** | PR title | Rewrite from user perspective. Strip leading emoji prefixes (`:bug:`, `:sparkles:`, `:tada:`). Focus on observable behavior. Use imperative mood. Use the `issue-title` skill to generate this. |
| **Labels** | PR labels | Copy `community contribution` if present. Skip `bug` and `enhancement` (redundant with Issue Type). Skip workflow labels (`backport candidate`, `team-qa`). |
| **Milestone** | PR milestone | **Always copy what's on the PR.** Fetch with: `gh pr view <PR_NUMBER> --json milestone --jq '.milestone.title'` If the PR has no milestone, create the issue without one. |
| **Project** | Always `Main` | Penpot uses the `Main` project (number 8) for all issues. |
| **Body** | PR's user-facing section | Extract steps to reproduce or feature description. Omit internal details. Use templates below. |
@ -101,8 +101,7 @@ Create:
gh issue create \
--repo penpot/penpot \
--title "<Title>" \
--label "<label1>" \
--label "<label2>" \
--label "community contribution" \ # only if PR has this label
--milestone "<milestone>" \
--project "Main" \
--body-file /tmp/issue-body.md
@ -198,12 +197,10 @@ rm -f /tmp/issue-body.md /tmp/pr-body.md
| PR has | Issue gets |
|--------|-----------|
| `bug` | `bug` |
| `enhancement` | `enhancement` |
| `community contribution` | `community contribution` |
| `bug`, `enhancement` | *(skip — redundant with Issue Type)* |
| `backport candidate` | *(skip — workflow label)* |
| `team-qa` | *(skip — workflow label)* |
| No user-facing label | Infer from title: `:bug:``bug`, `:sparkles:``enhancement` |
## Issue Type mapping

View File

@ -48,7 +48,7 @@ python3 tools/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
**Exclusion rules (issue-level):**
- `no changelog` label — Chore/refactor work that doesn't need a changelog entry
- `release blocker` label — Blocked issues not yet ready for changelog
- `Task` issue type — Internal chores are not user-facing; filter these out after fetching
- `Task` issue type — Internal chores are not user-facing; automatically excluded by `gh.py`. Use `--include-tasks` to override.
- **Rejected project status** — Issues with a "Rejected" status in the "Main" project board are automatically excluded by `gh.py`. This project-level status (independent of the GitHub issue `state`) indicates the issue was rejected from the release. Use `--include-rejected` to override.
**Exclusion rules (PR-level):**
@ -347,71 +347,233 @@ if closed:
- <fix description> (by @contributor) [#<ISSUE>](https://github.com/penpot/penpot/issues/<ISSUE>) (PR: [#<PR>](https://github.com/penpot/penpot/pull/<PR>))
```
### 10. Cross-reference milestone PRs against the changelog
### 11. Generate anomaly report and save to CHANGES-ISSUES.md
Issues can be fixed by PRs that aren't in the milestone, and merged PRs in
the milestone may not close any tracked issue. After writing, run a full
cross-reference to catch gaps:
After all edits and cross-referencing are complete, generate a structured
anomaly report and save it to `CHANGES-ISSUES.md` (overwriting if exists).
This provides a persistent record of any discrepancies between the milestone
and the changelog.
Run this self-contained script:
```bash
# List all merged PRs in the milestone
python3 tools/gh.py prs --milestone "<MILESTONE>" --state merged > /tmp/milestone-prs.json
python3 << 'PYEOF'
import json, re, subprocess, sys
# Extract PR numbers from the changelog section
python3 -c "
import json, re
MILESTONE = "<MILESTONE>"
CHANGES_MD = "CHANGES.md"
OUTPUT = "CHANGES-ISSUES.md"
with open('CHANGES.md') as f:
# Fetch milestone issues (all states)
result = subprocess.run(
["python3", "tools/gh.py", "issues", MILESTONE, "--state", "all"],
capture_output=True, text=True)
all_issues = json.loads(result.stdout)
issue_by_num = {i['number']: i for i in all_issues}
# Fetch milestone PRs (all states)
result = subprocess.run(
["python3", "tools/gh.py", "prs", "--milestone", MILESTONE, "--state", "all"],
capture_output=True, text=True)
all_prs = json.loads(result.stdout)
pr_by_num = {p['number']: p for p in all_prs}
# Read changelog
with open(CHANGES_MD) as f:
content = f.read()
# Extract the version section (adjust regex to match the actual version)
match = re.search(r'## <MILESTONE> \(Unreleased\)\n(.*?)(?:\n## |\Z)', content, re.DOTALL)
section = match.group(1)
m = re.search(rf'## {MILESTONE} \(Unreleased\)\n(.*?)(?:\n## |\Z)', content, re.DOTALL)
section = m.group(1) if m else ""
# Collect issue and PR references from the changelog section
changelog_issues = set()
for num in re.findall(r'\[#(\d+)\]\(https://github\.com/penpot/penpot/issues/\d+\)', section):
changelog_issues.add(int(num))
for num in re.findall(r'\[Github #(\d+)\]', section):
changelog_issues.add(int(num))
# Collect all PR numbers referenced
changelog_prs = set()
for m in re.findall(r'\[#(\d+)\]\(https://github\.com/penpot/penpot/pull/\d+\)', section):
changelog_prs.add(int(m))
for num in re.findall(r'\[#(\d+)\]\(https://github\.com/penpot/penpot/pull/\d+\)', section):
changelog_prs.add(int(num))
for num in re.findall(r'PR:\[(\d+)\]', section):
changelog_prs.add(int(num))
# Collect all milestone PRs (filtered)
with open('/tmp/milestone-prs.json') as f:
milestone_prs = json.load(f)
# Determine valid (non-excluded) milestone issues
EXCLUDED_LABELS = {'release blocker', 'no changelog'}
EXCLUDED_ISSUE_TYPES = {'Task'}
EXCLUDED_PROJECT_STATUS = {'Rejected'}
milestone_merged = {pr['number'] for pr in milestone_prs}
valid_issues = []
for issue in all_issues:
labels = set(issue.get('labels', []))
if issue.get('state') != 'CLOSED': continue
if issue.get('issue_type') in EXCLUDED_ISSUE_TYPES: continue
if issue.get('project_status') in EXCLUDED_PROJECT_STATUS: continue
if EXCLUDED_LABELS & labels: continue
valid_issues.append(issue)
valid_nums = {i['number'] for i in valid_issues}
# PRs in milestone but not in changelog
missing = sorted(milestone_merged - changelog_prs)
print(f'Milestone merged PRs: {len(milestone_merged)}')
print(f'Changelog referenced PRs: {len(changelog_prs)}')
print(f'PRs in milestone but NOT in changelog: {len(missing)}')
for num in missing:
pr = next(p for p in milestone_prs if p['number'] == num)
print(f' #{num} {pr[\"title\"][:80]}')
"
# --- Gather anomalies ---
anomalies = []
# Type 1: Entries in changelog that should be excluded
for num in sorted(changelog_issues):
issue = issue_by_num.get(num)
if issue is None:
anomalies.append({
'type': 'should_remove',
'severity': 'HIGH',
'number': num,
'title': '',
'reason': 'Issue not found in milestone (deleted or moved)'
})
continue
labels = set(issue.get('labels', []))
reasons = []
if issue.get('state') != 'CLOSED':
reasons.append(f'state is "{issue["state"]}" (should be CLOSED)')
if 'release blocker' in labels:
reasons.append('has "release blocker" label')
if 'no changelog' in labels:
reasons.append('has "no changelog" label')
if issue.get('issue_type') == 'Task':
reasons.append(f'issue_type is Task (internal chore)')
if issue.get('project_status') == 'Rejected':
reasons.append('project_status is Rejected')
if reasons:
anomalies.append({
'type': 'should_remove',
'severity': 'MEDIUM' if issue.get('issue_type') == 'Task' else 'HIGH',
'number': num,
'title': issue.get('title', '')[:80],
'reason': '; '.join(reasons)
})
# Type 2: Valid issues not in changelog
for num in sorted(valid_nums - changelog_issues):
issue = issue_by_num[num]
info = {
'type': 'missing',
'severity': 'MEDIUM',
'number': num,
'title': issue['title'][:80],
'issue_type': issue['issue_type'],
'closing_prs': issue.get('closing_prs', []),
'note': ''
}
# Check for duplicate (same PR as existing entry)
existing = []
for pr_num in issue.get('closing_prs', []):
for cl_num in changelog_issues:
cl_issue = issue_by_num.get(cl_num)
if cl_issue and pr_num in cl_issue.get('closing_prs', []):
existing.append(f'#{cl_num}')
if existing:
info['note'] = f'DUPLICATE: same PR as existing entry(ies): {", ".join(existing)}'
# Check closing PRs not merged
unmerged = []
for pr_num in issue.get('closing_prs', []):
pr = pr_by_num.get(pr_num)
if pr is None:
unmerged.append(f'#{pr_num} (unknown)')
elif pr.get('state') != 'MERGED':
unmerged.append(f'#{pr_num} (state={pr["state"]})')
if unmerged:
info['note'] = (info['note'] + '; ' if info['note'] else '') + f'Closing PRs not merged: {", ".join(unmerged)}'
anomalies.append(info)
# Type 3: PRs in changelog that are not merged
for pr_num in sorted(changelog_prs):
pr = pr_by_num.get(pr_num)
if pr is None:
anomalies.append({
'type': 'unmerged_pr',
'severity': 'HIGH',
'number': pr_num,
'title': '',
'reason': 'PR not found in milestone PR list'
})
elif pr.get('state') != 'MERGED':
anomalies.append({
'type': 'unmerged_pr',
'severity': 'HIGH',
'number': pr_num,
'title': pr.get('title', '')[:80],
'reason': f'state={pr["state"]} (should be MERGED)'
})
# --- Write report to CHANGES-ISSUES.md ---
with open(OUTPUT, 'w') as f:
f.write(f'# Changelog Anomaly Report — {MILESTONE}\n\n')
f.write(f'Generated: {__import__("datetime").datetime.now().strftime("%Y-%m-%d %H:%M UTC")}\n\n')
f.write('---\n\n')
# Summary
n_remove = sum(1 for a in anomalies if a['type'] == 'should_remove')
n_missing = sum(1 for a in anomalies if a['type'] == 'missing')
n_pr = sum(1 for a in anomalies if a['type'] == 'unmerged_pr')
f.write(f'## Summary\n\n')
f.write(f'- **Issues to remove from changelog:** {n_remove}\n')
f.write(f'- **Valid issues missing from changelog:** {n_missing}\n')
f.write(f'- **Unmerged PRs referenced:** {n_pr}\n')
f.write(f'- **Total anomalies:** {len(anomalies)}\n\n')
if not anomalies:
f.write('✅ No anomalies found. The changelog is fully consistent with the milestone.\n\n')
else:
# Type 1
if n_remove:
f.write(f'## Issues to Remove\n\n')
f.write('These entries are in the changelog but should be excluded based on current issue metadata.\n\n')
for a in anomalies:
if a['type'] != 'should_remove': continue
badge = '🔴' if a['severity'] == 'HIGH' else '🟡'
f.write(f'{badge} **#{a["number"]}**')
if a.get('title'): f.write(f' — {a["title"]}')
f.write(f'\n - Reason: {a["reason"]}\n\n')
# Type 2
if n_missing:
f.write(f'## Valid Issues Not in Changelog\n\n')
f.write('These issues are closed, non-excluded milestone items that lack a changelog entry.\n\n')
for a in anomalies:
if a['type'] != 'missing': continue
f.write(f'❓ **#{a["number"]}** — {a["title"]}\n')
f.write(f' - Type: {a["issue_type"]}, Closing PRs: {a["closing_prs"]}\n')
if a.get('note'): f.write(f' - Note: {a["note"]}\n')
f.write('\n')
# Type 3
if n_pr:
f.write(f'## Unmerged PRs Referenced in Changelog\n\n')
f.write('These PR numbers appear in the changelog but are not merged.\n\n')
for a in anomalies:
if a['type'] != 'unmerged_pr': continue
f.write(f'🔴 **#{a["number"]}**')
if a.get('title'): f.write(f' — {a["title"]}')
f.write(f'\n - {a["reason"]}\n\n')
# Appendix: counts
f.write('---\n\n')
f.write(f'## Context\n\n')
f.write(f'- Milestone total issues (all states): {len(all_issues)}\n')
f.write(f'- Valid issues after exclusions: {len(valid_issues)}\n')
f.write(f'- Issues referenced in changelog: {len(changelog_issues)}\n')
f.write(f'- PRs referenced in changelog: {len(changelog_prs)}\n')
print(f"Anomaly report written to {OUTPUT}")
PYEOF
```
For each missing PR found, decide whether it should be added to the
changelog or is legitimately excluded (check its labels).
This generates `CHANGES-ISSUES.md` with three sections:
1. **Issues to Remove** — Entries in the changelog that should be excluded based
on current issue metadata (labels, type, project status, or deletion).
2. **Valid Issues Not in Changelog** — Closed, non-excluded milestone issues
that lack a changelog entry (with notes on duplicates and unmerged closing PRs).
3. **Unmerged PRs Referenced** — PRs in the changelog that are not merged.
Also verify that no closed-unmerged PRs remain in the changelog:
```bash
python3 tools/gh.py prs --milestone "<MILESTONE>" --state all | python3 -c "
import json, sys
data = json.load(sys.stdin)
closed = [p for p in data if p['state'] == 'CLOSED']
if closed:
print('WARNING: CLOSED (unmerged) PRs in milestone:')
for p in closed:
print(f' #{p[\"number\"]} {p[\"title\"][:80]}')
"
```
**Post-edit audit checklist:**
- ✅ All referenced PRs are merged (no closed-unmerged artifacts)
- ✅ Every merged milestone PR is either in the changelog or excluded by label
- ✅ PR and issue counts are internally consistent
- ✅ No false-positive PR-to-issue associations
The report is overwritten each time it's generated, reflecting the current
state of the milestone and changelog.
## Key Principles

View File

@ -138,7 +138,6 @@
- Duplicate token group [#9638](https://github.com/penpot/penpot/issues/9638) (PR: [#8886](https://github.com/penpot/penpot/pull/8886))
- Copy token name from contextual menu [#9639](https://github.com/penpot/penpot/issues/9639) (PR: [#8566](https://github.com/penpot/penpot/pull/8566))
- Add drag-to-change for numeric inputs in workspace sidebar (by @RenzoMXD) [#2466](https://github.com/penpot/penpot/issues/2466) (PR: [#8536](https://github.com/penpot/penpot/pull/8536))
- Add CSS linter [#9636](https://github.com/penpot/penpot/issues/9636) (PR: [#8592](https://github.com/penpot/penpot/pull/8592))
- Add per-group add button for typographies (by @eureka0928) [#5275](https://github.com/penpot/penpot/issues/5275) (PR: [#8895](https://github.com/penpot/penpot/pull/8895))
- Add Find & Replace for text content and layer names (by @statxc) [#7108](https://github.com/penpot/penpot/issues/7108) (PR: [#8899](https://github.com/penpot/penpot/pull/8899), [#9687](https://github.com/penpot/penpot/pull/9687))
- Use page name for multi-export ZIP/PDF downloads (by @Dexterity104) [#8773](https://github.com/penpot/penpot/issues/8773) (PR: [#8874](https://github.com/penpot/penpot/pull/8874))
@ -169,10 +168,9 @@
- Preserve Inkscape labels when pasting SVGs (by @jeffrey701) [#7869](https://github.com/penpot/penpot/issues/7869) (PR: [#9252](https://github.com/penpot/penpot/pull/9252))
- Add Alt+click to expand layer subtree (by @MilosM348) [#7736](https://github.com/penpot/penpot/issues/7736) (PR: [#9179](https://github.com/penpot/penpot/pull/9179))
- Allow deleting the profile avatar after uploading (by @moorsecopers99) [#9067](https://github.com/penpot/penpot/issues/9067) (PR: [#9068](https://github.com/penpot/penpot/pull/9068))
- Clarify self-hosted OIDC configuration for containerized (by @sancfc) [#9764](https://github.com/penpot/penpot/issues/9764) (PR: [#9758](https://github.com/penpot/penpot/pull/9758))
- Update User Guide with 2.16 features (by @myfunnyandy) [#9767](https://github.com/penpot/penpot/issues/9767) (PR: [#9768](https://github.com/penpot/penpot/pull/9768))
- Improve file validation performance and fix orphan shape detection [#9790](https://github.com/penpot/penpot/issues/9790) (PR: [#9789](https://github.com/penpot/penpot/pull/9789))
- Add v2.16 release notes (What's new modal) [#9945](https://github.com/penpot/penpot/issues/9945) (PR: [#9940](https://github.com/penpot/penpot/pull/9940))
- Enable multi-instance horizontal scaling for MCP server [#10000](https://github.com/penpot/penpot/issues/10000) (PR: [#10013](https://github.com/penpot/penpot/pull/10013))
### :bug: Bugs fixed
@ -234,7 +232,6 @@
- Fix `:heigth` typo in clipboard frame-same-size? (by @iot2edge) [#9249](https://github.com/penpot/penpot/issues/9249) (PR: [#9250](https://github.com/penpot/penpot/pull/9250))
- Fix Settings Update button enabled state (by @moorsecopers99) [#9090](https://github.com/penpot/penpot/issues/9090) (PR: [#9091](https://github.com/penpot/penpot/pull/9091))
- Fix library updates reappearing after reload [#9326](https://github.com/penpot/penpot/issues/9326) (PR: [#9563](https://github.com/penpot/penpot/pull/9563))
- Fix dependency libraries visible after unlinking main library [#9331](https://github.com/penpot/penpot/issues/9331) (PR: [#9511](https://github.com/penpot/penpot/pull/9511))
- Fix internal error on margins [#9309](https://github.com/penpot/penpot/issues/9309) (PR: [#9311](https://github.com/penpot/penpot/pull/9311))
- Remove drag-to-change when token applied on numeric input [#9313](https://github.com/penpot/penpot/issues/9313) (PR: [#9314](https://github.com/penpot/penpot/pull/9314))
- Fix extra input on canvas background [#9359](https://github.com/penpot/penpot/issues/9359) (PR: [#9360](https://github.com/penpot/penpot/pull/9360))
@ -242,20 +239,16 @@
- Fix several color picker issues [#9556](https://github.com/penpot/penpot/issues/9556) (PR: [#9558](https://github.com/penpot/penpot/pull/9558))
- Fix asset icon broken on Asset tab [#9587](https://github.com/penpot/penpot/issues/9587) (PR: [#9612](https://github.com/penpot/penpot/pull/9612))
- Fix text fill color stops updating in multiselect with texts [#9608](https://github.com/penpot/penpot/issues/9608) (PR: [#9549](https://github.com/penpot/penpot/pull/9549))
- Fix editing a legacy text element silently detaches its color token [#9255](https://github.com/penpot/penpot/issues/9255) (PR: [#9525](https://github.com/penpot/penpot/pull/9525))
- Fix token application to grid paddings [#9494](https://github.com/penpot/penpot/issues/9494) (PR: [#9630](https://github.com/penpot/penpot/pull/9630))
- Fix file crashing when switching a variant [#9259](https://github.com/penpot/penpot/issues/9259) (PR: [#9147](https://github.com/penpot/penpot/pull/9147))
- Fix set activation after renaming [#9329](https://github.com/penpot/penpot/issues/9329) (PR: [#9545](https://github.com/penpot/penpot/pull/9545))
- Fix font selection position hiding available fonts [#9489](https://github.com/penpot/penpot/issues/9489) (PR: [#9499](https://github.com/penpot/penpot/pull/9499))
- Fix numeric input changes not saved when clicking on viewport [#9491](https://github.com/penpot/penpot/issues/9491) (PR: [#9548](https://github.com/penpot/penpot/pull/9548))
- Fix resize cursor appearing on login and register buttons [#9505](https://github.com/penpot/penpot/issues/9505) (PR: [#9590](https://github.com/penpot/penpot/pull/9590))
- Fix version restore restoring first previewed version instead of selected one [#9588](https://github.com/penpot/penpot/issues/9588) (PR: [#9626](https://github.com/penpot/penpot/pull/9626))
- Fix incorrect error message when applying tokens while editing text [#9620](https://github.com/penpot/penpot/issues/9620) (PR: [#9708](https://github.com/penpot/penpot/pull/9708))
- Fix standalone tokens ordering separated from token groups [#9733](https://github.com/penpot/penpot/issues/9733) (PR: [#9736](https://github.com/penpot/penpot/pull/9736))
- Fix delete invitation modal readability in light theme [#9737](https://github.com/penpot/penpot/issues/9737) (PR: [#9747](https://github.com/penpot/penpot/pull/9747))
- Fix team invitation not automatically accepted after account validation [#9776](https://github.com/penpot/penpot/issues/9776) (PR: [#9782](https://github.com/penpot/penpot/pull/9782))
- Fix design tokens vanishing from the sidebar when a token name collides with a token-group prefix from another active set (e.g. `a` in one set and `a.b` in another); the colliding token is now kept and rendered as a broken pill [Github #9584](https://github.com/penpot/penpot/issues/9584)
- Fix Plugin API addRulerGuide creating guides on page instead of board (by @girafic) [#8225](https://github.com/penpot/penpot/issues/8225) (PR: [#8632](https://github.com/penpot/penpot/pull/8632))
- Fix text editor not swapping correctly when enabling/disabling WebGL [#10015](https://github.com/penpot/penpot/issues/10015)
- Fix WebGL renderer focus mode leaving artefacts [#10061](https://github.com/penpot/penpot/issues/10061) (PR: [#10091](https://github.com/penpot/penpot/pull/10091))
- Fix double click on text selecting underlying element when WebGL render is enabled [#10080](https://github.com/penpot/penpot/issues/10080) (PR: [#10123](https://github.com/penpot/penpot/pull/10123))
- Fix publishing or unpublishing file as library failing with unexpected state found error [#10094](https://github.com/penpot/penpot/issues/10094) (PR: [#10093](https://github.com/penpot/penpot/pull/10093))
- Fix team invitation failing when email address contains consecutive dots in domain [#10097](https://github.com/penpot/penpot/issues/10097) (PR: [#10096](https://github.com/penpot/penpot/pull/10096))
- Add detailed error messages for unspecified import errors [#9759](https://github.com/penpot/penpot/issues/9759) (PR: [#9886](https://github.com/penpot/penpot/pull/9886))
## 2.15.4

View File

@ -4,9 +4,9 @@
:deps
{penpot/common {:local/root "../common"}
org.clojure/clojure {:mvn/version "1.12.5"}
org.clojure/tools.namespace {:mvn/version "1.5.0"}
org.clojure/tools.namespace {:mvn/version "1.5.1"}
com.github.luben/zstd-jni {:mvn/version "1.5.7-4"}
com.github.luben/zstd-jni {:mvn/version "1.5.7-10"}
io.prometheus/simpleclient {:mvn/version "0.16.0"}
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"}
@ -17,7 +17,7 @@
io.prometheus/simpleclient_httpserver {:mvn/version "0.16.0"}
io.lettuce/lettuce-core {:mvn/version "7.5.1.RELEASE"}
io.lettuce/lettuce-core {:mvn/version "7.6.0.RELEASE"}
;; Minimal dependencies required by lettuce, we need to include them
;; explicitly because clojure dependency management does not support
;; yet the BOM format.
@ -25,7 +25,7 @@
io.micrometer/micrometer-observation {:mvn/version "1.14.2"}
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
com.google.guava/guava {:mvn/version "33.4.8-jre"}
com.google.guava/guava {:mvn/version "33.6.0-jre"}
funcool/yetti
{:git/tag "v11.10"
@ -34,13 +34,13 @@
:exclusions [org.slf4j/slf4j-api]}
com.github.seancorfield/next.jdbc
{:mvn/version "1.3.1093"}
{:mvn/version "1.3.1108"}
metosin/reitit-core {:mvn/version "0.9.1"}
metosin/reitit-core {:mvn/version "0.10.1"}
nrepl/nrepl {:mvn/version "1.7.0"}
org.postgresql/postgresql {:mvn/version "42.7.11"}
org.xerial/sqlite-jdbc {:mvn/version "3.50.3.0"}
org.xerial/sqlite-jdbc {:mvn/version "3.53.2.0"}
com.zaxxer/HikariCP {:mvn/version "7.0.2"}
@ -51,7 +51,7 @@
com.github.ben-manes.caffeine/caffeine {:mvn/version "3.2.4"}
org.jsoup/jsoup {:mvn/version "1.21.2"}
org.jsoup/jsoup {:mvn/version "1.22.2"}
org.im4java/im4java
{:git/tag "1.4.0-penpot-2"
:git/sha "e2b3e16"
@ -63,18 +63,18 @@
org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"}
dawran6/emoji {:mvn/version "0.2.0"}
markdown-clj/markdown-clj {:mvn/version "1.12.4"}
markdown-clj/markdown-clj {:mvn/version "1.12.8"}
;; Pretty Print specs
pretty-spec/pretty-spec {:mvn/version "0.1.4"}
software.amazon.awssdk/s3 {:mvn/version "2.44.4"}
software.amazon.awssdk/sts {:mvn/version "2.44.4"}}
software.amazon.awssdk/s3 {:mvn/version "2.46.7"}
software.amazon.awssdk/sts {:mvn/version "2.46.7"}}
:paths ["src" "resources" "target/classes"]
:aliases
{:dev
{:extra-deps
{com.bhauman/rebel-readline {:mvn/version "0.1.5"}
{com.bhauman/rebel-readline {:mvn/version "0.1.7"}
clojure-humanize/clojure-humanize {:mvn/version "0.2.2"}
org.clojure/data.csv {:mvn/version "1.1.1"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "2.0.0-beta1"}
@ -83,7 +83,7 @@
:build
{:extra-deps
{io.github.clojure/tools.build {:mvn/version "0.10.10"}}
{io.github.clojure/tools.build {:mvn/version "0.10.14"}}
:ns-default build}
:test

View File

@ -62,7 +62,7 @@
(def default
{:database-uri "postgresql://postgres/penpot_test"
:redis-uri "redis://redis/1"
:redis-uri "redis://valkey/1"
:auto-file-snapshot-every 1
:file-data-backend "db"})

View File

@ -6,7 +6,7 @@
org.clojure/data.fressian {:mvn/version "1.1.1"}
org.clojure/clojurescript {:mvn/version "1.12.42"}
org.apache.commons/commons-pool2 {:mvn/version "2.12.1"}
org.apache.commons/commons-pool2 {:mvn/version "2.13.1"}
;; Logging
org.apache.logging.log4j/log4j-api {:mvn/version "2.26.0"}
@ -15,12 +15,12 @@
org.apache.logging.log4j/log4j-jul {:mvn/version "2.26.0"}
org.apache.logging.log4j/log4j-slf4j2-impl {:mvn/version "2.26.0"}
org.slf4j/slf4j-api {:mvn/version "2.0.18"}
pl.tkowalcz.tjahzi/log4j2-appender {:mvn/version "0.9.41"}
pl.tkowalcz.tjahzi/log4j2-appender {:mvn/version "0.9.42"}
selmer/selmer {:mvn/version "1.13.1"}
selmer/selmer {:mvn/version "1.13.4"}
criterium/criterium {:mvn/version "0.4.6"}
metosin/jsonista {:mvn/version "0.3.13"}
metosin/jsonista {:mvn/version "1.0.0"}
metosin/malli {:mvn/version "0.19.1"}
expound/expound {:mvn/version "0.9.0"}
@ -55,7 +55,7 @@
:aliases
{:dev
{:extra-deps
{org.clojure/tools.namespace {:mvn/version "1.5.0"}
{org.clojure/tools.namespace {:mvn/version "1.5.1"}
thheller/shadow-cljs {:mvn/version "3.2.0"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "2.0.0-beta1"}
com.bhauman/rebel-readline {:mvn/version "0.1.5"}
@ -65,7 +65,7 @@
:build
{:extra-deps
{io.github.clojure/tools.build {:mvn/version "0.10.10"}}
{io.github.clojure/tools.build {:mvn/version "0.10.14"}}
:ns-default build}
:test

View File

@ -4,21 +4,21 @@
"license": "MPL-2.0",
"author": "Kaleidos INC Sucursal en España SL",
"private": true,
"packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268",
"packageManager": "pnpm@11.5.3+sha512.7ac1c919341c213a34dc0d02afb7143c5c26ac26ee8c4782deea821b8ac64d2134a081fd8941dae6e29bbb48f58dfc2b7fbceeccc07cb2f09d219d342a4969ed",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
},
"devDependencies": {
"concurrently": "^9.1.2",
"nodemon": "^3.1.10",
"prettier": "3.5.3",
"concurrently": "^10.0.3",
"nodemon": "^3.1.14",
"prettier": "3.8.4",
"source-map-support": "^0.5.21",
"ws": "^8.18.2"
"ws": "^8.21.0"
},
"dependencies": {
"date-fns": "^4.1.0"
"date-fns": "^4.4.0"
},
"scripts": {
"lint:clj": "clj-kondo --parallel=true --lint src/",

288
common/pnpm-lock.yaml generated
View File

@ -9,48 +9,50 @@ importers:
.:
dependencies:
date-fns:
specifier: ^4.1.0
version: 4.1.0
specifier: ^4.4.0
version: 4.4.0
devDependencies:
concurrently:
specifier: ^9.1.2
version: 9.2.1
specifier: ^10.0.3
version: 10.0.3
nodemon:
specifier: ^3.1.10
version: 3.1.11
specifier: ^3.1.14
version: 3.1.14
prettier:
specifier: 3.5.3
version: 3.5.3
specifier: 3.8.4
version: 3.8.4
source-map-support:
specifier: ^0.5.21
version: 0.5.21
ws:
specifier: ^8.18.2
version: 8.18.3
specifier: ^8.21.0
version: 8.21.0
packages:
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-regex@6.2.2:
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
engines: {node: '>=12'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ansi-styles@6.2.3:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
balanced-match@4.0.4:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
brace-expansion@5.0.6:
resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
engines: {node: 18 || 20 || >=22}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
@ -59,35 +61,25 @@ packages:
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
chalk@5.6.2:
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
cliui@9.0.1:
resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==}
engines: {node: '>=20'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
concurrently@9.2.1:
resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==}
engines: {node: '>=18'}
concurrently@10.0.3:
resolution: {integrity: sha512-hc3LH4UaKWd/bbyDK/IGVa4RB6PtQ3CUYwtrkzqHn+wIG3Hr5fhpRlk0L/gCa8ZE1L/Ufj50Zho69cI5w8SQBA==}
engines: {node: '>=22'}
hasBin: true
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
date-fns@4.4.0:
resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
@ -98,8 +90,8 @@ packages:
supports-color:
optional: true
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@10.6.0:
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
@ -118,6 +110,10 @@ packages:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
get-east-asian-width@1.6.0:
resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==}
engines: {node: '>=18'}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@ -126,10 +122,6 @@ packages:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
ignore-by-default@1.0.1:
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
@ -141,10 +133,6 @@ packages:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
@ -153,14 +141,15 @@ packages:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nodemon@3.1.11:
resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
nodemon@3.1.14:
resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==}
engines: {node: '>=10'}
hasBin: true
@ -168,12 +157,12 @@ packages:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
picomatch@2.3.2:
resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
engines: {node: '>=8.6'}
prettier@3.5.3:
resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
prettier@3.8.4:
resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==}
engines: {node: '>=14'}
hasBin: true
@ -184,20 +173,16 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
semver@7.8.4:
resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==}
engines: {node: '>=10'}
hasBin: true
shell-quote@1.8.3:
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
shell-quote@1.8.4:
resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==}
engines: {node: '>= 0.4'}
simple-update-notifier@2.0.0:
@ -211,26 +196,22 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
string-width@7.2.0:
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
engines: {node: '>=18'}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-ansi@7.2.0:
resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
engines: {node: '>=12'}
supports-color@10.2.2:
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
engines: {node: '>=18'}
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@ -249,12 +230,12 @@ packages:
undefsafe@2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrap-ansi@9.0.2:
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
engines: {node: '>=18'}
ws@8.18.3:
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
ws@8.21.0:
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@ -269,35 +250,32 @@ packages:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
yargs-parser@22.0.0:
resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
yargs@18.0.0:
resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
snapshots:
ansi-regex@5.0.1: {}
ansi-regex@6.2.2: {}
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
ansi-styles@6.2.3: {}
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
picomatch: 2.3.2
balanced-match@1.0.2: {}
balanced-match@4.0.4: {}
binary-extensions@2.3.0: {}
brace-expansion@1.1.12:
brace-expansion@5.0.6:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
balanced-match: 4.0.4
braces@3.0.3:
dependencies:
@ -305,10 +283,7 @@ snapshots:
buffer-from@1.1.2: {}
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
chalk@5.6.2: {}
chokidar@3.6.0:
dependencies:
@ -322,30 +297,22 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
cliui@8.0.1:
cliui@9.0.1:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
string-width: 7.2.0
strip-ansi: 7.2.0
wrap-ansi: 9.0.2
color-convert@2.0.1:
concurrently@10.0.3:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
concat-map@0.0.1: {}
concurrently@9.2.1:
dependencies:
chalk: 4.1.2
chalk: 5.6.2
rxjs: 7.8.2
shell-quote: 1.8.3
supports-color: 8.1.1
shell-quote: 1.8.4
supports-color: 10.2.2
tree-kill: 1.2.2
yargs: 17.7.2
yargs: 18.0.0
date-fns@4.1.0: {}
date-fns@4.4.0: {}
debug@4.4.3(supports-color@5.5.0):
dependencies:
@ -353,7 +320,7 @@ snapshots:
optionalDependencies:
supports-color: 5.5.0
emoji-regex@8.0.0: {}
emoji-regex@10.6.0: {}
escalade@3.2.0: {}
@ -366,14 +333,14 @@ snapshots:
get-caller-file@2.0.5: {}
get-east-asian-width@1.6.0: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
has-flag@3.0.0: {}
has-flag@4.0.0: {}
ignore-by-default@1.0.1: {}
is-binary-path@2.1.0:
@ -382,28 +349,26 @@ snapshots:
is-extglob@2.1.1: {}
is-fullwidth-code-point@3.0.0: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-number@7.0.0: {}
minimatch@3.1.2:
minimatch@10.2.5:
dependencies:
brace-expansion: 1.1.12
brace-expansion: 5.0.6
ms@2.1.3: {}
nodemon@3.1.11:
nodemon@3.1.14:
dependencies:
chokidar: 3.6.0
debug: 4.4.3(supports-color@5.5.0)
ignore-by-default: 1.0.1
minimatch: 3.1.2
minimatch: 10.2.5
pstree.remy: 1.1.8
semver: 7.7.3
semver: 7.8.4
simple-update-notifier: 2.0.0
supports-color: 5.5.0
touch: 3.1.1
@ -411,29 +376,27 @@ snapshots:
normalize-path@3.0.0: {}
picomatch@2.3.1: {}
picomatch@2.3.2: {}
prettier@3.5.3: {}
prettier@3.8.4: {}
pstree.remy@1.1.8: {}
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
require-directory@2.1.1: {}
picomatch: 2.3.2
rxjs@7.8.2:
dependencies:
tslib: 2.8.1
semver@7.7.3: {}
semver@7.8.4: {}
shell-quote@1.8.3: {}
shell-quote@1.8.4: {}
simple-update-notifier@2.0.0:
dependencies:
semver: 7.7.3
semver: 7.8.4
source-map-support@0.5.21:
dependencies:
@ -442,28 +405,22 @@ snapshots:
source-map@0.6.1: {}
string-width@4.2.3:
string-width@7.2.0:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
emoji-regex: 10.6.0
get-east-asian-width: 1.6.0
strip-ansi: 7.2.0
strip-ansi@6.0.1:
strip-ansi@7.2.0:
dependencies:
ansi-regex: 5.0.1
ansi-regex: 6.2.2
supports-color@10.2.2: {}
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
supports-color@8.1.1:
dependencies:
has-flag: 4.0.0
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@ -476,24 +433,23 @@ snapshots:
undefsafe@2.0.5: {}
wrap-ansi@7.0.0:
wrap-ansi@9.0.2:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
ansi-styles: 6.2.3
string-width: 7.2.0
strip-ansi: 7.2.0
ws@8.18.3: {}
ws@8.21.0: {}
y18n@5.0.8: {}
yargs-parser@21.1.1: {}
yargs-parser@22.0.0: {}
yargs@17.7.2:
yargs@18.0.0:
dependencies:
cliui: 8.0.1
cliui: 9.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
string-width: 7.2.0
y18n: 5.0.8
yargs-parser: 21.1.1
yargs-parser: 22.0.0

View File

@ -0,0 +1 @@
minimumReleaseAge: 0

View File

@ -159,7 +159,7 @@ goog.scope(function () {
it1--, i++
) {
carry += (256 * b58[it1]) >>> 0;
b58[it1] = carry % BASE >>> 0;
b58[it1] = (carry % BASE) >>> 0;
carry = (carry / BASE) >>> 0;
}
if (carry !== 0) {
@ -214,7 +214,7 @@ goog.scope(function () {
it3--, i++
) {
carry += (BASE * b256[it3]) >>> 0;
b256[it3] = carry % 256 >>> 0;
b256[it3] = (carry % 256) >>> 0;
carry = (carry / 256) >>> 0;
}
if (carry !== 0) {

View File

@ -448,18 +448,22 @@
::oapi/type "string"
::oapi/format "uuid"}})
(def email-re #"[a-zA-Z0-9_.+-\\\\]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+")
;; Strict email regex aligned with app.common.spec/email-re.
;; Local part: valid RFC chars, no leading/trailing dot, no consecutive dots.
;; Domain: labels can't start/end with hyphen, no empty labels.
;; TLD: at least 2 alphabetic chars.
(def email-re
#"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,63}")
(defn parse-email
[s]
(if (string? s)
(first (re-seq email-re s))
nil))
(when (and (string? s) (re-matches email-re s))
s))
(defn email-string?
[s]
(and (string? s)
(re-seq email-re s)))
(some? (re-matches email-re s))))
(register!
{:type ::email

View File

@ -217,11 +217,11 @@ goog.scope(function () {
// Parse ........-....-....-####-............
int8[8] = (rest = parseInt(uuid.slice(19, 23), 16)) >>> 8;
(int8[9] = rest & 0xff),
((int8[9] = rest & 0xff),
// Parse ........-....-....-....-############
// (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes)
(int8[10] =
((rest = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff);
((rest = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff));
int8[11] = (rest / 0x100000000) & 0xff;
int8[12] = (rest >>> 24) & 0xff;
int8[13] = (rest >>> 16) & 0xff;

View File

@ -188,3 +188,60 @@
(t/is (= false (decode-s "f")))
(t/is (= true (decode-s "1")))
(t/is (= false (decode-s "0")))))
(t/deftest test-email-validation
(t/testing "accepts well-formed email addresses"
(doseq [email ["user@domain.com"
"user.name@domain.com"
"user+tag@domain.com"
"user-name@domain.com"
"user_name@domain.com"
"user123@domain.com"
"USER@DOMAIN.COM"
"u@domain.io"
"user@sub.domain.com"
"user@domain.co.uk"
"user@domain.dev"
"a@bc.co"]]
(t/is (sm/validate ::sm/email email) (str "should accept: " email))
(t/is (= email (sm/decode ::sm/email email sm/json-transformer)))))
(t/testing "rejects domain with consecutive dots (dot-dot)"
(t/is (false? (sm/validate ::sm/email "user@gmail.com..")))
(t/is (false? (sm/validate ::sm/email "user@sub..domain.com")))
(t/is (false? (sm/validate ::sm/email "eissaalbothigi@gmail.com..")))
(t/is (nil? (sm/parse-email "user@gmail.com..")))
(t/is (nil? (sm/parse-email "eissaalbothigi@gmail.com.."))))
(t/testing "rejects domain ending with a dot"
(t/is (false? (sm/validate ::sm/email "user@domain.")))
(t/is (nil? (sm/parse-email "user@domain."))))
(t/testing "rejects domain starting with a dot"
(t/is (false? (sm/validate ::sm/email "user@.domain.com")))
(t/is (nil? (sm/parse-email "user@.domain.com"))))
(t/testing "rejects local part with consecutive dots"
(t/is (false? (sm/validate ::sm/email "user..name@domain.com")))
(t/is (nil? (sm/parse-email "user..name@domain.com"))))
(t/testing "rejects local part starting with a dot"
(t/is (false? (sm/validate ::sm/email ".user@domain.com")))
(t/is (nil? (sm/parse-email ".user@domain.com"))))
(t/testing "rejects label starting or ending with hyphen"
(t/is (false? (sm/validate ::sm/email "user@-domain.com")))
(t/is (false? (sm/validate ::sm/email "user@domain-.com"))))
(t/testing "rejects TLD shorter than 2 chars"
(t/is (false? (sm/validate ::sm/email "user@domain.c"))))
(t/testing "rejects domain without a dot"
(t/is (false? (sm/validate ::sm/email "user@domain"))))
(t/testing "rejects empty or malformed emails"
(t/is (false? (sm/validate ::sm/email "")))
(t/is (false? (sm/validate ::sm/email "@domain.com")))
(t/is (false? (sm/validate ::sm/email "user@")))
(t/is (false? (sm/validate ::sm/email "userdomain.com")))
(t/is (false? (sm/validate ::sm/email "user@@domain.com")))))

View File

@ -4,29 +4,29 @@
"license": "MPL-2.0",
"author": "Kaleidos INC Sucursal en España SL",
"private": true,
"packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268",
"packageManager": "pnpm@11.5.3+sha512.7ac1c919341c213a34dc0d02afb7143c5c26ac26ee8c4782deea821b8ac64d2134a081fd8941dae6e29bbb48f58dfc2b7fbceeccc07cb2f09d219d342a4969ed",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
},
"type": "module",
"dependencies": {
"archiver": "7.0.1",
"archiver": "8.0.0",
"cookies": "^0.9.1",
"date-fns": "^4.1.0",
"date-fns": "^4.4.0",
"generic-pool": "^3.9.0",
"inflation": "^2.1.0",
"ioredis": "^5.10.1",
"ioredis": "^5.11.1",
"playwright": "^1.60.0",
"raw-body": "^3.0.2",
"source-map-support": "^0.5.21",
"svgo": "penpot/svgo#v3.1",
"undici": "^8.2.0",
"undici": "^8.4.1",
"xml-js": "^1.6.11",
"xregexp": "^5.1.2"
},
"devDependencies": {
"ws": "^8.20.1"
"ws": "^8.21.0"
},
"scripts": {
"clear:shadow-cache": "rm -rf .shadow-cljs && rm -rf target",

489
exporter/pnpm-lock.yaml generated
View File

@ -9,14 +9,14 @@ importers:
.:
dependencies:
archiver:
specifier: 7.0.1
version: 7.0.1
specifier: 8.0.0
version: 8.0.0
cookies:
specifier: ^0.9.1
version: 0.9.1
date-fns:
specifier: ^4.1.0
version: 4.1.0
specifier: ^4.4.0
version: 4.4.0
generic-pool:
specifier: ^3.9.0
version: 3.9.0
@ -24,8 +24,8 @@ importers:
specifier: ^2.1.0
version: 2.1.0
ioredis:
specifier: ^5.10.1
version: 5.10.1
specifier: ^5.11.1
version: 5.11.1
playwright:
specifier: ^1.60.0
version: 1.60.0
@ -39,8 +39,8 @@ importers:
specifier: penpot/svgo#v3.1
version: https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180
undici:
specifier: ^8.2.0
version: 8.2.0
specifier: ^8.4.1
version: 8.4.1
xml-js:
specifier: ^1.6.11
version: 1.6.11
@ -49,8 +49,8 @@ importers:
version: 5.1.2
devDependencies:
ws:
specifier: ^8.20.1
version: 8.20.1
specifier: ^8.21.0
version: 8.21.0
packages:
@ -58,16 +58,8 @@ packages:
resolution: {integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==}
engines: {node: '>=6.9.0'}
'@ioredis/commands@1.5.1':
resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@ioredis/commands@1.10.0':
resolution: {integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==}
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
@ -77,29 +69,9 @@ packages:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-regex@6.2.2:
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
engines: {node: '>=12'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ansi-styles@6.2.3:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
archiver-utils@5.0.2:
resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==}
engines: {node: '>= 14'}
archiver@7.0.1:
resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==}
engines: {node: '>= 14'}
archiver@8.0.0:
resolution: {integrity: sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==}
engines: {node: '>=18'}
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
@ -112,19 +84,20 @@ packages:
react-native-b4a:
optional: true
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
balanced-match@4.0.4:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
bare-events@2.8.2:
resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==}
bare-events@2.9.1:
resolution: {integrity: sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==}
peerDependencies:
bare-abort-controller: '*'
peerDependenciesMeta:
bare-abort-controller:
optional: true
bare-fs@4.7.1:
resolution: {integrity: sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw==}
bare-fs@4.7.2:
resolution: {integrity: sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==}
engines: {bare: '>=1.16.0'}
peerDependencies:
bare-buffer: '*'
@ -136,8 +109,8 @@ packages:
resolution: {integrity: sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==}
engines: {bare: '>=1.14.0'}
bare-path@3.0.0:
resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==}
bare-path@3.0.1:
resolution: {integrity: sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==}
bare-stream@2.13.1:
resolution: {integrity: sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow==}
@ -153,8 +126,8 @@ packages:
bare-events:
optional: true
bare-url@2.4.3:
resolution: {integrity: sha512-Kccpc7ACfXaxfeInfqKcZtW4pT5YBn1mesc4sCsun6sRwtbJ4h+sNOaksUpYEJUKfN65YWC6Bw2OJEFiKxq8nQ==}
bare-url@2.4.5:
resolution: {integrity: sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@ -162,8 +135,9 @@ packages:
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
brace-expansion@2.1.0:
resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==}
brace-expansion@5.0.6:
resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
engines: {node: 18 || 20 || >=22}
buffer-crc32@1.0.0:
resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
@ -179,20 +153,13 @@ packages:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
cluster-key-slot@1.1.2:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
cluster-key-slot@1.1.1:
resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==}
engines: {node: '>=0.10.0'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
compress-commons@6.0.2:
resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
engines: {node: '>= 14'}
compress-commons@7.0.1:
resolution: {integrity: sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==}
engines: {node: '>=18'}
cookies@0.9.1:
resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
@ -209,13 +176,9 @@ packages:
engines: {node: '>=0.8'}
hasBin: true
crc32-stream@6.0.0:
resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==}
engines: {node: '>= 14'}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
crc32-stream@7.0.1:
resolution: {integrity: sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==}
engines: {node: '>=18'}
css-select@5.2.2:
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
@ -236,8 +199,8 @@ packages:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
date-fns@4.4.0:
resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
@ -269,15 +232,6 @@ packages:
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@ -296,10 +250,6 @@ packages:
fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
foreground-child@3.3.1:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -309,14 +259,6 @@ packages:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
glob@10.5.0:
resolution: {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
hasBin: true
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
@ -335,27 +277,17 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
ioredis@5.10.1:
resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==}
ioredis@5.11.1:
resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==}
engines: {node: '>=12.22.0'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
is-stream@4.0.1:
resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
engines: {node: '>=18'}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
keygrip@1.1.0:
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
engines: {node: '>= 0.6'}
@ -365,35 +297,18 @@ packages:
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
engines: {node: '>= 0.6.3'}
lodash.defaults@4.2.0:
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
lodash.isarguments@3.1.0:
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
mdn-data@2.0.28:
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
mdn-data@2.12.2:
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
minimatch@5.1.9:
resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==}
engines: {node: '>=10'}
minimatch@9.0.9:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'}
minipass@7.1.3:
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
engines: {node: '>=16 || 14 >=14.17'}
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@ -405,17 +320,6 @@ packages:
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
path-scurry@1.11.1:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
playwright-core@1.60.0:
resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==}
engines: {node: '>=18'}
@ -444,8 +348,9 @@ packages:
resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
readdir-glob@1.1.3:
resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
readdir-glob@3.0.0:
resolution: {integrity: sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==}
engines: {node: '>=18'}
redis-errors@1.2.0:
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
@ -470,18 +375,6 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@ -500,16 +393,8 @@ packages:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
streamx@2.25.0:
resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
streamx@2.27.0:
resolution: {integrity: sha512-WZ189TKnHoAokYHvwzaAQMpd55cgUmFIcJFzBSgGcb886jau5DL+XdDhTWV4ps3FLvk+OORp0dLRTPsLZ21CSA==}
string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
@ -517,16 +402,8 @@ packages:
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-ansi@7.2.0:
resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
engines: {node: '>=12'}
svgo@https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180:
resolution: {tarball: https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180}
resolution: {gitHosted: true, tarball: https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180}
version: 4.0.0
engines: {node: '>=16.0.0'}
@ -547,8 +424,8 @@ packages:
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
engines: {node: '>=0.6.x'}
undici@8.2.0:
resolution: {integrity: sha512-Z+4Hx9GE26Lh9Upwfnc8C7SsrpBPGaM/Gm6kMFtiG7c+5IvQKlXi/t+9x9DrrCh29cww5TSP9YdVaBcnLDs5fQ==}
undici@8.4.1:
resolution: {integrity: sha512-RNHlB4fxZK0IrkhBsxhlbx7s8kFWwr7rzzOqj5nvZugw3ig3RsB7KW3zVlV0eu8POl+rx5d1hmL7rRg0z1owow==}
engines: {node: '>=22.19.0'}
unpipe@1.0.0:
@ -558,21 +435,8 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
ws@8.20.1:
resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==}
ws@8.21.0:
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@ -590,9 +454,9 @@ packages:
xregexp@5.1.2:
resolution: {integrity: sha512-6hGgEMCGhqCTFEJbqmWrNIPqfpdirdGWkqshu7fFZddmTSfgv5Sn9D2SaKloR79s5VUiUlpwzg3CM3G6D3VIlw==}
zip-stream@6.0.1:
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
engines: {node: '>= 14'}
zip-stream@7.0.5:
resolution: {integrity: sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==}
engines: {node: '>=18'}
snapshots:
@ -600,19 +464,7 @@ snapshots:
dependencies:
core-js-pure: 3.47.0
'@ioredis/commands@1.5.1': {}
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
string-width-cjs: string-width@4.2.3
strip-ansi: 7.2.0
strip-ansi-cjs: strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
'@pkgjs/parseargs@0.11.0':
optional: true
'@ioredis/commands@1.10.0': {}
'@trysound/sax@0.2.0': {}
@ -620,35 +472,17 @@ snapshots:
dependencies:
event-target-shim: 5.0.1
ansi-regex@5.0.1: {}
ansi-regex@6.2.2: {}
ansi-styles@4.3.0:
archiver@8.0.0:
dependencies:
color-convert: 2.0.1
ansi-styles@6.2.3: {}
archiver-utils@5.0.2:
dependencies:
glob: 10.5.0
graceful-fs: 4.2.11
is-stream: 2.0.1
lazystream: 1.0.1
lodash: 4.17.21
normalize-path: 3.0.0
readable-stream: 4.7.0
archiver@7.0.1:
dependencies:
archiver-utils: 5.0.2
async: 3.2.6
buffer-crc32: 1.0.0
is-stream: 4.0.1
lazystream: 1.0.1
normalize-path: 3.0.0
readable-stream: 4.7.0
readdir-glob: 1.1.3
readdir-glob: 3.0.0
tar-stream: 3.2.0
zip-stream: 6.0.1
zip-stream: 7.0.5
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
@ -658,16 +492,16 @@ snapshots:
b4a@1.8.1: {}
balanced-match@1.0.2: {}
balanced-match@4.0.4: {}
bare-events@2.8.2: {}
bare-events@2.9.1: {}
bare-fs@4.7.1:
bare-fs@4.7.2:
dependencies:
bare-events: 2.8.2
bare-path: 3.0.0
bare-stream: 2.13.1(bare-events@2.8.2)
bare-url: 2.4.3
bare-events: 2.9.1
bare-path: 3.0.1
bare-stream: 2.13.1(bare-events@2.9.1)
bare-url: 2.4.5
fast-fifo: 1.3.2
transitivePeerDependencies:
- bare-abort-controller
@ -675,30 +509,30 @@ snapshots:
bare-os@3.9.1: {}
bare-path@3.0.0:
bare-path@3.0.1:
dependencies:
bare-os: 3.9.1
bare-stream@2.13.1(bare-events@2.8.2):
bare-stream@2.13.1(bare-events@2.9.1):
dependencies:
streamx: 2.25.0
streamx: 2.27.0
teex: 1.0.1
optionalDependencies:
bare-events: 2.8.2
bare-events: 2.9.1
transitivePeerDependencies:
- react-native-b4a
bare-url@2.4.3:
bare-url@2.4.5:
dependencies:
bare-path: 3.0.0
bare-path: 3.0.1
base64-js@1.5.1: {}
boolbase@1.0.0: {}
brace-expansion@2.1.0:
brace-expansion@5.0.6:
dependencies:
balanced-match: 1.0.2
balanced-match: 4.0.4
buffer-crc32@1.0.0: {}
@ -711,19 +545,13 @@ snapshots:
bytes@3.1.2: {}
cluster-key-slot@1.1.2: {}
cluster-key-slot@1.1.1: {}
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
compress-commons@6.0.2:
compress-commons@7.0.1:
dependencies:
crc-32: 1.2.2
crc32-stream: 6.0.0
is-stream: 2.0.1
crc32-stream: 7.0.1
is-stream: 4.0.1
normalize-path: 3.0.0
readable-stream: 4.7.0
@ -738,17 +566,11 @@ snapshots:
crc-32@1.2.2: {}
crc32-stream@6.0.0:
crc32-stream@7.0.1:
dependencies:
crc-32: 1.2.2
readable-stream: 4.7.0
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
css-select@5.2.2:
dependencies:
boolbase: 1.0.0
@ -773,7 +595,7 @@ snapshots:
dependencies:
css-tree: 2.2.1
date-fns@4.1.0: {}
date-fns@4.4.0: {}
debug@4.4.3:
dependencies:
@ -801,19 +623,13 @@ snapshots:
domelementtype: 2.3.0
domhandler: 5.0.3
eastasianwidth@0.2.0: {}
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
entities@4.5.0: {}
event-target-shim@5.0.1: {}
events-universal@1.0.1:
dependencies:
bare-events: 2.8.2
bare-events: 2.9.1
transitivePeerDependencies:
- bare-abort-controller
@ -821,27 +637,11 @@ snapshots:
fast-fifo@1.3.2: {}
foreground-child@3.3.1:
dependencies:
cross-spawn: 7.0.6
signal-exit: 4.1.0
fsevents@2.3.2:
optional: true
generic-pool@3.9.0: {}
glob@10.5.0:
dependencies:
foreground-child: 3.3.1
jackspeak: 3.4.3
minimatch: 9.0.9
minipass: 7.1.3
package-json-from-dist: 1.0.1
path-scurry: 1.11.1
graceful-fs@4.2.11: {}
http-errors@2.0.1:
dependencies:
depd: 2.0.0
@ -860,34 +660,22 @@ snapshots:
inherits@2.0.4: {}
ioredis@5.10.1:
ioredis@5.11.1:
dependencies:
'@ioredis/commands': 1.5.1
cluster-key-slot: 1.1.2
'@ioredis/commands': 1.10.0
cluster-key-slot: 1.1.1
debug: 4.4.3
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
redis-errors: 1.2.0
redis-parser: 3.0.0
standard-as-callback: 2.1.0
transitivePeerDependencies:
- supports-color
is-fullwidth-code-point@3.0.0: {}
is-stream@2.0.1: {}
is-stream@4.0.1: {}
isarray@1.0.0: {}
isexe@2.0.0: {}
jackspeak@3.4.3:
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
keygrip@1.1.0:
dependencies:
tsscmp: 1.0.6
@ -896,27 +684,15 @@ snapshots:
dependencies:
readable-stream: 2.3.8
lodash.defaults@4.2.0: {}
lodash.isarguments@3.1.0: {}
lodash@4.17.21: {}
lru-cache@10.4.3: {}
mdn-data@2.0.28: {}
mdn-data@2.12.2: {}
minimatch@5.1.9:
minimatch@10.2.5:
dependencies:
brace-expansion: 2.1.0
minimatch@9.0.9:
dependencies:
brace-expansion: 2.1.0
minipass@7.1.3: {}
brace-expansion: 5.0.6
ms@2.1.3: {}
@ -926,15 +702,6 @@ snapshots:
dependencies:
boolbase: 1.0.0
package-json-from-dist@1.0.1: {}
path-key@3.1.1: {}
path-scurry@1.11.1:
dependencies:
lru-cache: 10.4.3
minipass: 7.1.3
playwright-core@1.60.0: {}
playwright@1.60.0:
@ -972,9 +739,9 @@ snapshots:
process: 0.11.10
string_decoder: 1.3.0
readdir-glob@1.1.3:
readdir-glob@3.0.0:
dependencies:
minimatch: 5.1.9
minimatch: 10.2.5
redis-errors@1.2.0: {}
@ -992,14 +759,6 @@ snapshots:
setprototypeof@1.2.0: {}
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@3.0.0: {}
signal-exit@4.1.0: {}
source-map-js@1.2.1: {}
source-map-support@0.5.21:
@ -1013,7 +772,7 @@ snapshots:
statuses@2.0.2: {}
streamx@2.25.0:
streamx@2.27.0:
dependencies:
events-universal: 1.0.1
fast-fifo: 1.3.2
@ -1022,18 +781,6 @@ snapshots:
- bare-abort-controller
- react-native-b4a
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
string-width@5.1.2:
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.2.0
string_decoder@1.1.1:
dependencies:
safe-buffer: 5.1.2
@ -1042,14 +789,6 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
strip-ansi@7.2.0:
dependencies:
ansi-regex: 6.2.2
svgo@https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180:
dependencies:
'@trysound/sax': 0.2.0
@ -1061,9 +800,9 @@ snapshots:
tar-stream@3.2.0:
dependencies:
b4a: 1.8.1
bare-fs: 4.7.1
bare-fs: 4.7.2
fast-fifo: 1.3.2
streamx: 2.25.0
streamx: 2.27.0
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
@ -1071,7 +810,7 @@ snapshots:
teex@1.0.1:
dependencies:
streamx: 2.25.0
streamx: 2.27.0
transitivePeerDependencies:
- bare-abort-controller
- react-native-b4a
@ -1086,29 +825,13 @@ snapshots:
tsscmp@1.0.6: {}
undici@8.2.0: {}
undici@8.4.1: {}
unpipe@1.0.0: {}
util-deprecate@1.0.2: {}
which@2.0.2:
dependencies:
isexe: 2.0.0
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi@8.1.0:
dependencies:
ansi-styles: 6.2.3
string-width: 5.1.2
strip-ansi: 7.2.0
ws@8.20.1: {}
ws@8.21.0: {}
xml-js@1.6.11:
dependencies:
@ -1118,8 +841,8 @@ snapshots:
dependencies:
'@babel/runtime-corejs3': 7.28.4
zip-stream@6.0.1:
zip-stream@7.0.5:
dependencies:
archiver-utils: 5.0.2
compress-commons: 6.0.2
compress-commons: 7.0.1
normalize-path: 3.0.0
readable-stream: 4.7.0

View File

@ -0,0 +1,2 @@
allowBuilds:
core-js-pure: false

View File

@ -7,7 +7,7 @@
(ns app.handlers.resources
"Temporal resources management."
(:require
["archiver$default" :as arc]
["archiver" :as arc]
["node:fs" :as fs]
["node:fs/promises" :as fsp]
["node:path" :as path]
@ -41,7 +41,7 @@
(defn create-zip
[& {:keys [resource on-complete on-progress on-error]}]
(let [^js zip (arc/create "zip")
(let [^js zip (new arc/ZipArchive)
^js out (fs/createWriteStream (:path resource))
on-complete (or on-complete (constantly nil))
progress (atom 0)]

View File

@ -4,7 +4,7 @@
"license": "MPL-2.0",
"author": "Kaleidos INC Sucursal en España SL",
"private": true,
"packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268",
"packageManager": "pnpm@11.5.3+sha512.7ac1c919341c213a34dc0d02afb7143c5c26ac26ee8c4782deea821b8ac64d2134a081fd8941dae6e29bbb48f58dfc2b7fbceeccc07cb2f09d219d342a4969ed",
"browserslist": [
"defaults"
],
@ -17,8 +17,8 @@
"build:app:assets": "node ./scripts/build-app-assets.js",
"build:storybook": "pnpm run build:storybook:assets && pnpm run build:storybook:cljs && storybook build",
"build:storybook:assets": "node ./scripts/build-storybook-assets.js",
"build:wasm": "../render-wasm/build",
"build:storybook:cljs": "clojure -M:dev:shadow-cljs compile storybook",
"build:wasm": "../render-wasm/build",
"build:app:libs": "node ./scripts/build-libs.js",
"build:app:main": "clojure -M:dev:shadow-cljs release main worker",
"build:app:worker": "clojure -M:dev:shadow-cljs release worker",
@ -46,34 +46,34 @@
"clear:wasm": "cargo clean --manifest-path ../render-wasm/Cargo.toml",
"watch": "exit 0",
"watch:app": "pnpm run clear:shadow-cache && pnpm run clear:wasm && pnpm run build:wasm && concurrently --kill-others-on-fail \"pnpm run watch:app:assets\" \"pnpm run watch:app:main\" \"pnpm run watch:app:libs\"",
"watch:storybook": "pnpm run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\"",
"watch:storybook": "pnpm run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 -h 0.0.0.0 --no-open\" \"node ./scripts/watch-storybook.js\"",
"postinstall": "(cd ../plugins/libs/plugins-runtime; pnpm install; pnpm run build)"
},
"devDependencies": {
"@penpot/draft-js": "workspace:./packages/draft-js",
"@penpot/mousetrap": "workspace:./packages/mousetrap",
"@penpot/draft-js": "link:packages/draft-js",
"@penpot/mousetrap": "link:packages/mousetrap",
"@penpot/plugins-runtime": "link:../plugins/libs/plugins-runtime",
"@penpot/svgo": "penpot/svgo#v3.2",
"@penpot/text-editor": "workspace:./text-editor",
"@penpot/tokenscript": "workspace:./packages/tokenscript",
"@penpot/ui": "workspace:./packages/ui",
"@penpot/svgo": "github:penpot/svgo#v3.2",
"@penpot/text-editor": "link:text-editor",
"@penpot/tokenscript": "link:packages/tokenscript",
"@penpot/ui": "link:packages/ui",
"@playwright/test": "1.60.0",
"@storybook/addon-docs": "10.3.5",
"@storybook/addon-themes": "10.3.5",
"@storybook/addon-vitest": "10.3.5",
"@storybook/react-vite": "10.3.5",
"@tokens-studio/sd-transforms": "1.2.11",
"@types/node": "^25.5.2",
"@vitest/browser": "4.1.3",
"@vitest/browser-playwright": "^4.1.3",
"@vitest/coverage-v8": "4.1.3",
"@storybook/addon-docs": "10.4.3",
"@storybook/addon-themes": "10.4.3",
"@storybook/addon-vitest": "10.4.3",
"@storybook/react-vite": "10.4.3",
"@tokens-studio/sd-transforms": "2.0.3",
"@types/node": "^25.9.2",
"@vitest/browser": "4.1.8",
"@vitest/browser-playwright": "^4.1.8",
"@vitest/coverage-v8": "4.1.8",
"@zip.js/zip.js": "2.8.26",
"autoprefixer": "^10.4.27",
"compression": "^1.8.1",
"concurrently": "^9.2.1",
"date-fns": "^4.1.0",
"concurrently": "^10.0.3",
"date-fns": "^4.4.0",
"esbuild": "^0.28.0",
"eventsource-parser": "^3.0.8",
"eventsource-parser": "^3.1.0",
"express": "^5.1.0",
"fancy-log": "^2.0.0",
"getopts": "^2.3.0",
@ -84,48 +84,47 @@
"lodash": "^4.18.1",
"lodash.debounce": "^4.0.8",
"map-stream": "0.0.7",
"marked": "^17.0.5",
"marked": "^18.0.5",
"mkdirp": "^3.0.1",
"mustache": "^4.2.0",
"nodemon": "^3.1.14",
"npm-run-all": "^4.1.5",
"opentype.js": "^1.3.4",
"opentype.js": "^2.0.0",
"p-limit": "^7.3.0",
"playwright": "1.60.0",
"postcss": "^8.5.8",
"postcss": "^8.5.15",
"postcss-clean": "^1.2.2",
"postcss-modules": "^6.0.1",
"postcss-scss": "^4.0.9",
"prettier": "3.8.1",
"prettier": "3.8.4",
"pretty-time": "^1.1.0",
"prop-types": "^15.8.1",
"randomcolor": "^0.6.2",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-error-boundary": "^6.1.1",
"react": "19.2.7",
"react-dom": "19.2.7",
"react-error-boundary": "^6.1.2",
"react-virtualized": "^9.22.6",
"rimraf": "^6.1.3",
"rxjs": "8.0.0-alpha.14",
"sass": "^1.98.0",
"sass-embedded": "^1.98.0",
"sax": "^1.4.1",
"sass": "^1.100.0",
"sass-embedded": "^1.100.0",
"sax": "^1.6.0",
"scheduler": "^0.27.0",
"source-map-support": "^0.5.21",
"storybook": "10.3.5",
"style-dictionary": "5.0.0-rc.1",
"stylelint": "^17.4.0",
"storybook": "10.4.3",
"style-dictionary": "5.4.4",
"stylelint": "^17.13.0",
"stylelint-config-standard-scss": "^17.0.0",
"stylelint-scss": "^7.0.0",
"stylelint-scss": "^7.2.0",
"stylelint-use-logical-spec": "^5.0.1",
"svg-sprite": "^2.0.4",
"tdigest": "^0.1.2",
"tinycolor2": "^1.6.0",
"typescript": "^6.0.2",
"ua-parser-js": "2.0.9",
"vite": "^8.0.7",
"vitest": "^4.1.3",
"ua-parser-js": "2.0.10",
"vite": "^8.0.16",
"vitest": "^4.1.8",
"wait-on": "^9.0.4",
"wasm-pack": "^0.13.1",
"watcher": "^2.3.1",
"workerpool": "^10.0.1",
"xregexp": "^5.1.2"

View File

@ -9,7 +9,7 @@
"license": "MPL-2.0",
"dependencies": {
"draft-js": "penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0",
"immutable": "^5.1.4"
"immutable": "^5.1.6"
},
"peerDependencies": {
"react": ">=0.17.0",

4401
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -9,3 +9,11 @@ patchedDependencies:
'@zip.js/zip.js@2.8.26': patches/@zip.js__zip.js@2.8.26.patch
shamefullyHoist: true
minimumReleaseAge: 0
allowBuilds:
'@parcel/watcher': true
canvas: true
core-js-pure: true
esbuild: true

View File

@ -37,7 +37,7 @@
}
.main-toolbar-hidden {
--toolbar-offset-y: -#{deprecated.$s-4};
--toolbar-offset-y: calc(-1 * #{deprecated.$s-4});
height: deprecated.$s-16;
z-index: deprecated.$z-index-1;

View File

@ -321,8 +321,16 @@
(filter #(or (root-frame-with-data? %)
(and (cfh/group-shape? objects %)
(not (contains? child-parent? %)))
;; WASM only: drop text from @hover unless the
;; cursor is over rendered glyphs, so clicks
;; pass through empty areas of the text box.
;; Skip this for shapes already in `selected`:
;; @hover drives on-click, and the first click
;; of a double-click would otherwise reselect
;; a parent/container after the pointer moves.
(and (features/active-feature? @st/state "render-wasm/v1")
(cfh/text-shape? (get objects %))
(not (contains? selected %))
(not (wasm.api/intersect-position-in-shape % @last-point-ref)))))))
remove-measure-xf

View File

@ -100,6 +100,7 @@
matching-font (some (fn [[_ font]]
(and (= (:font-id font) font-uuid)
(= (str (:font-weight font)) (str font-weight))
(= (:font-style font) font-style)
font))
(seq @fonts))]
(when matching-font

View File

@ -15,18 +15,18 @@
"test:watch:e2e": "vitest --browser"
},
"devDependencies": {
"@playwright/test": "^1.45.1",
"@types/node": "^25.0.3",
"@vitest/browser": "^1.6.0",
"@vitest/coverage-v8": "^1.6.0",
"@vitest/ui": "^1.6.0",
"esbuild": "^0.27.2",
"jsdom": "^27.4.0",
"canvas": "^3.2.1",
"playwright": "^1.45.1",
"prettier": "^3.7.4",
"vite": "^5.3.1",
"vitest": "^1.6.0"
"@playwright/test": "^1.60.0",
"@types/node": "^25.9.2",
"@vitest/browser": "^4.1.8",
"@vitest/coverage-v8": "^4.1.8",
"@vitest/ui": "^4.1.8",
"canvas": "^3.2.3",
"esbuild": "^0.28.0",
"jsdom": "^29.1.1",
"playwright": "^1.60.0",
"prettier": "^3.8.4",
"vite": "^8.0.16",
"vitest": "^4.1.8"
},
"packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268"
"packageManager": "pnpm@11.5.3+sha512.7ac1c919341c213a34dc0d02afb7143c5c26ac26ee8c4782deea821b8ac64d2134a081fd8941dae6e29bbb48f58dfc2b7fbceeccc07cb2f09d219d342a4969ed"
}

View File

@ -17,14 +17,22 @@ describe("Style", () => {
test("setStyles should apply multiple styles to an element using an Object", () => {
const element = document.createElement("div");
setStyles(element, [["display"]], {
"text-decoration": "none",
"font-size": "32px",
display: "none",
});
expect(element.style.display).toBe("");
expect(element.style.fontSize).toBe("");
expect(element.style.textDecoration).toBe("");
setStyles(
element,
[
["display"],
["font-size", "px"],
["text-decoration"],
],
{
"text-decoration": "none",
"font-size": "32",
display: "none",
},
);
expect(element.style.display).toBe("none");
expect(element.style.fontSize).toBe("32px");
expect(element.style.textDecoration).toBe("none");
});
test("setStyles should apply multiple styles to an element using a CSSStyleDeclaration", () => {
@ -32,13 +40,13 @@ describe("Style", () => {
setStyles(a, [["display"]], {
display: "none",
});
expect(a.style.display).toBe("");
expect(a.style.display).toBe("none");
expect(a.style.fontSize).toBe("");
expect(a.style.textDecoration).toBe("");
const b = document.createElement("div");
setStyles(b, [["display"]], a.style);
expect(b.style.display).toBe("");
expect(b.style.display).toBe("none");
expect(b.style.fontSize).toBe("");
expect(b.style.textDecoration).toBe("");
});

View File

@ -2,6 +2,7 @@ import path from "node:path";
import fs from "node:fs/promises";
import { defineConfig } from "vite";
import { coverageConfigDefaults } from "vitest/config";
import { playwright } from "@vitest/browser-playwright";
async function waitFor(timeInMillis) {
return new Promise((resolve) => setTimeout((_) => resolve(), timeInMillis));
@ -69,11 +70,7 @@ export default defineConfig({
enabled: true,
exclude: ["main.js", "**/scripts/**", ...coverageConfigDefaults.exclude],
},
poolOptions: {
threads: {
singleThread: true,
},
},
singleThread: true,
environmentOptions: {
jsdom: {
resources: "usable",
@ -81,7 +78,7 @@ export default defineConfig({
},
browser: {
name: "chromium",
provider: "playwright",
provider: playwright(),
},
exclude: ["main.js", "**/scripts/**", "**/node_modules/**", "**/dist/**"],
},

View File

@ -343,6 +343,13 @@ def cmd_issues(args: argparse.Namespace) -> None:
print(f"After excluding labels: {len(filtered)} issues", file=sys.stderr)
issues = filtered
# Exclude issues with type "Task" (internal chores) — opt out with --include-tasks
if not args.include_tasks:
tasks = [iss for iss in issues if iss.get("issue_type") == "Task"]
if tasks:
issues = [iss for iss in issues if iss.get("issue_type") != "Task"]
print(f"After excluding Task issues: {len(issues)} issues (removed {len(tasks)}: {[t['number'] for t in tasks]})", file=sys.stderr)
# Filter by included labels (--label) — issue must have ALL specified labels
if args.label:
inclusions = set(label.strip() for label in args.label.split(","))
@ -608,6 +615,10 @@ def main() -> None:
"--include-rejected", action="store_true",
help="Include issues with 'Rejected' project status (excluded by default)"
)
p_issues.add_argument(
"--include-tasks", action="store_true",
help="Include issues with type 'Task' (excluded by default, they are internal chores)"
)
p_issues.set_defaults(func=cmd_issues)
# --- prs ---