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

This commit is contained in:
Andrey Antukh 2026-04-21 20:44:31 +02:00
commit 8f2c467b82
8 changed files with 273 additions and 137 deletions

View File

@ -64,13 +64,14 @@ jobs:
echo "$PUB_DOCKER_PASSWORD" | skopeo login --username "$PUB_DOCKER_USERNAME" --password-stdin docker.io echo "$PUB_DOCKER_PASSWORD" | skopeo login --username "$PUB_DOCKER_USERNAME" --password-stdin docker.io
IMAGES=("frontend" "backend" "exporter" "storybook") IMAGES=("frontend" "backend" "exporter" "storybook")
SHORT_TAG=${TAG%.*}
for image in "${IMAGES[@]}"; do for image in "${IMAGES[@]}"; do
skopeo copy --all \ skopeo copy --all \
docker://$DOCKER_REGISTRY/$image:$TAG \ docker://$DOCKER_REGISTRY/$image:$TAG \
docker://docker.io/penpotapp/$image:$TAG docker://docker.io/penpotapp/$image:$TAG
for alias in main latest; do for alias in main latest "$SHORT_TAG"; do
skopeo copy --all \ skopeo copy --all \
docker://$DOCKER_REGISTRY/$image:$TAG \ docker://$DOCKER_REGISTRY/$image:$TAG \
docker://docker.io/penpotapp/$image:$alias docker://docker.io/penpotapp/$image:$alias

View File

@ -428,52 +428,72 @@
(ex/print-throwable instance :prefix "Server Error")) (ex/print-throwable instance :prefix "Server Error"))
(st/async-emit! (rt/assign-exception error))) (st/async-emit! (rt/assign-exception error)))
(defn- from-extension?
"True when the error stack trace originates from a browser extension."
[cause]
(let [stack (.-stack cause)]
(and (string? stack)
(or (str/includes? stack "chrome-extension://")
(str/includes? stack "moz-extension://")))))
(defn- from-posthog?
"True when the error stack trace originates from PostHog analytics."
[cause]
(let [stack (.-stack cause)]
(and (string? stack)
(str/includes? stack "posthog"))))
(defn is-ignorable-exception?
"True when the error is known to be harmless (browser extensions, analytics,
React/extension DOM conflicts, etc.) and should NOT be surfaced to the user."
[cause]
(let [message (ex-message cause)]
(or (from-extension? cause)
(from-posthog? cause)
(= message "Possible side-effect in debug-evaluate")
(= message "Unexpected end of input")
(str/starts-with? message "invalid props on component")
(str/starts-with? message "Unexpected token ")
;; Native AbortError DOMException: raised when an in-flight
;; HTTP fetch is cancelled via AbortController (e.g. by an
;; RxJS unsubscription / take-until chain). These are
;; handled gracefully inside app.util.http/fetch and must NOT
;; be surfaced as application errors.
(= (.-name ^js cause) "AbortError")
;; Zone.js (injected by browser extensions such as Angular
;; DevTools) wraps event listeners and assigns a custom
;; .toString to its wrapper functions using
;; Object.defineProperty. When the wrapper was previously
;; defined with {writable: false}, a subsequent plain assignment
;; in strict mode (our libs.js uses "use strict") throws this
;; TypeError. This is a known Zone.js / browser-extension
;; incompatibility and is NOT a Penpot bug.
(str/starts-with? message "Cannot assign to read only property 'toString'")
;; NotFoundError DOMException: "Failed to execute
;; 'removeChild' on 'Node'" — Thrown by React's commit
;; phase when the DOM tree has been modified externally
;; (typically by browser extensions like Grammarly,
;; LastPass, translation tools, or ad blockers that
;; inject/remove nodes). The entire stack trace is inside
;; React internals (libs.js) with no application code,
;; so there is nothing actionable on our side. React's
;; error boundary already handles recovery.
(and (= (.-name ^js cause) "NotFoundError")
(str/includes? message "removeChild")))))
(defn- from-plugin?
"Check if the error is marked as originating from plugin code. The
plugin runtime tracks plugin errors in a WeakMap, which works even
in SES hardened environments where error objects may be frozen."
[cause]
(try
(is-plugin-error? cause)
(catch :default _
false)))
(defonce uncaught-error-handler (defonce uncaught-error-handler
(letfn [(from-extension? [cause] (letfn [(on-unhandled-error [event]
(let [stack (.-stack cause)]
(and (string? stack)
(or (str/includes? stack "chrome-extension://")
(str/includes? stack "moz-extension://")))))
(from-posthog? [cause]
(let [stack (.-stack cause)]
(and (string? stack)
(str/includes? stack "posthog"))))
;; Check if the error is marked as originating from plugin code.
;; The plugin runtime tracks plugin errors in a WeakMap, which works
;; even in SES hardened environments where error objects may be frozen.
(from-plugin? [cause]
(try
(is-plugin-error? cause)
(catch :default _
false)))
(is-ignorable-exception? [cause]
(let [message (ex-message cause)]
(or (from-extension? cause)
(from-posthog? cause)
(= message "Possible side-effect in debug-evaluate")
(= message "Unexpected end of input")
(str/starts-with? message "invalid props on component")
(str/starts-with? message "Unexpected token ")
;; Native AbortError DOMException: raised when an in-flight
;; HTTP fetch is cancelled via AbortController (e.g. by an
;; RxJS unsubscription / take-until chain). These are
;; handled gracefully inside app.util.http/fetch and must NOT
;; be surfaced as application errors.
(= (.-name ^js cause) "AbortError")
;; Zone.js (injected by browser extensions such as Angular
;; DevTools) wraps event listeners and assigns a custom
;; .toString to its wrapper functions using
;; Object.defineProperty. When the wrapper was previously
;; defined with {writable: false}, a subsequent plain assignment
;; in strict mode (our libs.js uses "use strict") throws this
;; TypeError. This is a known Zone.js / browser-extension
;; incompatibility and is NOT a Penpot bug.
(str/starts-with? message "Cannot assign to read only property 'toString'"))))
(on-unhandled-error [event]
(.preventDefault ^js event) (.preventDefault ^js event)
(when-let [cause (unchecked-get event "error")] (when-let [cause (unchecked-get event "error")]
(cond (cond

View File

@ -265,54 +265,68 @@
prev-transforms (mf/use-var nil)] prev-transforms (mf/use-var nil)]
(mf/with-effect [add-children] (mf/with-effect [add-children]
(ts/raf (let [raf-id1
#(doseq [{:keys [shape]} add-children-prev] (ts/raf
(let [shape-node (get-shape-node shape) #(doseq [{:keys [shape]} add-children-prev]
mirror-node (dom/query (dm/fmt ".mirror-shape[href='#shape-%'" shape))] (let [shape-node (get-shape-node shape)
(when mirror-node (.remove mirror-node)) mirror-node (dom/query (dm/fmt ".mirror-shape[href='#shape-%'" shape))]
(dom/remove-attribute! (dom/get-parent shape-node) "display")))) (when mirror-node (.remove mirror-node))
(when-let [parent (some-> shape-node dom/get-parent)]
(dom/remove-attribute! parent "display")))))
(ts/raf raf-id2
#(doseq [{:keys [frame shape]} add-children] (ts/raf
(let [frame-node (get-shape-node frame) #(doseq [{:keys [frame shape]} add-children]
shape-node (get-shape-node shape) (let [frame-node (get-shape-node frame)
shape-node (get-shape-node shape)]
(when (and (some? frame-node) (some? shape-node))
(let [clip-id
(-> (dom/query frame-node ":scope > defs > .frame-clip-def")
(dom/get-attribute "id"))
clip-id use-node
(-> (dom/query frame-node ":scope > defs > .frame-clip-def") (dom/create-element "http://www.w3.org/2000/svg" "use")
(dom/get-attribute "id"))
use-node contents-node
(dom/create-element "http://www.w3.org/2000/svg" "use") (or (dom/query frame-node ".frame-children") frame-node)]
contents-node (dom/set-attribute! use-node "href" (dm/fmt "#shape-%" shape))
(or (dom/query frame-node ".frame-children") frame-node)] (dom/set-attribute! use-node "clip-path" (dm/fmt "url(#%)" clip-id))
(dom/add-class! use-node "mirror-shape")
(dom/set-attribute! use-node "href" (dm/fmt "#shape-%" shape)) (dom/append-child! contents-node use-node)
(dom/set-attribute! use-node "clip-path" (dm/fmt "url(#%)" clip-id)) (dom/set-attribute! (dom/get-parent shape-node) "display" "none"))))))]
(dom/add-class! use-node "mirror-shape") (fn []
(dom/append-child! contents-node use-node) (js/cancelAnimationFrame raf-id1)
(dom/set-attribute! (dom/get-parent shape-node) "display" "none"))))) (js/cancelAnimationFrame raf-id2))))
(mf/with-effect [transforms] (mf/with-effect [transforms]
(let [curr-shapes-set (into #{} (map :id) shapes) (let [curr-shapes-set (into #{} (map :id) shapes)
prev-shapes-set (into #{} (map :id) @prev-shapes) prev-shapes-set (into #{} (map :id) @prev-shapes)
new-shapes (->> shapes (remove #(contains? prev-shapes-set (:id %)))) new-shapes (->> shapes (remove #(contains? prev-shapes-set (:id %))))
removed-shapes (->> @prev-shapes (remove #(contains? curr-shapes-set (:id %))))] removed-shapes (->> @prev-shapes (remove #(contains? curr-shapes-set (:id %))))
;; NOTE: we schedule the dom modifications to be executed ;; NOTE: we schedule the dom modifications to be executed
;; asynchronously for avoid component flickering when react18 ;; asynchronously for avoid component flickering when react18
;; is used. ;; is used.
(when (d/not-empty? new-shapes) raf-id1
(ts/raf #(start-transform! node new-shapes))) (when (d/not-empty? new-shapes)
(ts/raf #(start-transform! node new-shapes)))
(when (d/not-empty? shapes) raf-id2
(ts/raf #(update-transform! node shapes transforms modifiers))) (when (d/not-empty? shapes)
(ts/raf #(update-transform! node shapes transforms modifiers)))
(when (d/not-empty? removed-shapes) raf-id3
(ts/raf #(remove-transform! node removed-shapes)))) (when (d/not-empty? removed-shapes)
(ts/raf #(remove-transform! node removed-shapes)))]
(reset! prev-modifiers modifiers) (reset! prev-modifiers modifiers)
(reset! prev-transforms transforms) (reset! prev-transforms transforms)
(reset! prev-shapes shapes)))) (reset! prev-shapes shapes)
(fn []
(when raf-id1 (js/cancelAnimationFrame raf-id1))
(when raf-id2 (js/cancelAnimationFrame raf-id2))
(when raf-id3 (js/cancelAnimationFrame raf-id3)))))))

View File

@ -242,7 +242,11 @@
;; afterwards, in the next render cycle. ;; afterwards, in the next render cycle.
(dom/append-child! item-el counter-el) (dom/append-child! item-el counter-el)
(dnd/set-drag-image! event item-el (:x offset) (:y offset)) (dnd/set-drag-image! event item-el (:x offset) (:y offset))
(ts/raf #(.removeChild ^js item-el counter-el)))) ;; Guard against race condition: if the user navigates away
;; before the RAF fires, item-el may have been unmounted and
;; counter-el is no longer a child — removeChild would throw.
(ts/raf #(when (dom/child? counter-el item-el)
(dom/remove-child! item-el counter-el)))))
(defn on-asset-drag-start (defn on-asset-drag-start
[event file-id asset selected item-ref asset-type on-drag-start] [event file-id asset selected item-ref asset-type on-drag-start]

View File

@ -0,0 +1,95 @@
;; 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 frontend-tests.errors-test
(:require
[app.main.errors :as errors]
[cljs.test :as t :include-macros true]))
(defn- make-error
"Create a JS Error-like object with the given name, message, and optional stack."
[error-name message & {:keys [stack] :or {stack ""}}]
(let [err (js/Error. message)]
(set! (.-name err) error-name)
(when (some? stack)
(set! (.-stack err) stack))
err))
;; ---------------------------------------------------------------------------
;; is-ignorable-exception? tests
;; ---------------------------------------------------------------------------
(t/deftest test-ignorable-chrome-extension
(t/testing "Errors from Chrome extensions are ignorable"
(let [cause (make-error "Error" "some error"
:stack "Error: some error\n at chrome-extension://abc123/content.js:1:1")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-moz-extension
(t/testing "Errors from Firefox extensions are ignorable"
(let [cause (make-error "Error" "some error"
:stack "Error: some error\n at moz-extension://abc123/content.js:1:1")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-posthog
(t/testing "Errors from PostHog are ignorable"
(let [cause (make-error "Error" "some error"
:stack "Error: some error\n at https://app.posthog.com/static/array.js:1:1")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-debug-evaluate
(t/testing "Debug-evaluate side-effect errors are ignorable"
(let [cause (make-error "Error" "Possible side-effect in debug-evaluate")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-unexpected-end-of-input
(t/testing "Unexpected end of input errors are ignorable"
(let [cause (make-error "SyntaxError" "Unexpected end of input")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-invalid-props
(t/testing "Invalid React props errors are ignorable"
(let [cause (make-error "Error" "invalid props on component Foo")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-unexpected-token
(t/testing "Unexpected token errors are ignorable"
(let [cause (make-error "SyntaxError" "Unexpected token <")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-abort-error
(t/testing "AbortError DOMException is ignorable"
(let [cause (make-error "AbortError" "The operation was aborted")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-zone-js-tostring
(t/testing "Zone.js toString read-only property error is ignorable"
(let [cause (make-error "TypeError"
"Cannot assign to read only property 'toString' of function 'function () { [native code] }'")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-ignorable-not-found-error-remove-child
(t/testing "NotFoundError with removeChild message is ignorable"
(let [cause (make-error "NotFoundError"
"Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node."
:stack "NotFoundError: Failed to execute 'removeChild'\n at zLe (libs.js:1:1)")]
(t/is (true? (errors/is-ignorable-exception? cause))))))
(t/deftest test-not-ignorable-not-found-error-other
(t/testing "NotFoundError without removeChild is NOT ignorable"
(let [cause (make-error "NotFoundError"
"Failed to execute 'insertBefore' on 'Node': something else")]
(t/is (false? (errors/is-ignorable-exception? cause))))))
(t/deftest test-not-ignorable-regular-error
(t/testing "Regular application errors are NOT ignorable"
(let [cause (make-error "Error" "Cannot read property 'x' of undefined")]
(t/is (false? (errors/is-ignorable-exception? cause))))))
(t/deftest test-not-ignorable-type-error
(t/testing "Regular TypeError is NOT ignorable"
(let [cause (make-error "TypeError" "undefined is not a function")]
(t/is (false? (errors/is-ignorable-exception? cause))))))

View File

@ -7,6 +7,7 @@
[frontend-tests.data.workspace-colors-test] [frontend-tests.data.workspace-colors-test]
[frontend-tests.data.workspace-texts-test] [frontend-tests.data.workspace-texts-test]
[frontend-tests.data.workspace-thumbnails-test] [frontend-tests.data.workspace-thumbnails-test]
[frontend-tests.errors-test]
[frontend-tests.helpers-shapes-test] [frontend-tests.helpers-shapes-test]
[frontend-tests.logic.comp-remove-swap-slots-test] [frontend-tests.logic.comp-remove-swap-slots-test]
[frontend-tests.logic.components-and-tokens] [frontend-tests.logic.components-and-tokens]
@ -42,6 +43,7 @@
(t/run-tests (t/run-tests
'frontend-tests.basic-shapes-test 'frontend-tests.basic-shapes-test
'frontend-tests.data.repo-test 'frontend-tests.data.repo-test
'frontend-tests.errors-test
'frontend-tests.main-errors-test 'frontend-tests.main-errors-test
'frontend-tests.data.viewer-test 'frontend-tests.data.viewer-test
'frontend-tests.data.workspace-colors-test 'frontend-tests.data.workspace-colors-test

View File

@ -19,6 +19,6 @@
"@github/copilot": "^1.0.21", "@github/copilot": "^1.0.21",
"@types/node": "^25.5.2", "@types/node": "^25.5.2",
"esbuild": "^0.28.0", "esbuild": "^0.28.0",
"opencode-ai": "^1.4.3" "opencode-ai": "^1.14.19"
} }
} }

106
pnpm-lock.yaml generated
View File

@ -18,8 +18,8 @@ importers:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0 version: 0.28.0
opencode-ai: opencode-ai:
specifier: ^1.4.3 specifier: ^1.14.19
version: 1.4.3 version: 1.14.19
packages: packages:
@ -227,67 +227,67 @@ packages:
engines: {node: '>=18'} engines: {node: '>=18'}
hasBin: true hasBin: true
opencode-ai@1.4.3: opencode-ai@1.14.19:
resolution: {integrity: sha512-WwCSrLgJiS+sLIWoi9pa62vAw3l6VI3a+ShhjDDMUJBBG2FxU18xEhk8xhEedLMKyHo1p0nwD41+iKZ1y+rdAw==} resolution: {integrity: sha512-67h56qYcJivd2U9VK8LJvyMBCc3ZE3HcJ/qL4YtaidSnjEumy4SxO+HHlDISsLase7TUQ0w1nULOAibdZxGzbQ==}
hasBin: true hasBin: true
opencode-darwin-arm64@1.4.3: opencode-darwin-arm64@1.14.19:
resolution: {integrity: sha512-d/MT28Is5yhdFY+36AqKc5r31zx8lXTQIYblfn5R8kdhamXijZVGdD0pHl3eJc1ZolUHNwzg2B+IqV22uyU9GQ==} resolution: {integrity: sha512-gjJ97dTiBbCas3Y7K6KQMQm5/KNIBOM9+10KZHqNr2bQfn0N09O977ZkXoX6IVBBE2632Ahaa71d4pzLKN9ULw==}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
opencode-darwin-x64-baseline@1.4.3: opencode-darwin-x64-baseline@1.14.19:
resolution: {integrity: sha512-WTqf7WBNRZcv6pClqnN4F7X/T/osgcPGikNHkHUSLszKWg9flqz7Z68kHR4i9ae8Bn3ke9MQRgzRdOt2PgLL0w==} resolution: {integrity: sha512-UuwLpa511h7qQ+rTmOUmsHch/N4NBQT64dzg+iLWnR5yoR/81inENpbwxiS7hXpVdzCUGo/YnxI1u6SIBoMlTQ==}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
opencode-darwin-x64@1.4.3: opencode-darwin-x64@1.14.19:
resolution: {integrity: sha512-8FUHeybVmaCYt4S2YmWcf32o/xa/ahCfI258bpWssrzs7Xg51JgUB/Csoble0I1mH7RpW39SKy/hHUtHGuJfJg==} resolution: {integrity: sha512-uksrjOtWI7Ob5JvjZBSsrKuy3JVF9d89oYZYfWS5m8ordNyv1Nob39MXJXizv85ozsXjSb0rqjpJurnJw8K+tQ==}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
opencode-linux-arm64-musl@1.4.3: opencode-linux-arm64-musl@1.14.19:
resolution: {integrity: sha512-3Ej2klaep8+fxcc44UyEuRpb/UFiNkwfzIDLIST83hFUtjzprjpTRqg6zHmOfzyfjNAaNpB4VZw6e9y3mGBpiQ==} resolution: {integrity: sha512-R1BJuBGWHfBxfKvIA/Hb4nhYaJgCKl1B+mAGNydu+z0CLtGtwU8r+kQWF/G2N0y8Vx6Y6DRfJiv1X0eZEfD1BQ==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
opencode-linux-arm64@1.4.3: opencode-linux-arm64@1.14.19:
resolution: {integrity: sha512-9jpVSOEF7TX3gPPAHVAsBT9XEO3LgYafI+IUmOzbBB9CDiVVNJw6JmEffmSpSxY4nkAh322xnMbNjVGEyXQBRA==} resolution: {integrity: sha512-Bo+aZOppLF366mgGfK0CnIcAVy1EmsrBv93eot1CmPSN1oeud07GpGdn3Bjl5f6KuBx1io6JsvjQyHno+MH5AA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
opencode-linux-x64-baseline-musl@1.4.3: opencode-linux-x64-baseline-musl@1.14.19:
resolution: {integrity: sha512-aned/3FQTHXXQv2PPKDprJwQaQkoadriQ6AByGhRl6/bHhSkhkiVl6cHHvYMKxYEwN4bVOydWhasfgm/xru/xw==} resolution: {integrity: sha512-+K8MuGoHugtUec4P/nKcTwZFUipHfW7oPpwlIoPiAQou3bNFTzzP6rslbzzNwjXlQRsUw9GAtuIPDOCL6CkgDg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
opencode-linux-x64-baseline@1.4.3: opencode-linux-x64-baseline@1.14.19:
resolution: {integrity: sha512-HpzdgYaI90qqt0WokcyBhadgFQ0EYMhq4TZ4EcaSPuZTssS2Drb6kp70Si54uOJL/MUAdc9+E0BYYIAdOJ6h1g==} resolution: {integrity: sha512-KuvITzg4iK0hdIjpNZepwu3bLZ/dUZDI6BwCoV4w//VEP1j3UfDyeS3vWghKcQLd2T1+yybuEMM/3RXcwm/lGQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
opencode-linux-x64-musl@1.4.3: opencode-linux-x64-musl@1.14.19:
resolution: {integrity: sha512-ibUevyDxVrwkp6FWu8UBCBsrzlKDT/uEug2NHCKaHIwo9uwVf5zsL/0ueHYqmH14SHK+M6wzWewYk6WuW9f0zQ==} resolution: {integrity: sha512-0qe2+X76UJdrCdhdlJyfubMC4tveHAVxjSmPq7g9Zm95heBeJdcQDCLeyQk/lGgeXgsZzVPfLmyTWNtBvCZYFQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
opencode-linux-x64@1.4.3: opencode-linux-x64@1.14.19:
resolution: {integrity: sha512-RS6TsDqTUrW5sefxD1KD9Xy9mSYGXAlr2DlGrdi8vNm9e/Bt4r4u557VB7f/Uj2CxTt2Gf7OWl08ZoPlxMJ5Gg==} resolution: {integrity: sha512-2GljfL7BeG4xALBJVRwaAGCM/dzYF5aQf6bfLTKsQIl6QpLUguYSF+fkStBHLeehyqbDP5MtiEEuXjC0+mecjA==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
opencode-windows-arm64@1.4.3: opencode-windows-arm64@1.14.19:
resolution: {integrity: sha512-2ViH17WpIQbRVfQaOBMi49pu73gqTQYT/4/WxFjShmRagX40/KkG18fhvyDAZrBKfkhPtdwgFsFxMSYP9F6QCQ==} resolution: {integrity: sha512-8/vRHe5tHexikfPceLmpjsQiEhuDTOSCSlEmP4s0Yq3UAkVaDAxpiWq7Bx4g8hjr1gzfXD9vbjV+WHq/BtMC/w==}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
opencode-windows-x64-baseline@1.4.3: opencode-windows-x64-baseline@1.14.19:
resolution: {integrity: sha512-SWYDli9SAKQd/pS/hVfuq1KEsc+gnAJdv+YtBmxaHOw57y0euqLwbGFUYFq78GAMGt/RnTYWZIEUbRK/ZiX3UA==} resolution: {integrity: sha512-Z8imEJK/srE/r1fr7oNLvpLTeRJQyuL7vsbXvCt3T7j2Ew9BOZ7RuYa8EE0R6bNqQ+MLhBGPiAG7NWc++MgK8Q==}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
opencode-windows-x64@1.4.3: opencode-windows-x64@1.14.19:
resolution: {integrity: sha512-UxmKDIw3t4XHST6JSUWHmSrCGIEK1LRTAOpO82HBC3XkIjH78gVIeauRR6RULjWAApmy9I1C3TukO2sDUi7Gvw==} resolution: {integrity: sha512-/TqGN91WiUzx7IPMPwmpMIzRixi5TMjaBcC9FH1TgD7DCqKnP6pokvu+ak0C9xwA4wKorE9PoZzeRt/+c3rDCQ==}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@ -434,55 +434,55 @@ snapshots:
'@esbuild/win32-ia32': 0.28.0 '@esbuild/win32-ia32': 0.28.0
'@esbuild/win32-x64': 0.28.0 '@esbuild/win32-x64': 0.28.0
opencode-ai@1.4.3: opencode-ai@1.14.19:
optionalDependencies: optionalDependencies:
opencode-darwin-arm64: 1.4.3 opencode-darwin-arm64: 1.14.19
opencode-darwin-x64: 1.4.3 opencode-darwin-x64: 1.14.19
opencode-darwin-x64-baseline: 1.4.3 opencode-darwin-x64-baseline: 1.14.19
opencode-linux-arm64: 1.4.3 opencode-linux-arm64: 1.14.19
opencode-linux-arm64-musl: 1.4.3 opencode-linux-arm64-musl: 1.14.19
opencode-linux-x64: 1.4.3 opencode-linux-x64: 1.14.19
opencode-linux-x64-baseline: 1.4.3 opencode-linux-x64-baseline: 1.14.19
opencode-linux-x64-baseline-musl: 1.4.3 opencode-linux-x64-baseline-musl: 1.14.19
opencode-linux-x64-musl: 1.4.3 opencode-linux-x64-musl: 1.14.19
opencode-windows-arm64: 1.4.3 opencode-windows-arm64: 1.14.19
opencode-windows-x64: 1.4.3 opencode-windows-x64: 1.14.19
opencode-windows-x64-baseline: 1.4.3 opencode-windows-x64-baseline: 1.14.19
opencode-darwin-arm64@1.4.3: opencode-darwin-arm64@1.14.19:
optional: true optional: true
opencode-darwin-x64-baseline@1.4.3: opencode-darwin-x64-baseline@1.14.19:
optional: true optional: true
opencode-darwin-x64@1.4.3: opencode-darwin-x64@1.14.19:
optional: true optional: true
opencode-linux-arm64-musl@1.4.3: opencode-linux-arm64-musl@1.14.19:
optional: true optional: true
opencode-linux-arm64@1.4.3: opencode-linux-arm64@1.14.19:
optional: true optional: true
opencode-linux-x64-baseline-musl@1.4.3: opencode-linux-x64-baseline-musl@1.14.19:
optional: true optional: true
opencode-linux-x64-baseline@1.4.3: opencode-linux-x64-baseline@1.14.19:
optional: true optional: true
opencode-linux-x64-musl@1.4.3: opencode-linux-x64-musl@1.14.19:
optional: true optional: true
opencode-linux-x64@1.4.3: opencode-linux-x64@1.14.19:
optional: true optional: true
opencode-windows-arm64@1.4.3: opencode-windows-arm64@1.14.19:
optional: true optional: true
opencode-windows-x64-baseline@1.4.3: opencode-windows-x64-baseline@1.14.19:
optional: true optional: true
opencode-windows-x64@1.4.3: opencode-windows-x64@1.14.19:
optional: true optional: true
undici-types@7.18.2: {} undici-types@7.18.2: {}