diff --git a/CHANGES.md b/CHANGES.md index 42e162059b..b86f924cca 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,14 @@ ### :bug: Bugs fixed - Fix incorrect handling of version restore operation [Github #9041](https://github.com/penpot/penpot/pull/9041) + + +## 2.14.4 + +### :bug: Bugs fixed + +- Fix email validation [Taiga #14006](https://tree.taiga.io/project/penpot/issue/14006) +- Fix email blacklisting [Github #9122](https://github.com/penpot/penpot/pull/9122) - Fix removeChild errors from unmount race conditions [Github #8927](https://github.com/penpot/penpot/pull/8927) diff --git a/backend/src/app/email/blacklist.clj b/backend/src/app/email/blacklist.clj index ca80afb6c9..a07dfccf91 100644 --- a/backend/src/app/email/blacklist.clj +++ b/backend/src/app/email/blacklist.clj @@ -36,10 +36,18 @@ :cause cause))))) (defn contains? - "Check if email is in the blacklist." + "Check if email is in the blacklist. Also matches subdomains: if + 'somedomain.com' is blacklisted, 'xxx@foo.somedomain.com' will also + be rejected." [{:keys [::email/blacklist]} email] - (let [[_ domain] (str/split email "@" 2)] - (c/contains? blacklist (str/lower domain)))) + (let [[_ domain] (str/split email "@" 2) + parts (str/split (str/lower domain) #"\.")] + (loop [parts parts] + (if (empty? parts) + false + (if (c/contains? blacklist (str/join "." parts)) + true + (recur (rest parts))))))) (defn enabled? "Check if the blacklist is enabled" diff --git a/backend/test/backend_tests/email_blacklist_test.clj b/backend/test/backend_tests/email_blacklist_test.clj new file mode 100644 index 0000000000..5cc043fe32 --- /dev/null +++ b/backend/test/backend_tests/email_blacklist_test.clj @@ -0,0 +1,34 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.email-blacklist-test + (:require + [app.email :as-alias email] + [app.email.blacklist :as blacklist] + [clojure.test :as t])) + +(def ^:private cfg + {::email/blacklist #{"somedomain.com" "spam.net"}}) + +(t/deftest test-exact-domain-match + (t/is (true? (blacklist/contains? cfg "user@somedomain.com"))) + (t/is (true? (blacklist/contains? cfg "user@spam.net"))) + (t/is (false? (blacklist/contains? cfg "user@legit.com")))) + +(t/deftest test-subdomain-match + (t/is (true? (blacklist/contains? cfg "user@sub.somedomain.com"))) + (t/is (true? (blacklist/contains? cfg "user@a.b.somedomain.com"))) + ;; A domain that merely contains the blacklisted string but is not a + ;; subdomain must NOT be rejected. + (t/is (false? (blacklist/contains? cfg "user@notsomedomain.com")))) + +(t/deftest test-case-insensitive + (t/is (true? (blacklist/contains? cfg "user@SOMEDOMAIN.COM"))) + (t/is (true? (blacklist/contains? cfg "user@Sub.SomeDomain.Com")))) + +(t/deftest test-non-blacklisted-domain + (t/is (false? (blacklist/contains? cfg "user@example.com"))) + (t/is (false? (blacklist/contains? cfg "user@sub.legit.com")))) diff --git a/common/src/app/common/spec.cljc b/common/src/app/common/spec.cljc index 38af563499..d6f0d6cacc 100644 --- a/common/src/app/common/spec.cljc +++ b/common/src/app/common/spec.cljc @@ -113,12 +113,19 @@ (tgen/fmap keyword))))) ;; --- SPEC: email +;; +;; Regex rules enforced: +;; 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-.]+") +(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] - (some->> s (re-seq email-re) first)) + (when (and (string? s) (re-matches email-re s)) + s)) (letfn [(conformer [v] (or (parse-email v) ::s/invalid)) @@ -126,11 +133,10 @@ (dm/str v))] (s/def ::email (s/with-gen (s/conformer conformer unformer) - #(as-> (tgen/let [p1 (s/gen ::not-empty-string) - p2 (s/gen ::not-empty-string) - p3 (tgen/elements ["com" "net"])] - (str p1 "@" p2 "." p3)) $ - (tgen/such-that (partial re-matches email-re) $ 50))))) + #(tgen/let [local (tgen/string-alphanumeric 1 20) + label (tgen/string-alphanumeric 2 10) + tld (tgen/elements ["com" "net" "org" "io" "co" "dev"])] + (str local "@" label "." tld))))) ;; -- SPEC: uri diff --git a/common/test/common_tests/runner.cljc b/common/test/common_tests/runner.cljc index 2d9a216cbc..b3c2ad4f0e 100644 --- a/common/test/common_tests/runner.cljc +++ b/common/test/common_tests/runner.cljc @@ -49,6 +49,7 @@ [common-tests.path-names-test] [common-tests.record-test] [common-tests.schema-test] + [common-tests.spec-test] [common-tests.svg-path-test] [common-tests.svg-test] [common-tests.text-test] @@ -122,6 +123,7 @@ 'common-tests.path-names-test 'common-tests.record-test 'common-tests.schema-test + 'common-tests.spec-test 'common-tests.svg-path-test 'common-tests.svg-test 'common-tests.text-test diff --git a/common/test/common_tests/spec_test.cljc b/common/test/common_tests/spec_test.cljc new file mode 100644 index 0000000000..425f7f8066 --- /dev/null +++ b/common/test/common_tests/spec_test.cljc @@ -0,0 +1,89 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns common-tests.spec-test + (:require + [app.common.spec :as spec] + [clojure.test :as t])) + +(t/deftest valid-emails + (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 (some? (spec/parse-email email)) (str "should accept: " email))))) + +(t/deftest rejects-invalid-local-part + (t/testing "rejects local part starting with a dot" + (t/is (nil? (spec/parse-email ".user@domain.com")))) + + (t/testing "rejects local part with consecutive dots" + (t/is (nil? (spec/parse-email "user..name@domain.com")))) + + (t/testing "rejects local part with spaces" + (t/is (nil? (spec/parse-email "us er@domain.com")))) + + (t/testing "rejects local part with comma" + (t/is (nil? (spec/parse-email "user,name@domain.com"))) + (t/is (nil? (spec/parse-email ",user@domain.com")))) + + (t/testing "rejects empty local part" + (t/is (nil? (spec/parse-email "@domain.com"))))) + +(t/deftest rejects-invalid-domain + (t/testing "rejects domain starting with a dot" + (t/is (nil? (spec/parse-email "user@.domain.com")))) + + (t/testing "rejects domain part with comma" + (t/is (nil? (spec/parse-email "user@domain,com"))) + (t/is (nil? (spec/parse-email "user@,domain.com")))) + + (t/testing "rejects domain with consecutive dots" + (t/is (nil? (spec/parse-email "user@sub..domain.com")))) + + (t/testing "rejects label starting with hyphen" + (t/is (nil? (spec/parse-email "user@-domain.com")))) + + (t/testing "rejects label ending with hyphen" + (t/is (nil? (spec/parse-email "user@domain-.com")))) + + (t/testing "rejects TLD shorter than 2 chars" + (t/is (nil? (spec/parse-email "user@domain.c")))) + + (t/testing "rejects domain without a dot" + (t/is (nil? (spec/parse-email "user@domain")))) + + (t/testing "rejects domain with spaces" + (t/is (nil? (spec/parse-email "user@do main.com")))) + + (t/testing "rejects domain ending with a dot" + (t/is (nil? (spec/parse-email "user@domain."))))) + +(t/deftest rejects-invalid-structure + (t/testing "rejects nil" + (t/is (nil? (spec/parse-email nil)))) + + (t/testing "rejects empty string" + (t/is (nil? (spec/parse-email "")))) + + (t/testing "rejects string without @" + (t/is (nil? (spec/parse-email "userdomain.com")))) + + (t/testing "rejects string with multiple @" + (t/is (nil? (spec/parse-email "user@@domain.com"))) + (t/is (nil? (spec/parse-email "us@er@domain.com")))) + + (t/testing "rejects empty domain" + (t/is (nil? (spec/parse-email "user@"))))) diff --git a/frontend/resources/styles/common/refactor/basic-rules.scss b/frontend/resources/styles/common/refactor/basic-rules.scss index c82907a5b6..91068275cc 100644 --- a/frontend/resources/styles/common/refactor/basic-rules.scss +++ b/frontend/resources/styles/common/refactor/basic-rules.scss @@ -800,7 +800,7 @@ position: absolute; padding: $s-4; border-radius: $br-8; - z-index: $z-index-10; + z-index: $z-index-dropdown; color: var(--title-foreground-color-hover); background-color: var(--menu-background-color); border: $s-2 solid var(--panel-border-color); diff --git a/frontend/resources/styles/common/refactor/z-index.scss b/frontend/resources/styles/common/refactor/z-index.scss index 755b2e9fad..3d36cb37f5 100644 --- a/frontend/resources/styles/common/refactor/z-index.scss +++ b/frontend/resources/styles/common/refactor/z-index.scss @@ -11,5 +11,5 @@ $z-index-4: 4; // context menu $z-index-5: 5; // modal $z-index-10: 10; $z-index-20: 20; -$z-index-modal: 30; // When refactor finish we can reduce this number, -$z-index-alert: 40; // When refactor finish we can reduce this number, +$z-index-modal: 300; +$z-index-dropdown: 400; diff --git a/frontend/src/app/main/ui/components/context_menu_a11y.scss b/frontend/src/app/main/ui/components/context_menu_a11y.scss index e0fc29989e..787941b595 100644 --- a/frontend/src/app/main/ui/components/context_menu_a11y.scss +++ b/frontend/src/app/main/ui/components/context_menu_a11y.scss @@ -5,12 +5,13 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/z-index.scss" as *; .context-menu { position: relative; visibility: hidden; opacity: deprecated.$op-0; - z-index: deprecated.$z-index-4; + z-index: var(--z-index-dropdown); &.is-open { position: relative; diff --git a/frontend/src/app/main/ui/dashboard/deleted.scss b/frontend/src/app/main/ui/dashboard/deleted.scss index 8a04eda993..7187633722 100644 --- a/frontend/src/app/main/ui/dashboard/deleted.scss +++ b/frontend/src/app/main/ui/dashboard/deleted.scss @@ -6,11 +6,11 @@ @use "refactor/common-refactor.scss" as deprecated; @use "common/refactor/common-dashboard"; -@use "../ds/typography.scss" as t; -@use "../ds/_borders.scss" as *; -@use "../ds/spacing.scss" as *; -@use "../ds/_sizes.scss" as *; -@use "../ds/z-index.scss" as *; +@use "ds/typography.scss" as t; +@use "ds/spacing.scss" as *; +@use "ds/z-index.scss" as *; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .dashboard-container { flex: 1 0 0; @@ -51,7 +51,7 @@ padding: var(--sp-xxl) var(--sp-xxl) var(--sp-s) var(--sp-xxl); position: sticky; top: 0; - z-index: $z-index-100; + z-index: var(--z-index-panels); } .nav-inside { diff --git a/frontend/src/app/main/ui/dashboard/files.scss b/frontend/src/app/main/ui/dashboard/files.scss index 79f3563168..838f8ea78c 100644 --- a/frontend/src/app/main/ui/dashboard/files.scss +++ b/frontend/src/app/main/ui/dashboard/files.scss @@ -6,6 +6,8 @@ @use "refactor/common-refactor.scss" as deprecated; @use "common/refactor/common-dashboard"; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; .dashboard-container { flex: 1 0 0; @@ -13,6 +15,7 @@ overflow-y: auto; width: 100%; border-top: deprecated.$s-1 solid var(--color-background-quaternary); + padding-block-end: var(--sp-xxxl); &.dashboard-projects { user-select: none; diff --git a/frontend/src/app/main/ui/dashboard/grid.scss b/frontend/src/app/main/ui/dashboard/grid.scss index 3f4189c729..e1aaef396a 100644 --- a/frontend/src/app/main/ui/dashboard/grid.scss +++ b/frontend/src/app/main/ui/dashboard/grid.scss @@ -17,7 +17,7 @@ $thumbnail-default-height: deprecated.$s-168; // Default width height: 100%; overflow-y: auto; overflow-x: hidden; - padding: 0 deprecated.$s-16; + padding: 0 var(--sp-l) deprecated.$s-16; } .grid-row { diff --git a/frontend/src/app/main/ui/dashboard/projects.scss b/frontend/src/app/main/ui/dashboard/projects.scss index 7df6a0f9c9..a37575c38e 100644 --- a/frontend/src/app/main/ui/dashboard/projects.scss +++ b/frontend/src/app/main/ui/dashboard/projects.scss @@ -19,16 +19,15 @@ margin-inline-end: var(--sp-l); border-block-start: $b-1 solid var(--panel-border-color); overflow-y: auto; - padding-block-end: var(--sp-xxxl); } .dashboard-projects { user-select: none; - block-size: calc(100vh - px2rem(64)); + block-size: calc(100vh - px2rem(80)); } .with-team-hero { - block-size: calc(100vh - px2rem(280)); + block-size: calc(100vh - px2rem(360)); } .dashboard-shared { diff --git a/frontend/src/app/main/ui/dashboard/templates.scss b/frontend/src/app/main/ui/dashboard/templates.scss index 8a5ab660c0..5d58ac4fea 100644 --- a/frontend/src/app/main/ui/dashboard/templates.scss +++ b/frontend/src/app/main/ui/dashboard/templates.scss @@ -4,10 +4,11 @@ // // Copyright (c) KALEIDOS INC -@use "ds/_borders.scss" as *; -@use "ds/_utils.scss" as *; -@use "ds/_sizes.scss" as *; @use "ds/typography.scss" as t; +@use "ds/z-index.scss" as *; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; .dashboard-templates-section { background-color: var(--color-background-tertiary); @@ -26,6 +27,8 @@ transition: bottom 300ms; width: calc(100% - $sz-12); pointer-events: none; + z-index: var(--z-index-set); + &.collapsed { inset-block-end: calc(-1 * px2rem(228)); background-color: transparent; diff --git a/package.json b/package.json index fbb4c5d92f..d2d6a9f5a8 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,9 @@ "fmt": "./scripts/fmt" }, "devDependencies": { - "@github/copilot": "^1.0.21", - "@types/node": "^25.5.2", + "@github/copilot": "^1.0.35", + "@types/node": "^25.6.0", "esbuild": "^0.28.0", - "opencode-ai": "^1.14.19" + "opencode-ai": "^1.14.22" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32b4fa3382..cf5a098662 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,17 +9,17 @@ importers: .: devDependencies: '@github/copilot': - specifier: ^1.0.21 - version: 1.0.21 + specifier: ^1.0.35 + version: 1.0.35 '@types/node': - specifier: ^25.5.2 - version: 25.5.2 + specifier: ^25.6.0 + version: 25.6.0 esbuild: specifier: ^0.28.0 version: 0.28.0 opencode-ai: - specifier: ^1.14.19 - version: 1.14.19 + specifier: ^1.14.22 + version: 1.14.22 packages: @@ -179,120 +179,120 @@ packages: cpu: [x64] os: [win32] - '@github/copilot-darwin-arm64@1.0.21': - resolution: {integrity: sha512-aB+s9ldTwcyCOYmzjcQ4SknV6g81z92T8aUJEJZBwOXOTBeWKAJtk16ooAKangZgdwuLgO3or1JUjx1FJAm5nQ==} + '@github/copilot-darwin-arm64@1.0.35': + resolution: {integrity: sha512-NNZE0TOz0HOlv7eqlh6EcQbNkhtnIHReBLieW6pfDUUTKkgsqbUu1MOitF8m+LUQk3ml1T0MQ5MOfad1HSa/MQ==} cpu: [arm64] os: [darwin] hasBin: true - '@github/copilot-darwin-x64@1.0.21': - resolution: {integrity: sha512-aNad81DOGuGShmaiFNIxBUSZLwte0dXmDYkGfAF9WJIgY4qP4A8CPWFoNr8//gY+4CwaIf9V+f/OC6k2BdECbw==} + '@github/copilot-darwin-x64@1.0.35': + resolution: {integrity: sha512-XCv/mfdv0rnrtrNVOluio/N/kyCge0uG2hghvtlgO/+z6EjvzFygkpXXS1gVxiXhWc3lX232cTXQU3zklC/8Ng==} cpu: [x64] os: [darwin] hasBin: true - '@github/copilot-linux-arm64@1.0.21': - resolution: {integrity: sha512-FL0NsCnHax4czHVv1S8iBqPLGZDhZ28N3+6nT29xWGhmjBWTkIofxLThKUPcyyMsfPTTxIlrdwWa8qQc5z2Q+g==} + '@github/copilot-linux-arm64@1.0.35': + resolution: {integrity: sha512-mbaadATfJPzmXq2SD1TWocIG/GobcYC6OvNFhCG8UXMsiXY5cevhszl5ujuayhPJBxS77Yj5uvIFjNQ1Kf5V8Q==} cpu: [arm64] os: [linux] hasBin: true - '@github/copilot-linux-x64@1.0.21': - resolution: {integrity: sha512-S7pWVI16hesZtxYbIyfw+MHZpc5ESoGKUVr5Y+lZJNaM2340gJGPQzQwSpvKIRMLHRKI2hXLwciAnYeMFxE/Tg==} + '@github/copilot-linux-x64@1.0.35': + resolution: {integrity: sha512-NrZ0VjztdBbJ5qAmuUtuKsWkimOaqzjDV+ZGUv1FxSxoys40kiiakQ5WbnMFDzaIFaf47zDi++6ixgQzq7Jk5A==} cpu: [x64] os: [linux] hasBin: true - '@github/copilot-win32-arm64@1.0.21': - resolution: {integrity: sha512-a9qc2Ku+XbyBkXCclbIvBbIVnECACTIWnPctmXWsQeSdeapGxgfHGux7y8hAFV5j6+nhCm6cnyEMS3rkZjAhdA==} + '@github/copilot-win32-arm64@1.0.35': + resolution: {integrity: sha512-KQN7Q7+oPyglmvUEiMp6SYWjl30VSu91T0dUpNHbUs/xRM3qgnCymLPPUyBZGWHog/FueUAsRkhisMHWQVnO+g==} cpu: [arm64] os: [win32] hasBin: true - '@github/copilot-win32-x64@1.0.21': - resolution: {integrity: sha512-9klu+7NQ6tEyb8sibb0rsbimBivDrnNltZho10Bgbf1wh3o+erTjffXDjW9Zkyaw8lZA9Fz8bqhVkKntZq58Lg==} + '@github/copilot-win32-x64@1.0.35': + resolution: {integrity: sha512-J0XhXO2FmlFr8pGa970xEd4tr1rqFiZxoaPW5WvkJYZoZUHbBhFcGasp5/yEeJ71b3vI4PHm/mSZZebD3ALMKQ==} cpu: [x64] os: [win32] hasBin: true - '@github/copilot@1.0.21': - resolution: {integrity: sha512-P+nORjNKAtl92jYCG6Qr1Rsw2JoyScgeQSkIR6O2WB37WS5JVdA4ax1WVualMbfuc9V58CPHX6fwyNpkI89FkQ==} + '@github/copilot@1.0.35': + resolution: {integrity: sha512-O1nUy8DXOTE+v86b/FTkyu09EMrDy+vj+2rhmUOcmsXGe0RE5ECyESsasUTUoHK/CSgAExFTziNxbubUoiMMfg==} hasBin: true - '@types/node@25.5.2': - resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} esbuild@0.28.0: resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} engines: {node: '>=18'} hasBin: true - opencode-ai@1.14.19: - resolution: {integrity: sha512-67h56qYcJivd2U9VK8LJvyMBCc3ZE3HcJ/qL4YtaidSnjEumy4SxO+HHlDISsLase7TUQ0w1nULOAibdZxGzbQ==} + opencode-ai@1.14.22: + resolution: {integrity: sha512-J+q1Ehlfg7SSXw2aIY8Mb47FHhPTN8IciKNt0/D+H/brO8RWLe67WjFzxhh/z9SSad9wPcCiLRGAc/iAn8W8wA==} hasBin: true - opencode-darwin-arm64@1.14.19: - resolution: {integrity: sha512-gjJ97dTiBbCas3Y7K6KQMQm5/KNIBOM9+10KZHqNr2bQfn0N09O977ZkXoX6IVBBE2632Ahaa71d4pzLKN9ULw==} + opencode-darwin-arm64@1.14.22: + resolution: {integrity: sha512-h9FjzNoDRsuJD0EEg535P9ul5TyrWovwx591VmuG8fp9d4PoSrAN1O3Zi07GJjkrYyrB8g3c+x5whDqJCz+qog==} cpu: [arm64] os: [darwin] - opencode-darwin-x64-baseline@1.14.19: - resolution: {integrity: sha512-UuwLpa511h7qQ+rTmOUmsHch/N4NBQT64dzg+iLWnR5yoR/81inENpbwxiS7hXpVdzCUGo/YnxI1u6SIBoMlTQ==} + opencode-darwin-x64-baseline@1.14.22: + resolution: {integrity: sha512-GgfP0wSm9/I+j3shOxfeA++7yZpXS6Y1Vis258nEFoRS9Xfv3YlHom7c/8BR9rYqeUE/+rrijP7PrGWGl+IHBw==} cpu: [x64] os: [darwin] - opencode-darwin-x64@1.14.19: - resolution: {integrity: sha512-uksrjOtWI7Ob5JvjZBSsrKuy3JVF9d89oYZYfWS5m8ordNyv1Nob39MXJXizv85ozsXjSb0rqjpJurnJw8K+tQ==} + opencode-darwin-x64@1.14.22: + resolution: {integrity: sha512-cyKRo22sxDwu4ITOlENwXaqVM9kMGndwSaAd95gz1Rmz5NYMShUO/8eckrD2MhS2wm+QvKw9XkRVWVHWQlZw3Q==} cpu: [x64] os: [darwin] - opencode-linux-arm64-musl@1.14.19: - resolution: {integrity: sha512-R1BJuBGWHfBxfKvIA/Hb4nhYaJgCKl1B+mAGNydu+z0CLtGtwU8r+kQWF/G2N0y8Vx6Y6DRfJiv1X0eZEfD1BQ==} + opencode-linux-arm64-musl@1.14.22: + resolution: {integrity: sha512-DtSd5tbGk6R5+hGhqViSvbY8ICf+u4oVQhfvCAplQCb1UEwYVc0+oAF6PimFJ+o8i8L6x14O0rry0NaRzZ0CzA==} cpu: [arm64] os: [linux] - opencode-linux-arm64@1.14.19: - resolution: {integrity: sha512-Bo+aZOppLF366mgGfK0CnIcAVy1EmsrBv93eot1CmPSN1oeud07GpGdn3Bjl5f6KuBx1io6JsvjQyHno+MH5AA==} + opencode-linux-arm64@1.14.22: + resolution: {integrity: sha512-ohK4LkkGvzB4ptr0nqDOVi2JEJMLROfy1s2U2A4Qrh+1Y0QimgH2b5VgTm+BjA3bC2Hm8Yf/IfkitqlUnCp7YA==} cpu: [arm64] os: [linux] - opencode-linux-x64-baseline-musl@1.14.19: - resolution: {integrity: sha512-+K8MuGoHugtUec4P/nKcTwZFUipHfW7oPpwlIoPiAQou3bNFTzzP6rslbzzNwjXlQRsUw9GAtuIPDOCL6CkgDg==} + opencode-linux-x64-baseline-musl@1.14.22: + resolution: {integrity: sha512-oZffotEbGXbA38Y0Dmj7IVq0ATl3nKbP8j91Z0zR5kBEBykOqExJIyc9pZpModgfPf86k98XBsRHiVLK4u9ARw==} cpu: [x64] os: [linux] - opencode-linux-x64-baseline@1.14.19: - resolution: {integrity: sha512-KuvITzg4iK0hdIjpNZepwu3bLZ/dUZDI6BwCoV4w//VEP1j3UfDyeS3vWghKcQLd2T1+yybuEMM/3RXcwm/lGQ==} + opencode-linux-x64-baseline@1.14.22: + resolution: {integrity: sha512-J67YAIWr3E03o9e6wNaPEqBo+9FcPKf5CzjIUSb8yNDyobWON1HHihcuu0hCJ6wF9J9awmlp2/4mO1HOoCo3QQ==} cpu: [x64] os: [linux] - opencode-linux-x64-musl@1.14.19: - resolution: {integrity: sha512-0qe2+X76UJdrCdhdlJyfubMC4tveHAVxjSmPq7g9Zm95heBeJdcQDCLeyQk/lGgeXgsZzVPfLmyTWNtBvCZYFQ==} + opencode-linux-x64-musl@1.14.22: + resolution: {integrity: sha512-r+QnqwR/OPmMm197Kb8VLD9mkZGFXz4m5QCZFxOAL34k8AhQZqn3d2mx2bfrMBVfoSiSVxa3jEjZEbNNFGlICQ==} cpu: [x64] os: [linux] - opencode-linux-x64@1.14.19: - resolution: {integrity: sha512-2GljfL7BeG4xALBJVRwaAGCM/dzYF5aQf6bfLTKsQIl6QpLUguYSF+fkStBHLeehyqbDP5MtiEEuXjC0+mecjA==} + opencode-linux-x64@1.14.22: + resolution: {integrity: sha512-MSUaO/Cvfb8DFRYETVrVeCnKtoIfgLflyB+O8xQOkVtjMKJ41M+1dFSMyZ3LQa2Vfp5tDskyMhj7eUxvT/owgQ==} cpu: [x64] os: [linux] - opencode-windows-arm64@1.14.19: - resolution: {integrity: sha512-8/vRHe5tHexikfPceLmpjsQiEhuDTOSCSlEmP4s0Yq3UAkVaDAxpiWq7Bx4g8hjr1gzfXD9vbjV+WHq/BtMC/w==} + opencode-windows-arm64@1.14.22: + resolution: {integrity: sha512-8grcxLSf9BD9Bt38MIxXfkI6aOFophVgM0US5r8nAUdVU78/8TS9Flnn6D39GM5RmxzqGWMl1u10vMFrBtMwPA==} cpu: [arm64] os: [win32] - opencode-windows-x64-baseline@1.14.19: - resolution: {integrity: sha512-Z8imEJK/srE/r1fr7oNLvpLTeRJQyuL7vsbXvCt3T7j2Ew9BOZ7RuYa8EE0R6bNqQ+MLhBGPiAG7NWc++MgK8Q==} + opencode-windows-x64-baseline@1.14.22: + resolution: {integrity: sha512-R/o36LpmQmbv/tL2pkcmApn6030z/1oJIYmjDkW5a4K5MXmV7aq+jWrH5p6iYKp9fo9L8oCtOp/rELMBqDS3UA==} cpu: [x64] os: [win32] - opencode-windows-x64@1.14.19: - resolution: {integrity: sha512-/TqGN91WiUzx7IPMPwmpMIzRixi5TMjaBcC9FH1TgD7DCqKnP6pokvu+ak0C9xwA4wKorE9PoZzeRt/+c3rDCQ==} + opencode-windows-x64@1.14.22: + resolution: {integrity: sha512-jVbZ4VA5b5MF2QhWQOE1VYBKdBE0v/ZebFjwzs6Vieazfgr6OFnGSHVP5WJbU/r6zDssbTBzzpnFxo0IY1SQWw==} cpu: [x64] os: [win32] - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} snapshots: @@ -374,36 +374,36 @@ snapshots: '@esbuild/win32-x64@0.28.0': optional: true - '@github/copilot-darwin-arm64@1.0.21': + '@github/copilot-darwin-arm64@1.0.35': optional: true - '@github/copilot-darwin-x64@1.0.21': + '@github/copilot-darwin-x64@1.0.35': optional: true - '@github/copilot-linux-arm64@1.0.21': + '@github/copilot-linux-arm64@1.0.35': optional: true - '@github/copilot-linux-x64@1.0.21': + '@github/copilot-linux-x64@1.0.35': optional: true - '@github/copilot-win32-arm64@1.0.21': + '@github/copilot-win32-arm64@1.0.35': optional: true - '@github/copilot-win32-x64@1.0.21': + '@github/copilot-win32-x64@1.0.35': optional: true - '@github/copilot@1.0.21': + '@github/copilot@1.0.35': optionalDependencies: - '@github/copilot-darwin-arm64': 1.0.21 - '@github/copilot-darwin-x64': 1.0.21 - '@github/copilot-linux-arm64': 1.0.21 - '@github/copilot-linux-x64': 1.0.21 - '@github/copilot-win32-arm64': 1.0.21 - '@github/copilot-win32-x64': 1.0.21 + '@github/copilot-darwin-arm64': 1.0.35 + '@github/copilot-darwin-x64': 1.0.35 + '@github/copilot-linux-arm64': 1.0.35 + '@github/copilot-linux-x64': 1.0.35 + '@github/copilot-win32-arm64': 1.0.35 + '@github/copilot-win32-x64': 1.0.35 - '@types/node@25.5.2': + '@types/node@25.6.0': dependencies: - undici-types: 7.18.2 + undici-types: 7.19.2 esbuild@0.28.0: optionalDependencies: @@ -434,55 +434,55 @@ snapshots: '@esbuild/win32-ia32': 0.28.0 '@esbuild/win32-x64': 0.28.0 - opencode-ai@1.14.19: + opencode-ai@1.14.22: optionalDependencies: - opencode-darwin-arm64: 1.14.19 - opencode-darwin-x64: 1.14.19 - opencode-darwin-x64-baseline: 1.14.19 - opencode-linux-arm64: 1.14.19 - opencode-linux-arm64-musl: 1.14.19 - opencode-linux-x64: 1.14.19 - opencode-linux-x64-baseline: 1.14.19 - opencode-linux-x64-baseline-musl: 1.14.19 - opencode-linux-x64-musl: 1.14.19 - opencode-windows-arm64: 1.14.19 - opencode-windows-x64: 1.14.19 - opencode-windows-x64-baseline: 1.14.19 + opencode-darwin-arm64: 1.14.22 + opencode-darwin-x64: 1.14.22 + opencode-darwin-x64-baseline: 1.14.22 + opencode-linux-arm64: 1.14.22 + opencode-linux-arm64-musl: 1.14.22 + opencode-linux-x64: 1.14.22 + opencode-linux-x64-baseline: 1.14.22 + opencode-linux-x64-baseline-musl: 1.14.22 + opencode-linux-x64-musl: 1.14.22 + opencode-windows-arm64: 1.14.22 + opencode-windows-x64: 1.14.22 + opencode-windows-x64-baseline: 1.14.22 - opencode-darwin-arm64@1.14.19: + opencode-darwin-arm64@1.14.22: optional: true - opencode-darwin-x64-baseline@1.14.19: + opencode-darwin-x64-baseline@1.14.22: optional: true - opencode-darwin-x64@1.14.19: + opencode-darwin-x64@1.14.22: optional: true - opencode-linux-arm64-musl@1.14.19: + opencode-linux-arm64-musl@1.14.22: optional: true - opencode-linux-arm64@1.14.19: + opencode-linux-arm64@1.14.22: optional: true - opencode-linux-x64-baseline-musl@1.14.19: + opencode-linux-x64-baseline-musl@1.14.22: optional: true - opencode-linux-x64-baseline@1.14.19: + opencode-linux-x64-baseline@1.14.22: optional: true - opencode-linux-x64-musl@1.14.19: + opencode-linux-x64-musl@1.14.22: optional: true - opencode-linux-x64@1.14.19: + opencode-linux-x64@1.14.22: optional: true - opencode-windows-arm64@1.14.19: + opencode-windows-arm64@1.14.22: optional: true - opencode-windows-x64-baseline@1.14.19: + opencode-windows-x64-baseline@1.14.22: optional: true - opencode-windows-x64@1.14.19: + opencode-windows-x64@1.14.22: optional: true - undici-types@7.18.2: {} + undici-types@7.19.2: {}