From 99f39c977763db93896be4f74ee0d4450292fc19 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 9 Nov 2023 18:32:37 +0100 Subject: [PATCH 01/16] :sparkles: Add improved REPL support --- backend/deps.edn | 5 +- backend/package.json | 24 +- backend/scripts/nrepl | 3 + backend/scripts/start-dev | 24 +- backend/src/app/main.clj | 61 +- backend/src/app/srepl.clj | 23 +- backend/src/app/srepl/main.clj | 1 + backend/yarn.lock | 1120 ++++------------------------- docker/devenv/docker-compose.yaml | 3 + 9 files changed, 231 insertions(+), 1033 deletions(-) create mode 100755 backend/scripts/nrepl diff --git a/backend/deps.edn b/backend/deps.edn index ea29b7790..d8ff1d16e 100644 --- a/backend/deps.edn +++ b/backend/deps.edn @@ -4,6 +4,8 @@ :deps {penpot/common {:local/root "../common"} org.clojure/clojure {:mvn/version "1.12.0-alpha5"} + org.clojure/tools.namespace {:mvn/version "1.4.4"} + com.github.luben/zstd-jni {:mvn/version "1.5.5-10"} io.prometheus/simpleclient {:mvn/version "0.16.0"} @@ -26,6 +28,8 @@ com.github.seancorfield/next.jdbc {:mvn/version "1.3.894"} metosin/reitit-core {:mvn/version "0.6.0"} + nrepl/nrepl {:mvn/version "1.1.0"} + cider/cider-nrepl {:mvn/version "0.43.1"} org.postgresql/postgresql {:mvn/version "42.6.0"} @@ -61,7 +65,6 @@ {:dev {:extra-deps {com.bhauman/rebel-readline {:mvn/version "RELEASE"} - org.clojure/tools.namespace {:mvn/version "RELEASE"} clojure-humanize/clojure-humanize {:mvn/version "0.2.2"} org.clojure/data.csv {:mvn/version "RELEASE"} com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"} diff --git a/backend/package.json b/backend/package.json index c5510791c..08f68cd1d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,18 +1,16 @@ { - "name": "uxbox-back", - "version": "0.1.0", - "description": "The Open-Source prototyping tool", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build-emails": "./scripts/build-email-templates.sh" + "name": "penpot-backend", + "version": "1.0.0", + "main": "index.js", + "license": "MPL-2.0", + "dependencies": { + "luxon": "^3.4.2", + "sax": "^1.2.4" }, - "repository": { - "type": "git", - "url": "git+https://github.com/uxbox/uxbox.git" - }, - "author": "Uxbox", - "license": "SEE LICENSE IN ", + "scripts": {}, "devDependencies": { - "mjml": "^4.6.3" + "nodemon": "^3.0.1", + "source-map-support": "^0.5.21", + "ws": "^8.13.0" } } diff --git a/backend/scripts/nrepl b/backend/scripts/nrepl new file mode 100755 index 000000000..c24ef8165 --- /dev/null +++ b/backend/scripts/nrepl @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +clojure -J-Xms50m -J-Xmx256m -J-XX:+UseSerialGC -Sdeps '{:deps {reply/reply {:mvn/version "0.5.0"}}}' -M -m reply.main --attach localhost:6064 -e "(in-ns 'app.main)" diff --git a/backend/scripts/start-dev b/backend/scripts/start-dev index 33f4dcac8..493f4aeff 100755 --- a/backend/scripts/start-dev +++ b/backend/scripts/start-dev @@ -6,6 +6,7 @@ export PENPOT_FLAGS="\ $PENPOT_FLAGS \ enable-prepl-server \ enable-urepl-server \ + enable-nrepl-server \ enable-webhooks \ enable-backend-asserts \ enable-audit-log \ @@ -18,22 +19,35 @@ export PENPOT_FLAGS="\ enable-access-tokens \ disable-file-validation"; -set -ex +# Initialize MINIO config +mc alias set penpot-s3/ http://minio:9000 minioadmin minioadmin +mc admin user add penpot-s3 penpot-devenv penpot-devenv +mc admin policy attach penpot-s3 readwrite --user=penpot-devenv +mc mb penpot-s3/penpot -p + +export AWS_ACCESS_KEY_ID=penpot-devenv +export AWS_SECRET_ACCESS_KEY=penpot-devenv +export PENPOT_ASSETS_STORAGE_BACKEND=assets-s3 +export PENPOT_STORAGE_ASSETS_S3_ENDPOINT=http://minio:9000 +export PENPOT_STORAGE_ASSETS_S3_BUCKET=penpot if [ "$1" = "--watch" ]; then + trap "exit" INT TERM ERR + trap "kill 0" EXIT + echo "Start Watch..." clojure -A:dev -M -m app.main & - PID=$! npx nodemon \ --watch src \ --watch ../common \ --ext "clj" \ --signal SIGKILL \ - --exec 'echo "(user/restart)" | nc -N localhost 6062' + --exec 'echo "(app.main/stop)\n\r(repl/refresh)\n\r(app.main/start)\n" | nc -N localhost 6062' + + wait; - kill -9 $PID else - clojure -A:dev -M -m app.main + clojure -A:dev -M -m app.main; fi diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index f0d8eff2a..fd2360be1 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -37,8 +37,12 @@ [app.storage.s3 :as-alias sto.s3] [app.util.time :as dt] [app.worker :as-alias wrk] + [cider.nrepl :refer [cider-nrepl-handler]] + [clojure.test :as test] + [clojure.tools.namespace.repl :as repl] [cuerdas.core :as str] [integrant.core :as ig] + [nrepl.server :as nrepl] [promesa.exec :as px]) (:gen-class)) @@ -527,22 +531,65 @@ (merge worker-config)) (ig/prep) (ig/init)))) - (l/info :hint "welcome to penpot" - :flags (str/join "," (map name cf/flags)) - :worker? (contains? cf/flags :backend-worker) - :version (:full cf/version))) + (l/inf :hint "welcome to penpot" + :flags (str/join "," (map name cf/flags)) + :worker? (contains? cf/flags :backend-worker) + :version (:full cf/version))) (defn stop [] (alter-var-root #'system (fn [sys] (when sys (ig/halt! sys)) nil))) +(defn restart + [] + (stop) + (repl/refresh :after 'app.main/start)) + +(defn restart-all + [] + (stop) + (repl/refresh-all :after 'app.main/start)) + +(defmacro run-bench + [& exprs] + `(do + (require 'criterium.core) + (criterium.core/with-progress-reporting (crit/quick-bench (do ~@exprs) :verbose)))) + +(defn run-tests + ([] (run-tests #"^backend-tests.*-test$")) + ([o] + (repl/refresh) + (cond + (instance? java.util.regex.Pattern o) + (test/run-all-tests o) + + (symbol? o) + (if-let [sns (namespace o)] + (do (require (symbol sns)) + (test/test-vars [(resolve o)])) + (test/test-ns o))))) + +(repl/disable-reload! (find-ns 'integrant.core)) (defn -main [& _args] (try - (start) + (let [p (promise)] + (when (contains? cf/flags :nrepl-server) + (l/inf :hint "start nrepl server" :port 6064) + (nrepl/start-server :bind "0.0.0.0" :port 6064 :handler cider-nrepl-handler)) + + (start) + (deref p)) (catch Throwable cause - (l/error :hint (ex-message cause) - :cause cause) + (binding [*out* *err*] + (println "==== ERROR ====")) + (.printStackTrace cause) + (when-let [cause' (ex-cause cause)] + (binding [*out* *err*] + (println "==== CAUSE ====")) + (.printStackTrace cause')) + (px/sleep 500) (System/exit -1)))) diff --git a/backend/src/app/srepl.clj b/backend/src/app/srepl.clj index fcb802c02..de96fe2b1 100644 --- a/backend/src/app/srepl.clj +++ b/backend/src/app/srepl.clj @@ -36,7 +36,9 @@ lock (locks/create)] (ccs/prepl *in* (fn [m] - (binding [*out* out, *flush-on-newline* true, *print-readably* true] + (binding [*out* out, + *flush-on-newline* true, + *print-readably* true] (locks/locking lock (println (json/encode-str m)))))))) @@ -44,13 +46,10 @@ (s/def ::port ::us/integer) (s/def ::host ::us/not-empty-string) -(s/def ::flag #{:urepl-server :prepl-server}) -(s/def ::type #{::prepl ::urepl}) -(s/def ::key (s/tuple ::type ::us/keyword)) (defmethod ig/pre-init-spec ::server [_] - (s/keys :req [::flag ::host ::port])) + (s/keys :req [::host ::port])) (defmethod ig/prep-key ::server [[type _] cfg] @@ -59,6 +58,12 @@ (defmethod ig/init-key ::server [[type _] {:keys [::flag ::port ::host] :as cfg}] (when (contains? cf/flags flag) + + (l/inf :hint "initializing repl server" + :name (name type) + :port port + :host host) + (let [accept (case type ::prepl 'app.srepl/json-repl ::urepl 'app.srepl/user-repl) @@ -67,14 +72,8 @@ :name (name type) :accept accept}] - (l/info :msg "initializing repl server" - :name (name type) - :port port - :host host) - (ccs/start-server params) - - params))) + (assoc params :type type)))) (defmethod ig/halt-key! ::server [_ params] diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj index e468d3fc4..e2de09810 100644 --- a/backend/src/app/srepl/main.clj +++ b/backend/src/app/srepl/main.clj @@ -31,6 +31,7 @@ [app.util.time :as dt] [app.worker :as wrk] [clojure.pprint :refer [pprint print-table]] + [clojure.tools.namespace.repl :as repl] [cuerdas.core :as str])) (defn print-available-tasks diff --git a/backend/yarn.lock b/backend/yarn.lock index 3fee05541..081a03801 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2,44 +2,15 @@ # yarn lockfile v1 -"@babel/runtime@^7.8.7": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" - integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== - dependencies: - regenerator-runtime "^0.13.4" - -"@types/node@*": - version "14.14.14" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae" - integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ== - abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -54,11 +25,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== -boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -74,237 +40,37 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" -camel-case@3.0.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -cheerio@1.0.0-rc.3, cheerio@^1.0.0-rc.3: - version "1.0.0-rc.3" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" - integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== +chokidar@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.1" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash "^4.15.0" - parse5 "^3.0.1" - -chokidar@^3.0.0: - version "3.4.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" - integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== - dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.2" - glob-parent "~5.1.0" + glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.5.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "~2.1.2" - -clean-css@4.2.x: - version "4.2.3" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" - integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== - dependencies: - source-map "~0.6.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -commander@2.17.x: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== - -commander@^2.19.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - -commander@~2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + fsevents "~2.3.2" concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -config-chain@^1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - -css-what@2.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -dom-serializer@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" - integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - entities "^2.0.0" - -dom-serializer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== - dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" - -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1, domelementtype@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" - integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - -domhandler@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" - integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== - dependencies: - domelementtype "^2.0.1" - -domhandler@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" - integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== - dependencies: - domelementtype "^2.1.0" - -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^2.0.0: - version "2.4.4" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" - integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.0.1" - domhandler "^4.0.0" - -editorconfig@^0.15.3: - version "0.15.3" - resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" - integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g== - dependencies: - commander "^2.19.0" - lru-cache "^4.1.5" - semver "^5.6.0" - sigmund "^1.0.1" - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -entities@^1.1.1, entities@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - -entities@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" - integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== - -escape-goat@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c" - integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw== + ms "^2.1.1" fill-range@^7.0.1: version "7.0.1" @@ -313,105 +79,27 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.1.1, glob@^7.1.3: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== -he@1.2.x: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -html-minifier@^3.5.3: - version "3.5.21" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" - integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" - -htmlparser2@^3.9.1, htmlparser2@^3.9.2: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" - -htmlparser2@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" - integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q== - dependencies: - domelementtype "^2.0.1" - domhandler "^3.0.0" - domutils "^2.0.0" - entities "^2.0.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== is-binary-path@~2.1.0: version "2.1.0" @@ -425,11 +113,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -442,424 +125,50 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -js-beautify@^1.6.14: - version "1.13.0" - resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.13.0.tgz#a056d5d3acfd4918549aae3ab039f9f3c51eebb2" - integrity sha512-/Tbp1OVzZjbwzwJQFIlYLm9eWQ+3aYbBXLSaqb1mEJzhcQAfrqMMQYtjb6io+U6KpD0ID4F+Id3/xcjH3l/sqA== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: - config-chain "^1.1.12" - editorconfig "^0.15.3" - glob "^7.1.3" - mkdirp "^1.0.4" - nopt "^5.0.0" + yallist "^4.0.0" -"js-tokens@^3.0.0 || ^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +luxon@^3.4.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.3.tgz#8ddf0358a9492267ffec6a13675fbaab5551315d" + integrity sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg== -juice@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/juice/-/juice-7.0.0.tgz#509bed6adbb6e4bbaa7fbfadac4e2e83e8c89ba3" - integrity sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q== - dependencies: - cheerio "^1.0.0-rc.3" - commander "^5.1.0" - mensch "^0.3.4" - slick "^1.12.2" - web-resource-inliner "^5.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash@^4.15.0, lodash@^4.17.15: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== - -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= - -lru-cache@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -mensch@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd" - integrity sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g== - -mime@^2.4.6: - version "2.4.7" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74" - integrity sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA== - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -mjml-accordion@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-accordion/-/mjml-accordion-4.7.1.tgz#61492a63f84ec7bebb16644caea31d8e3eaecec8" - integrity sha512-oYwC/CLOUWJ6pRt2saDHj/HytGOHO5B5lKNqUAhKPye5HFNZykKEV5ChmZ2NfGsGU+9BhQ7H5DaCafp4fDmPAg== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nodemon@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.0.1.tgz#affe822a2c5f21354466b2fc8ae83277d27dadc7" + integrity sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw== dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^7.5.3" + simple-update-notifier "^2.0.0" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" -mjml-body@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-body/-/mjml-body-4.7.1.tgz#d8ede15fea556f2c4d62243ab68bb6d9b344bd67" - integrity sha512-JCrkit+kjCfQyKuVyWSOonM2LGs/o3+63R9l2SleFeXf3+0CaKWaZr/Exzvaeo28c+1o3yRqXbJIpD22SEtJfQ== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-button@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-button/-/mjml-button-4.7.1.tgz#8f62352a1b27c720e6bbba65d736874ac766573f" - integrity sha512-N3WkTMPOvKw2y6sakt1YfYDbOB8apumm1OApPG6J18CHcrX03BwhHPrdfu1JwlRNGwx4kCDdb6zNCGPwuZxkCg== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-carousel@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-carousel/-/mjml-carousel-4.7.1.tgz#ab8e4fe8b9f95f9be304502e16b7edfa815b7932" - integrity sha512-eH3rRyX23ES0BKOn+UUV39+yGNmZVApBVVV0A5znDaNWskCg6/g6ZhEHi4nkWpj+aP2lJKI0HX1nrMfJg0Mxhg== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-cli@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-cli/-/mjml-cli-4.7.1.tgz#0ba1ef4613e26cc31c148b4f2242034d74c4dc46" - integrity sha512-xzCtJVKYVhGorvTmnbcMUfZlmJdBnu1UBD9A1H8UUBGMNE/Hs9QpHs9PLCMp8JR/uhSu15IgVjhFN0oSVndMRQ== - dependencies: - "@babel/runtime" "^7.8.7" - chokidar "^3.0.0" - glob "^7.1.1" - lodash "^4.17.15" - mjml-core "4.7.1" - mjml-migrate "4.7.1" - mjml-parser-xml "4.7.1" - mjml-validator "4.7.1" - yargs "^15.3.1" - -mjml-column@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-column/-/mjml-column-4.7.1.tgz#0639466687691b3bd182bdca39718ae46a853abc" - integrity sha512-CGw81TnGiuPR1GblLOez8xeoeAz1SEFjMpqapazjgXUuF5xUxg3qH55Wt4frpXe3VypeZWVYeumr6CwoNaPbKg== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-core@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-core/-/mjml-core-4.7.1.tgz#9c5da30479cc8c8206e6295220fc297ca1cb4378" - integrity sha512-AMACoq/h440m7SM86As8knW0bNQgjNIzsP/cMF6X9RO07GfszgbaWUq/XCaRNi+q8bWvBJSCXbngDJySVc5ALw== - dependencies: - "@babel/runtime" "^7.8.7" - cheerio "1.0.0-rc.3" - html-minifier "^3.5.3" - js-beautify "^1.6.14" - juice "^7.0.0" - lodash "^4.17.15" - mjml-migrate "4.7.1" - mjml-parser-xml "4.7.1" - mjml-validator "4.7.1" - -mjml-divider@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-divider/-/mjml-divider-4.7.1.tgz#d2e416ce3f0fec8763ed5c41ba97d296785eca81" - integrity sha512-7+uCUJdqEr6w8AzpF8lhRheelYEgOwiK0KJGlAQN3LF+h2S1rTPEzEB67qL2x5cU+80kPlxtxoQWImDBy0vXqg== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-group@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-group/-/mjml-group-4.7.1.tgz#88b4c396a00f3b7cb4452ca4047c0f65646b6320" - integrity sha512-mAYdhocCzetdhPSws/9/sQ4hcz4kQPX2dNitQmbxNVwoMFYXjp/WcLEfGc5u13Ue7dPfcV6c9lB/Uu5o3NmRvw== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head-attributes@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head-attributes/-/mjml-head-attributes-4.7.1.tgz#7b620bb45438909c6b33afe1069a7d70bb8d5b37" - integrity sha512-nB/bQ3I98Dvy/IkI4nqxTCnLonULkIKc8KrieRTrtPkUV3wskBzngpCgnjKvFPbHWiGlwjHDzcFJc7G0uWeqog== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head-breakpoint@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head-breakpoint/-/mjml-head-breakpoint-4.7.1.tgz#1a0ab3c22cda6c6b019e0a45e2361723f2897c94" - integrity sha512-0KB5SweIWDvwHkn4VCUsEhCQgfY/0wkNUnSXNoftaRujv0NQFQfOOH4eINy0NZYfDfrE4WYe08z+olHprp+T2A== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head-font@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head-font/-/mjml-head-font-4.7.1.tgz#884b626559ce8836412dc45b7040ec80d29ff040" - integrity sha512-9YGzBcQ2htZ6j266fiLLfzcxqDEDLTvfKtypTjaeRb1w3N8S5wL+/zJA5ZjRL6r39Ij5ZPQSlSDC32KPiwhGkA== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head-html-attributes@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head-html-attributes/-/mjml-head-html-attributes-4.7.1.tgz#3764ab44ad530c6c3cdbd6db92813ed0db07fc0b" - integrity sha512-2TK2nGpq4rGaghbVx2UNm5TXeZ5BTGYEvtSPoYPNu02KRCj6tb+uedAgFXwJpX+ogRfIfPK50ih+9ZMoHwf2IQ== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head-preview@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head-preview/-/mjml-head-preview-4.7.1.tgz#f06423bf07b1889c39658c830a8d3f4a6f17b93e" - integrity sha512-UHlvvgldiPDODq/5zKMsmXgRb/ZyKygKDUVQSM5bm3HvpKXeyYxJZazcIGmlGICEqv1ced1WGINhCg72dSfN+Q== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head-style@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head-style/-/mjml-head-style-4.7.1.tgz#df8e055852fa3b28a50ac820ec449e8915a1ecbf" - integrity sha512-8Gij99puN1SoOx5tGBjgkh4iCpI+zbwGBiB2Y8VwJrwXQxdJ1Qa902dQP5djoFFG39Bthii/48cS/d1bHigGPQ== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head-title@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head-title/-/mjml-head-title-4.7.1.tgz#8c0216881457f02be592fd5ebe59d7968825b874" - integrity sha512-vK3r+DApTXw2EoK/fh8dQOsO438Z7Ksy6iBIb7h04x33d4Z41r6+jtgxGXoKFXnjgr8MyLX5HZyyie5obW+hZg== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-head@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-head/-/mjml-head-4.7.1.tgz#ce79193702dd4b1eae5b9cf9864f8ac8bd25269d" - integrity sha512-jUcJ674CT1oT8NTQWTjQQBFZu4yklK0oppfGFJ1cq76ze3isMiyhSnGnOHw6FkjLnZtb3gXXaGKX7UZM+UMk/w== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-hero@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-hero/-/mjml-hero-4.7.1.tgz#49bc1c844ae7221f2f4e8d862d111c41f2cf8b2c" - integrity sha512-x+29V8zJAs8EV/eTtGbR921pCpitMQOAkyvNANW/3JLDTL2Oio1OYvGPVC3z1wOT9LKuRTxVzNHVt/bBw02CSQ== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-image@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-image/-/mjml-image-4.7.1.tgz#0b4da3d46b1f79c9472df965c70f15bc1255a425" - integrity sha512-l3uRR2jaM0Bpz4ctdWuxQUFgg+ol6Nt+ODOrnHsGMwpmFOh4hTPTky6KaF0LCXxYmGbI0FoGBna+hVNnkBsQCA== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-migrate@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-migrate/-/mjml-migrate-4.7.1.tgz#a086af92fd94b797b5ba1a59ba2ae73a34c65911" - integrity sha512-RgrJ9fHg6iRHC2H4pjRDWilBQ1eTH2jRu1ayDplbnepGoql83vLZaYaWc5Q+J+NsaNI16x+bgNB3fQdBiK+mng== - dependencies: - "@babel/runtime" "^7.8.7" - js-beautify "^1.6.14" - lodash "^4.17.15" - mjml-core "4.7.1" - mjml-parser-xml "4.7.1" - yargs "^15.3.1" - -mjml-navbar@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-navbar/-/mjml-navbar-4.7.1.tgz#8f5d7be62ddf118c85332b4509321c996e879c62" - integrity sha512-awdu8zT7xhS+9aCVunqtocUs8KA2xb+UhJ8UGbxVBpYbTNj3rCL9aWUXqWVwMk1la+3ypCkFuDuTl6dIoWPWlA== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-parser-xml@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-parser-xml/-/mjml-parser-xml-4.7.1.tgz#adda1ba42c9f5b21d4817a4f0cbaff4b8a97eeec" - integrity sha512-UWfuRpN45k3GUEv2yl8n5Uf98Tg6FyCsyRnqZGo83mgZzlJRDYTdKII9RjZM646/S8+Q8e9qxi3AsL00j6sZsQ== - dependencies: - "@babel/runtime" "^7.8.7" - htmlparser2 "^3.9.2" - lodash "^4.17.15" - -mjml-raw@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-raw/-/mjml-raw-4.7.1.tgz#a862dfabb540b78b6d31feef156f704c19ed308b" - integrity sha512-mCQFEXINTkC8i7ydP1Km99e0FaZTeu79AoYnTBAILd4QO+RuD3n/PimBGrcGrOUex0JIKa2jyVQOcSCBuG4WpA== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-section@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-section/-/mjml-section-4.7.1.tgz#b76b7e59090ca380758b74dbd2b67e0d2f35d097" - integrity sha512-PlhCMsl/bpFwwgQGUopi9OgOGWgRPpEJVKE8hk4He8GXzbfIuDj4DZ9QJSkwIoZ0fZtcgz11Wwb19i9BZcozVw== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-social@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-social/-/mjml-social-4.7.1.tgz#8d1555314744bdbca9dde769a7af535df0ee38d6" - integrity sha512-tN/6V3m59izO9rqWpUokHxhwkk2GHkltzIlhI936hAJHh8hFyEO6+ZwQBZm738G00qgfICmQvX5FNq4upkCYjw== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-spacer@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-spacer/-/mjml-spacer-4.7.1.tgz#96a7e59329dc9db7bb914a2e3d67b4f478f33cdf" - integrity sha512-gQu1+nA9YGnoolfNPvzfVe/RJ8WqS8ho0hthlhiLOC2RnEnmqH7HHSzCFXm4OeN0VgvDQsM7mfYQGl82O58Y+g== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-table@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-table/-/mjml-table-4.7.1.tgz#4b75185b150d3a4f4bf29d6fb3918de3dc6f87db" - integrity sha512-rPkOtufMiVreb7I7vXk6rDm9i1DXncODnM5JJNhA9Z1dAQwXiz6V5904gAi2cEYfe0M2m0XQ8P5ZCtvqxGkfGA== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-text@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-text/-/mjml-text-4.7.1.tgz#135a7d2c7aaebf4c41bde1cd763965c50375e371" - integrity sha512-hrjxbY59v6hu/Pn0NO+6TMlrdAlRa3M7GVALx/YWYV3hi59zjYfot8Au7Xq64XdcbcI4eiBVbP/AVr8w03HsOw== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - -mjml-validator@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-validator/-/mjml-validator-4.7.1.tgz#9ccadec3090ea6cc18956b292e94c1f0372fa47b" - integrity sha512-Qxubbz5WE182iLSTd/XRuezMr6UE7/u73grDCw0bTIcQsaTAIkWQn2tBI3jj0chWOw+sxwK2C6zPm9B0Cv7BGA== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - warning "^3.0.0" - -mjml-wrapper@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml-wrapper/-/mjml-wrapper-4.7.1.tgz#a354dcf186ab6f56fa39d5c820e31cd40e6217b7" - integrity sha512-6i+ZATUyqIO5YBnx+RFKZ3+6mg3iOCS/EdXGYZSonZ/EHqlt+RJa3fG2BB4dacXqAjghfl6Lk+bLoR47P3xYIQ== - dependencies: - "@babel/runtime" "^7.8.7" - lodash "^4.17.15" - mjml-core "4.7.1" - mjml-section "4.7.1" - -mjml@^4.6.3: - version "4.7.1" - resolved "https://registry.yarnpkg.com/mjml/-/mjml-4.7.1.tgz#cf3398a4d43d694ec75768f4319875f0d9846ba0" - integrity sha512-nwMrmhTI+Aeh9Gav9LHX/i8k8yDi/QpX5h535BlT5oP4NaAUmyxP/UeYUn9yxtPcIzDlM5ullFnRv/71jyHpkQ== - dependencies: - mjml-accordion "4.7.1" - mjml-body "4.7.1" - mjml-button "4.7.1" - mjml-carousel "4.7.1" - mjml-cli "4.7.1" - mjml-column "4.7.1" - mjml-core "4.7.1" - mjml-divider "4.7.1" - mjml-group "4.7.1" - mjml-head "4.7.1" - mjml-head-attributes "4.7.1" - mjml-head-breakpoint "4.7.1" - mjml-head-font "4.7.1" - mjml-head-html-attributes "4.7.1" - mjml-head-preview "4.7.1" - mjml-head-style "4.7.1" - mjml-head-title "4.7.1" - mjml-hero "4.7.1" - mjml-image "4.7.1" - mjml-migrate "4.7.1" - mjml-navbar "4.7.1" - mjml-raw "4.7.1" - mjml-section "4.7.1" - mjml-social "4.7.1" - mjml-spacer "4.7.1" - mjml-table "4.7.1" - mjml-text "4.7.1" - mjml-validator "4.7.1" - mjml-wrapper "4.7.1" - -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -no-case@^2.2.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - -node-fetch@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== dependencies: abbrev "1" @@ -868,166 +177,61 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -param-case@2.1.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= - dependencies: - no-case "^2.2.0" - -parse5@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" - integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== - dependencies: - "@types/node" "*" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - picomatch@^2.0.4, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -readable-stream@^3.1.1: +readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== +sax@^1.2.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== -relateurl@0.2.x: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= +semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^2.0.0: +simple-update-notifier@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + dependencies: + semver "^7.5.3" -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +source-map-support@^0.5.21: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" -semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -sigmund@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= - -slick@^1.12.2: - version "1.12.2" - resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" - integrity sha1-vQSN23TefRymkV+qSldXCzVQwtc= - -source-map@~0.6.0, source-map@~0.6.1: +source-map@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== +supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" + has-flag "^3.0.0" to-regex-range@^5.0.1: version "5.0.1" @@ -1036,98 +240,24 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -uglify-js@3.4.x: - version "3.4.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" - integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== dependencies: - commander "~2.19.0" - source-map "~0.6.1" + nopt "~1.0.10" -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +ws@^8.13.0: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== -valid-data-url@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/valid-data-url/-/valid-data-url-3.0.1.tgz#826c1744e71b5632e847dd15dbd45b9fb38aa34f" - integrity sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA== - -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" - integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= - dependencies: - loose-envify "^1.0.0" - -web-resource-inliner@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-5.0.0.tgz#ac30db8096931f20a7c1b3ade54ff444e2e20f7b" - integrity sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A== - dependencies: - ansi-colors "^4.1.1" - escape-goat "^3.0.0" - htmlparser2 "^4.0.0" - mime "^2.4.6" - node-fetch "^2.6.0" - valid-data-url "^3.0.0" - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -y18n@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" - integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/docker/devenv/docker-compose.yaml b/docker/devenv/docker-compose.yaml index a8f7c4cff..9d70fa7f1 100644 --- a/docker/devenv/docker-compose.yaml +++ b/docker/devenv/docker-compose.yaml @@ -37,6 +37,9 @@ services: - 3449:3449 - 6060:6060 - 6061:6061 + - 6062:6062 + - 6063:6063 + - 6064:6064 - 9090:9090 environment: From 59fe93cb450c9818217760c113c8b2e71758fa04 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 10 Nov 2023 11:39:49 +0100 Subject: [PATCH 02/16] :bug: Fix minor issue with pointer-map feature interaction with team features --- common/src/app/common/features.cljc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/common/src/app/common/features.cljc b/common/src/app/common/features.cljc index 310f7eef7..0e3e652d0 100644 --- a/common/src/app/common/features.cljc +++ b/common/src/app/common/features.cljc @@ -190,10 +190,16 @@ (check-supported-features! file-features) - (let [not-supported (-> file-features - (set/difference enabled-features) - (set/difference client-features) - (set/difference frontend-only-features))] + (let [;; We should ignore all features that does not match with + ;; the `no-migration-features` set because we can't enable + ;; them as-is, because they probably need migrations + client-features (set/intersection client-features no-migration-features) + not-supported (-> file-features + (set/difference enabled-features) + (set/difference client-features) + (set/difference backend-only-features) + (set/difference frontend-only-features))] + (when (seq not-supported) (ex/raise :type :restriction :code :feature-mismatch From 34437ea5a59f88eadb3070a491cc6da9070d5705 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 10 Nov 2023 11:40:52 +0100 Subject: [PATCH 03/16] :bug: Fix default flags on start-dev script --- backend/scripts/start-dev | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/scripts/start-dev b/backend/scripts/start-dev index 493f4aeff..742f81303 100755 --- a/backend/scripts/start-dev +++ b/backend/scripts/start-dev @@ -12,8 +12,8 @@ export PENPOT_FLAGS="\ enable-audit-log \ enable-transit-readable-response \ enable-demo-users \ - enable-fdata-storage-pointer-map \ - enable-fdata-storage-objets-map \ + enable-feature-fdata-pointer-map \ + enable-feature-fdata-objects-map \ disable-secure-session-cookies \ enable-smtp \ enable-access-tokens \ From 22a0aea2a15c5ed835423ca52b061e38a1fda6bb Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:02:50 +0100 Subject: [PATCH 04/16] :paperclip: Add missing logger config on default log4j2.xml file --- backend/resources/log4j2.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/resources/log4j2.xml b/backend/resources/log4j2.xml index 685eac0fb..9041556c4 100644 --- a/backend/resources/log4j2.xml +++ b/backend/resources/log4j2.xml @@ -13,10 +13,14 @@ + + + + From 76bca216cb382ac6e7c04b88de632307b889590a Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:03:17 +0100 Subject: [PATCH 05/16] :sparkles: Enable by default file validation on start-dev command --- backend/scripts/start-dev | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/scripts/start-dev b/backend/scripts/start-dev index 742f81303..6322a27b3 100755 --- a/backend/scripts/start-dev +++ b/backend/scripts/start-dev @@ -12,6 +12,7 @@ export PENPOT_FLAGS="\ enable-audit-log \ enable-transit-readable-response \ enable-demo-users \ + enable-file-validation \ enable-feature-fdata-pointer-map \ enable-feature-fdata-objects-map \ disable-secure-session-cookies \ From 9d05e2260cafa7a2dfa02a008af7d4e57877c4ae Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:05:11 +0100 Subject: [PATCH 06/16] :bug: Fix incorrect local library indexing on components-v2 migration --- backend/src/app/features/components_v2.clj | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/src/app/features/components_v2.clj b/backend/src/app/features/components_v2.clj index ba1949550..cf22b7298 100644 --- a/backend/src/app/features/components_v2.clj +++ b/backend/src/app/features/components_v2.clj @@ -632,21 +632,21 @@ cfeat/*wrap-with-objects-map-fn* (if (contains? (:features file) "fdata/objectd-map") omap/wrap identity)] - (let [libs (sequence - (map (fn [{:keys [id] :as lib}] - (binding [pmap/*load-fn* (partial files/load-pointer conn id)] - (-> (db/get conn :file {:id id}) - (files/decode-row) - (files/process-pointers deref) ; ensure all pointers resolved - (pmg/migrate-file))))) - (files/get-file-libraries conn id)) - - libs (-> (d/index-by :id libs) - (assoc (:id file) file)) - - file (-> file + (let [file (-> file (update :data blob/decode) (update :data assoc :id id) + (pmg/migrate-file)) + + libs (->> (files/get-file-libraries conn id) + (into [file] (map (fn [{:keys [id]}] + (binding [pmap/*load-fn* (partial files/load-pointer conn id)] + (-> (db/get conn :file {:id id}) + (files/decode-row) + (files/process-pointers deref) ; ensure all pointers resolved + (pmg/migrate-file)))))) + (d/index-by :id)) + + file (-> file (update :data migrate-file-data libs) (update :features conj "components/v2"))] From 3c64955b9309f86874c0602a430e19272f1d6bbb Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:07:19 +0100 Subject: [PATCH 07/16] :sparkles: Add efficiency improvements to backend error reporting --- backend/src/app/http/errors.clj | 30 ++++++++++++------- backend/src/app/rpc/commands/binfile.clj | 1 - common/src/app/common/exceptions.cljc | 7 ++--- common/src/app/common/logging.cljc | 2 +- common/src/app/common/schema.cljc | 23 ++++++++++---- frontend/src/app/main/errors.cljs | 27 +++++++++++++---- .../src/app/main/ui/dashboard/import.cljs | 2 +- 7 files changed, 63 insertions(+), 29 deletions(-) diff --git a/backend/src/app/http/errors.clj b/backend/src/app/http/errors.clj index 2445e6099..875bd5663 100644 --- a/backend/src/app/http/errors.clj +++ b/backend/src/app/http/errors.clj @@ -88,11 +88,19 @@ (= code :params-validation) (let [explain (::sm/explain data) - payload (sm/humanize-data explain)] + explain (sm/humanize-data explain)] {::yrs/status 400 ::yrs/body (-> data (dissoc ::sm/explain) - (assoc :data payload))}) + (assoc :explain explain))}) + + (= code :data-validation) + (let [explain (::sm/explain data) + explain (sm/humanize-data explain)] + {::yrs/status 400 + ::yrs/body (-> data + (dissoc ::sm/explain) + (assoc :explain explain))}) (= code :request-body-too-large) {::yrs/status 413 ::yrs/body data} @@ -114,18 +122,18 @@ (cond (= code :data-validation) (let [explain (::sm/explain data) - payload (sm/humanize-data explain)] - (l/error :hint "data assertion error" :message (ex-message error) :cause cause) + explain (sm/humanize-data explain)] + (l/error :hint "data assertion error" :cause cause) {::yrs/status 500 ::yrs/body {:type :server-error :code :assertion :data (-> data (dissoc ::sm/explain) - (assoc :data payload))}}) + (assoc :explain explain))}}) (= code :spec-validation) (let [explain (ex/explain data)] - (l/error :hint "spec assertion error" :message (ex-message error) :cause cause) + (l/error :hint "spec assertion error" :cause cause) {::yrs/status 500 ::yrs/body {:type :server-error :code :assertion @@ -135,7 +143,7 @@ :else (do - (l/error :hint "assertion error" :message (ex-message error) :cause cause) + (l/error :hint "assertion error" :cause cause) {::yrs/status 500 ::yrs/body {:type :server-error :code :assertion @@ -150,7 +158,7 @@ [error request parent-cause] (binding [l/*context* (request->context request)] (let [cause (or parent-cause error)] - (l/error :hint "internal error" :message (ex-message error) :cause cause) + (l/error :hint "internal error" :cause cause) {::yrs/status 500 ::yrs/body {:type :server-error :code :unhandled @@ -175,7 +183,7 @@ (let [state (.getSQLState ^java.sql.SQLException error) cause (or parent-cause error)] (binding [l/*context* (request->context request)] - (l/error :hint "PSQL error" :message (ex-message error) + (l/error :hint "PSQL error" :cause cause) (cond (= state "57014") @@ -205,7 +213,7 @@ ;; This means that exception is not a controlled exception. (nil? edata) (binding [l/*context* (request->context request)] - (l/error :hint "unexpected error" :message (ex-message error) :cause cause) + (l/error :hint "unexpected error" :cause cause) {::yrs/status 500 ::yrs/body {:type :server-error :code :unexpected @@ -213,7 +221,7 @@ :else (binding [l/*context* (request->context request)] - (l/error :hint "unhandled error" :message (ex-message error) :cause cause) + (l/error :hint "unhandled error" :cause cause) {::yrs/status 500 ::yrs/body {:type :server-error :code :unhandled diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index 1b11c8ab4..d9db769fa 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -973,7 +973,6 @@ :import-id id :elapsed (dt/format-duration (tp)) :error? (some? @cs) - :cause @cs ))))) ;; --- Command: export-binfile diff --git a/common/src/app/common/exceptions.cljc b/common/src/app/common/exceptions.cljc index 0f56082f7..cb6388997 100644 --- a/common/src/app/common/exceptions.cljc +++ b/common/src/app/common/exceptions.cljc @@ -66,9 +66,9 @@ (defn explain ([data] (explain data nil)) - ([data {:keys [level length] :or {level 8 length 10} :as opts}] + ([data {:keys [level length] :or {level 8 length 12} :as opts}] (cond - ;; ;; NOTE: a special case for spec validation errors on integrant + ;; NOTE: a special case for spec validation errors on integrant (and (= (:reason data) :integrant.core/build-failed-spec) (contains? data :explain)) (explain (:explain data) opts) @@ -81,8 +81,7 @@ (s/explain-out (update data ::s/problems #(take length %))))) (contains? data ::sm/explain) - (-> (sm/humanize-data (::sm/explain data)) - (pp/pprint-str {:level level :length length}))))) + (sm/humanize-data (::sm/explain data) :level level :length length)))) #?(:clj (defn format-throwable diff --git a/common/src/app/common/logging.cljc b/common/src/app/common/logging.cljc index 0402343b9..89a929d0b 100644 --- a/common/src/app/common/logging.cljc +++ b/common/src/app/common/logging.cljc @@ -271,7 +271,7 @@ (js/console.error n (pr-str v)) (js/console.error n v)))) - (when cause + (when (ex/exception? cause) (let [data (ex-data cause) explain (ex/explain data)] (when explain diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc index 641837f53..82aca3ffe 100644 --- a/common/src/app/common/schema.cljc +++ b/common/src/app/common/schema.cljc @@ -8,7 +8,9 @@ (:refer-clojure :exclude [deref merge parse-uuid]) #?(:cljs (:require-macros [app.common.schema :refer [ignoring]])) (:require + [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.pprint :as pp] [app.common.schema.generators :as sg] [app.common.schema.openapi :as-alias oapi] [app.common.schema.registry :as sr] @@ -142,10 +144,19 @@ (m/decoder s options transformer))) (defn humanize-data - [explain-data] - (-> explain-data - (update :schema form) - (update :errors (fn [errors] (map #(update % :schema form) errors))))) + [{:keys [schema errors value]} & {:keys [length level]}] + (let [errors (mapv #(update % :schema form) errors)] + (with-out-str + (println "Schema: ") + (println (pp/pprint-str (form schema)) {:level (d/nilv level 10) + :length (d/nilv length 10)}) + (println) + (println "Errors:") + (println (pp/pprint-str errors {:level (d/nilv level 10) + :length (d/nilv length 10)})) + (println "Value:") + (println (pp/pprint-str value {:level (d/nilv level 5) + :length (d/nilv length 10)}))))) (defn pretty-explain [s d] @@ -191,7 +202,7 @@ (fn [v] (let [result (v-fn v)] (when (and (not result) (true? dm/*assert-context*)) - (let [hint (str "schema assert: " (pr-str (form s))) + (let [hint "schema validation" exp (e-fn v)] (throw (ex-info hint {:type :assertion :code :data-validation @@ -204,7 +215,7 @@ [s v] (let [result (validate s v)] (when (and (not result) (true? dm/*assert-context*)) - (let [hint (str "schema assert: " (pr-str (form s))) + (let [hint "schema validation" exp (explain s v)] (throw (ex-info hint {:type :assertion :code :data-validation diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs index c7e83c3b3..a17b5bada 100644 --- a/frontend/src/app/main/errors.cljs +++ b/frontend/src/app/main/errors.cljs @@ -25,7 +25,7 @@ [data] (-> data (dissoc ::sm/explain) - (dissoc :hint) + (dissoc :explain) (dissoc ::trace) (dissoc ::instance) (pp/pprint {:width 70}))) @@ -33,8 +33,9 @@ (defn- print-explain! [data] (when-let [explain (::sm/explain data)] - (-> (sm/humanize-data explain) - (pp/pprint {:width 70})))) + (js/console.log (sm/humanize-data explain))) + (when-let [explain (:explain data)] + (js/console.log explain))) (defn- print-trace! [data] @@ -98,7 +99,8 @@ (print-group! "Validation Error" (fn [] - (print-data! error)))) + (print-data! error) + (print-explain! error)))) ;; This is a pure frontend error that can be caused by an active @@ -223,7 +225,22 @@ (print-group! "Server Error" (fn [] - (print-data! error)))) + (print-data! (dissoc error :data)) + + (when-let [werror (:data error)] + (cond + (= :assertion (:type werror)) + (print-group! "Assertion Error" + (fn [] + (print-data! werror) + (print-explain! werror))) + + :else + (print-group! "Unexpected" + (fn [] + (print-data! werror) + (print-explain! werror)))))))) + (defonce uncaught-error-handler (letfn [(is-ignorable-exception? [cause] diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index 120119193..4edb59bf6 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -258,9 +258,9 @@ {:cmd :analyze-import :files files}) (rx/delay-emit emit-delay) + (rx/filter some?) (rx/subs (fn [{:keys [uri data error type] :as msg}] - (log/debug :uri uri :data data :error error) (if (some? error) (swap! state update :files set-analyze-error uri) (swap! state update :files set-analyze-result uri type data))))))) From 19c5d32a89d6c31e490ea9c636d341404b62e5a5 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:10:26 +0100 Subject: [PATCH 08/16] :sparkles: Improve efficiency and code style of file validate ns --- common/src/app/common/files/validate.cljc | 490 +++++++++++--------- common/src/app/common/types/pages_list.cljc | 4 +- 2 files changed, 268 insertions(+), 226 deletions(-) diff --git a/common/src/app/common/files/validate.cljc b/common/src/app/common/files/validate.cljc index c690bf713..7272e59c6 100644 --- a/common/src/app/common/files/validate.cljc +++ b/common/src/app/common/files/validate.cljc @@ -61,34 +61,37 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:dynamic *errors* nil) -(def ^:dynamic *throw-on-error* false) -(defn- report-error - [code msg shape file page & args] - (when (some? *errors*) - (if (true? *throw-on-error*) - (ex/raise {:type :validation - :code code - :hint msg - :args args - ::explain (str/format "file %s, page %s, shape %s" - (:id file) - (:id page) - (:id shape))}) - (vswap! *errors* conj {:code code - :hint msg - :shape shape - :file-id (:id file) - :page-id (:id page) - :args args})))) +(defn report-error! + [code hint shape file page & args] + (if (some? *errors*) + (vswap! *errors* conj {:code code + :hint hint + :shape shape + :file-id (:id file) + :page-id (:id page) + :args args}) + + (let [explain (str/ffmt "file %, page %, shape %" + (:id file) + (:id page) + (:id shape))] + (ex/raise :type :validation + :code code + :hint hint + :args args + :file-id (:id file) + :page-id (:id page) + :shape-id (:id shape) + ::explain explain)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; VALIDATION FUNCTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(declare validate-shape) +(declare validate-shape!) -(defn validate-geometry +(defn validate-geometry! "Validate that the shape has valid coordinates, selrect and points." [shape file page] (when (and (not (#{:path :bool} (:type shape))) @@ -98,318 +101,357 @@ (nil? (:height shape)) (nil? (:selrect shape)) (nil? (:points shape)))) - (report-error :invalid-geometry - (str/format "Shape greometry is invalid") - shape file page))) + (report-error! :invalid-geometry + "Shape greometry is invalid" + shape file page))) -(defn validate-parent-children +(defn validate-parent-children! "Validate parent and children exists, and the link is bidirectional." [shape file page] (let [parent (ctst/get-shape page (:parent-id shape))] (if (nil? parent) - (report-error :parent-not-found - (str/format "Parent %s not found" (:parent-id shape)) - shape file page) + (report-error! :parent-not-found + (str/ffmt "Parent % not found" (:parent-id shape)) + shape file page) (do (when-not (cph/root? shape) (when-not (some #{(:id shape)} (:shapes parent)) - (report-error :child-not-in-parent - (str/format "Shape %s not in parent's children list" (:id shape)) - shape file page))) + (report-error! :child-not-in-parent + (str/ffmt "Shape % not in parent's children list" (:id shape)) + shape file page))) (doseq [child-id (:shapes shape)] (when (nil? (ctst/get-shape page child-id)) - (report-error :child-not-found - (str/format "Child %s not found" child-id) - shape file page - :child-id child-id))))))) + (report-error! :child-not-found + (str/ffmt "Child % not found" child-id) + shape file page + :child-id child-id))))))) -(defn validate-frame - "Validate that the frame-id shape exists and is indeed a frame. Also it must point to the - parent shape (if this is a frame) or to the frame-id of the parent (if not)." +(defn validate-frame! + "Validate that the frame-id shape exists and is indeed a frame. Also + it must point to the parent shape (if this is a frame) or to the + frame-id of the parent (if not)." [shape file page] (let [frame (ctst/get-shape page (:frame-id shape))] (if (nil? frame) - (report-error :frame-not-found - (str/format "Frame %s not found" (:frame-id shape)) - shape file page) + (report-error! :frame-not-found + (str/ffmt "Frame % not found" (:frame-id shape)) + shape file page) (if (not= (:type frame) :frame) - (report-error :invalid-frame - (str/format "Frame %s is not actually a frame" (:frame-id shape)) - shape file page) + (report-error! :invalid-frame + (str/ffmt "Frame % is not actually a frame" (:frame-id shape)) + shape file page) (let [parent (ctst/get-shape page (:parent-id shape))] (when (some? parent) (if (= (:type parent) :frame) (when-not (= (:frame-id shape) (:id parent)) - (report-error :invalid-frame - (str/format "Frame-id should point to parent" (:id parent)) - shape file page)) + (report-error! :invalid-frame + (str/ffmt "Frame-id should point to parent %" (:id parent)) + shape file page)) (when-not (= (:frame-id shape) (:frame-id parent)) - (report-error :invalid-frame - (str/format "Frame-id should point to parent frame" (:frame-id parent)) - shape file page))))))))) + (report-error! :invalid-frame + (str/ffmt "Frame-id should point to parent frame %" (:frame-id parent)) + shape file page))))))))) -(defn validate-component-main-head - "Validate shape is a main instance head, component exists and its main-instance points to this shape." +(defn validate-component-main-head! + "Validate shape is a main instance head, component exists + and its main-instance points to this shape." [shape file page libraries] (when (nil? (:main-instance shape)) - (report-error :component-not-main - (str/format "Shape expected to be main instance") - shape file page)) + (report-error! :component-not-main + "Shape expected to be main instance" + shape file page)) (when-not (= (:component-file shape) (:id file)) - (report-error :component-main-external - (str/format "Main instance should refer to a component in the same file") - shape file page)) + (report-error! :component-main-external + "Main instance should refer to a component in the same file" + shape file page)) (let [component (ctf/resolve-component shape file libraries :include-deleted? true)] (if (nil? component) - (report-error :component-not-found - (str/format "Component %s not found in file" (:component-id shape) (:component-file shape)) - shape file page) + (report-error! :component-not-found + (str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape)) + shape file page) (do (when-not (= (:main-instance-id component) (:id shape)) - (report-error :invalid-main-instance-id - (str/format "Main instance id of component %s is not valid" (:component-id shape)) - shape file page)) + (report-error! :invalid-main-instance-id + (str/ffmt "Main instance id of component % is not valid" (:component-id shape)) + shape file page)) (when-not (= (:main-instance-page component) (:id page)) - (report-error :invalid-main-instance-page - (str/format "Main instance page of component %s is not valid" (:component-id shape)) - shape file page)))))) + (report-error! :invalid-main-instance-page + (str/ffmt "Main instance page of component % is not valid" (:component-id shape)) + shape file page)))))) -(defn validate-component-not-main-head - "Validate shape is a not-main instance head, component exists and its main-instance does not point to this shape." +(defn validate-component-not-main-head! + "Validate shape is a not-main instance head, component + exists and its main-instance does not point to this + shape." [shape file page libraries] (when (some? (:main-instance shape)) - (report-error :component-not-main - (str/format "Shape not expected to be main instance") - shape file page)) + (report-error! :component-not-main + "Shape not expected to be main instance" + shape file page)) + (let [component (ctf/resolve-component shape file libraries {:include-deleted? true})] (if (nil? component) - (report-error :component-not-found - (str/format "Component %s not found in file" (:component-id shape) (:component-file shape)) - shape file page) - (do - (when (and (= (:main-instance-id component) (:id shape)) - (= (:main-instance-page component) (:id page))) - (report-error :invalid-main-instance - (str/format "Main instance of component %s should not be this shape" (:id component)) - shape file page)))))) + (report-error! :component-not-found + (str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape)) + shape file page) + (when (and (= (:main-instance-id component) (:id shape)) + (= (:main-instance-page component) (:id page))) + (report-error! :invalid-main-instance + (str/ffmt "Main instance of component % should not be this shape" (:id component)) + shape file page))))) -(defn validate-component-not-main-not-head +(defn validate-component-not-main-not-head! "Validate that this shape is not main instance and not head." [shape file page] (when (some? (:main-instance shape)) - (report-error :component-main - (str/format "Shape not expected to be main instance") - shape file page)) + (report-error! :component-main + "Shape not expected to be main instance" + shape file page)) (when (or (some? (:component-id shape)) (some? (:component-file shape))) - (report-error :component-main - (str/format "Shape not expected to be component head") - shape file page))) + (report-error! :component-main + "Shape not expected to be component head" + shape file page))) -(defn validate-component-root +(defn validate-component-root! "Validate that this shape is an instance root." [shape file page] (when (nil? (:component-root shape)) - (report-error :should-be-component-root - (str/format "Shape should be component root") - shape file page))) + (report-error! :should-be-component-root + "Shape should be component root" + shape file page))) -(defn validate-component-not-root +(defn validate-component-not-root! "Validate that this shape is not an instance root." [shape file page] (when (some? (:component-root shape)) - (report-error :should-not-be-component-root - (str/format "Shape should not be component root") - shape file page))) + (report-error! :should-not-be-component-root + "Shape should not be component root" + shape file page))) -(defn validate-component-ref +(defn validate-component-ref! "Validate that the referenced shape exists in the near component." [shape file page libraries] (let [ref-shape (ctf/find-ref-shape file page libraries shape :include-deleted? true)] (when (nil? ref-shape) - (report-error :ref-shape-not-found - (str/format "Referenced shape %s not found in near component" (:shape-ref shape)) - shape file page)))) + (report-error! :ref-shape-not-found + (str/ffmt "Referenced shape % not found in near component" (:shape-ref shape)) + shape file page)))) -(defn validate-component-not-ref +(defn validate-component-not-ref! "Validate that this shape does not reference other one." [shape file page] (when (some? (:shape-ref shape)) - (report-error :shape-ref-in-main - (str/format "Shape inside main instance should not have shape-ref") - shape file page))) + (report-error! :shape-ref-in-main + "Shape inside main instance should not have shape-ref" + shape file page))) -(defn validate-shape-main-root-top - "Root shape of a top main instance - :main-instance - :component-id - :component-file - :component-root" +(defn validate-shape-main-root-top! + "Root shape of a top main instance: + + - :main-instance + - :component-id + - :component-file + - :component-root" [shape file page libraries] - (validate-component-main-head shape file page libraries) - (validate-component-root shape file page) - (validate-component-not-ref shape file page) + (validate-component-main-head! shape file page libraries) + (validate-component-root! shape file page) + (validate-component-not-ref! shape file page) (doseq [child-id (:shapes shape)] - (validate-shape child-id file page libraries :context :main-top))) + (validate-shape! child-id file page libraries :context :main-top))) -(defn validate-shape-main-root-nested +(defn validate-shape-main-root-nested! "Root shape of a nested main instance - :main-instance - :component-id - :component-file" + - :main-instance + - :component-id + - :component-file" [shape file page libraries] - (validate-component-main-head shape file page libraries) - (validate-component-not-root shape file page) - (validate-component-not-ref shape file page) + (validate-component-main-head! shape file page libraries) + (validate-component-not-root! shape file page) + (validate-component-not-ref! shape file page) (doseq [child-id (:shapes shape)] - (validate-shape child-id file page libraries :context :main-nested))) + (validate-shape! child-id file page libraries :context :main-nested))) -(defn validate-shape-copy-root-top +(defn validate-shape-copy-root-top! "Root shape of a top copy instance - :component-id - :component-file - :component-root - :shape-ref" + - :component-id + - :component-file + - :component-root + - :shape-ref" [shape file page libraries] - (validate-component-not-main-head shape file page libraries) - (validate-component-root shape file page) - (validate-component-ref shape file page libraries) + (validate-component-not-main-head! shape file page libraries) + (validate-component-root! shape file page) + (validate-component-ref! shape file page libraries) (doseq [child-id (:shapes shape)] - (validate-shape child-id file page libraries :context :copy-top))) + (validate-shape! child-id file page libraries :context :copy-top))) -(defn validate-shape-copy-root-nested +(defn validate-shape-copy-root-nested! "Root shape of a nested copy instance - :component-id - :component-file - :shape-ref" + - :component-id + - :component-file + - :shape-ref" [shape file page libraries] - (validate-component-not-main-head shape file page libraries) - (validate-component-not-root shape file page) - (validate-component-ref shape file page libraries) + (validate-component-not-main-head! shape file page libraries) + (validate-component-not-root! shape file page) + (validate-component-ref! shape file page libraries) (doseq [child-id (:shapes shape)] - (validate-shape child-id file page libraries :context :copy-nested))) + (validate-shape! child-id file page libraries :context :copy-nested))) -(defn validate-shape-main-not-root - "Not-root shape of a main instance - (not any attribute)" +(defn validate-shape-main-not-root! + "Not-root shape of a main instance (not any attribute)" [shape file page libraries] - (validate-component-not-main-not-head shape file page) - (validate-component-not-root shape file page) - (validate-component-not-ref shape file page) + (validate-component-not-main-not-head! shape file page) + (validate-component-not-root! shape file page) + (validate-component-not-ref! shape file page) (doseq [child-id (:shapes shape)] - (validate-shape child-id file page libraries :context :main-any))) + (validate-shape! child-id file page libraries :context :main-any))) -(defn validate-shape-copy-not-root - "Not-root shape of a copy instance - :shape-ref" +(defn validate-shape-copy-not-root! + "Not-root shape of a copy instance :shape-ref" [shape file page libraries] - (validate-component-not-main-not-head shape file page) - (validate-component-not-root shape file page) - (validate-component-ref shape file page libraries) + (validate-component-not-main-not-head! shape file page) + (validate-component-not-root! shape file page) + (validate-component-ref! shape file page libraries) (doseq [child-id (:shapes shape)] - (validate-shape child-id file page libraries :context :copy-any))) + (validate-shape! child-id file page libraries :context :copy-any))) -(defn validate-shape-not-component - "Shape is not in a component or is a fostered children - (not any attribute)" +(defn validate-shape-not-component! + "Shape is not in a component or is a fostered children (not any + attribute)" [shape file page libraries] - (validate-component-not-main-not-head shape file page) - (validate-component-not-root shape file page) - (validate-component-not-ref shape file page) + (validate-component-not-main-not-head! shape file page) + (validate-component-not-root! shape file page) + (validate-component-not-ref! shape file page) (doseq [child-id (:shapes shape)] - (validate-shape child-id file page libraries :context :not-component))) + (validate-shape! child-id file page libraries :context :not-component))) -(defn validate-shape - "Validate referential integrity and semantic coherence of a shape and all its children. +(defn validate-shape! + "Validate referential integrity and semantic coherence of + a shape and all its children. Raises an exception on first + error found. - The context is the situation of the parent in respect to components: - :not-component - :main-top - :main-nested - :copy-top - :copy-nested - :main-any - :copy-any" - [shape-id file page libraries & {:keys [context throw?] - :or {context :not-component - throw? nil}}] - (binding [*throw-on-error* (if (some? throw?) throw? *throw-on-error*) - *errors* (or *errors* (volatile! []))] - (let [shape (ctst/get-shape page shape-id)] + The context is the situation of the parent in respect to components: + - :not-component + - :main-top + - :main-nested + - :copy-top + - :copy-nested + - :main-any + - :copy-any + " + [shape-id file page libraries & {:keys [context] :or {context :not-component}}] + (let [shape (ctst/get-shape page shape-id)] - ; If this happens it's a bug in this validate functions - (dm/verify! (str/format "Shape %s not found" shape-id) (some? shape)) + ;; If this happens it's a bug in this validate functions + (dm/verify! + ["Shape % not found" shape-id] + (some? shape)) - (validate-geometry shape file page) - (validate-parent-children shape file page) - (validate-frame shape file page) - - (validate-parent-children shape file page) - (validate-frame shape file page) + (validate-geometry! shape file page) + (validate-parent-children! shape file page) + (validate-frame! shape file page) (if (ctk/instance-head? shape) (if (not= :frame (:type shape)) - (report-error :instance-head-not-frame - (str/format "Instance head should be a frame") - shape file page) + (report-error! :instance-head-not-frame + "Instance head should be a frame" + shape file page) (if (ctk/instance-root? shape) (if (ctk/main-instance? shape) (if (not= context :not-component) - (report-error :root-main-not-allowed - (str/format "Root main component not allowed inside other component") - shape file page) - (validate-shape-main-root-top shape file page libraries)) + (report-error! :root-main-not-allowed + "Root main component not allowed inside other component" + shape file page) + (validate-shape-main-root-top! shape file page libraries)) (if (not= context :not-component) - (report-error :root-copy-not-allowed - (str/format "Root copy component not allowed inside other component") - shape file page) - (validate-shape-copy-root-top shape file page libraries))) + (report-error! :root-copy-not-allowed + "Root copy component not allowed inside other component" + shape file page) + (validate-shape-copy-root-top! shape file page libraries))) (if (ctk/main-instance? shape) (if (= context :not-component) - (report-error :nested-main-not-allowed - (str/format "Nested main component only allowed inside other component") - shape file page) - (validate-shape-main-root-nested shape file page libraries)) + (report-error! :nested-main-not-allowed + "Nested main component only allowed inside other component" + shape file page) + (validate-shape-main-root-nested! shape file page libraries)) (if (= context :not-component) - (report-error :nested-copy-not-allowed - (str/format "Nested copy component only allowed inside other component") - shape file page) - (validate-shape-copy-root-nested shape file page libraries))))) + (report-error! :nested-copy-not-allowed + "Nested copy component only allowed inside other component" + shape file page) + (validate-shape-copy-root-nested! shape file page libraries))))) (if (ctk/in-component-copy? shape) (if-not (#{:copy-top :copy-nested :copy-any} context) - (report-error :not-head-copy-not-allowed - (str/format "Non-root copy only allowed inside a copy") - shape file page) - (validate-shape-copy-not-root shape file page libraries)) + (report-error! :not-head-copy-not-allowed + "Non-root copy only allowed inside a copy" + shape file page) + (validate-shape-copy-not-root! shape file page libraries)) (if (ctn/inside-component-main? (:objects page) shape) (if-not (#{:main-top :main-nested :main-any} context) - (report-error :not-head-main-not-allowed - (str/format "Non-root main only allowed inside a main component") - shape file page) - (validate-shape-main-not-root shape file page libraries)) + (report-error! :not-head-main-not-allowed + "Non-root main only allowed inside a main component" + shape file page) + (validate-shape-main-not-root! shape file page libraries)) (if (#{:main-top :main-nested :main-any} context) - (report-error :not-component-not-allowed - (str/format "Not compoments are not allowed inside a main") - shape file page) - (validate-shape-not-component shape file page libraries))))) + (report-error! :not-component-not-allowed + "Not compoments are not allowed inside a main" + shape file page) + (validate-shape-not-component! shape file page libraries))))))) - (deref *errors*)))) +(defn validate-shape + "Validate referential integrity and semantic coherence of + a shape and all its children. Returns a list of errors." + [shape-id file page libraries] + (binding [*errors* (volatile! [])] + (validate-shape! shape-id file page libraries) + (deref *errors*))) + +(def valid-fdata? + "Structural validation of file data using defined schema" + (sm/lazy-validator ::ctf/data)) + +(def get-fdata-explain + "Get schema explain data for file data" + (sm/lazy-explainer ::ctf/data)) + +(defn validate-file! + "Validate file data structure. + + If libraries are provided, then a full referential integrity and + semantic coherence check will be performed on all content of the + file. + + Raises a validation exception on first error found." + + ([file] (validate-file! file nil)) + ([{:keys [id data] :as file} libraries] + (when-not (valid-fdata? data) + (ex/raise :type :validation + :code :data-validation + :hint (str/ffmt "invalid file data found on file '%'" id) + :file-id id + ::sm/explain (get-fdata-explain data))) + + ;; If `libraries` is provided, this means the fill file + ;; validation is activated so we proceed to execute the + ;; validation + (when (seq libraries) + (doseq [page (filter :id (ctpl/pages-seq data))] + (validate-shape! uuid/zero file page libraries))) + + file)) (defn validate-file - "Validate referencial integrity and semantic coherence of all contents of a file." - [file libraries & {:keys [throw?] :or {throw? false}}] - (binding [*throw-on-error* throw? - *errors* (volatile! [])] - (->> (ctpl/pages-seq (:data file)) - (filter #(some? (:id %))) - (run! #(validate-shape uuid/zero file % libraries :throw? throw?))) - + "Validate referencial integrity and semantic coherence of + all contents of a file. Returns a list of errors." + [file libraries] + (binding [*errors* (volatile! [])] + (validate-file! file libraries) (deref *errors*))) diff --git a/common/src/app/common/types/pages_list.cljc b/common/src/app/common/types/pages_list.cljc index ad2019e13..1a1dbb566 100644 --- a/common/src/app/common/types/pages_list.cljc +++ b/common/src/app/common/types/pages_list.cljc @@ -33,8 +33,8 @@ (update :pages-index assoc id (dissoc page :index)))) (defn pages-seq - [file-data] - (vals (:pages-index file-data))) + [fdata] + (vals (:pages-index fdata))) (defn update-page [file-data page-id f] From 31c46a90b461d5484ac8fa21e6aa77f0159e245b Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:07:58 +0100 Subject: [PATCH 09/16] :sparkles: Add file structure validation on binfile import --- backend/src/app/rpc/commands/binfile.clj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index d9db769fa..852516ebf 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -12,6 +12,7 @@ [app.common.features :as cfeat] [app.common.files.defaults :as cfd] [app.common.files.migrations :as pmg] + [app.common.files.validate :as fval] [app.common.fressian :as fres] [app.common.logging :as l] [app.common.spec :as us] @@ -743,7 +744,13 @@ (update :pages-index relink-shapes) (update :components relink-shapes) (update :media relink-media) - (pmg/migrate-data)))) + (pmg/migrate-data) + (d/without-nils)))) + + ;; Without providing all libs, here we just + ;; peform a structural file data validation, + ;; full referential check is omited. + (fval/validate-file!) (postprocess-file) (update :features #(db/create-array conn "text" %)) (update :data blob/encode))] From 9e4ed0ea92382c45f3d59f9115287dfd1c278b10 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:08:29 +0100 Subject: [PATCH 10/16] :sparkles: Improve file validation process on update-file rpc method --- backend/src/app/rpc/commands/files_update.clj | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/backend/src/app/rpc/commands/files_update.clj b/backend/src/app/rpc/commands/files_update.clj index 63d352a9d..2cc9aa73d 100644 --- a/backend/src/app/rpc/commands/files_update.clj +++ b/backend/src/app/rpc/commands/files_update.clj @@ -178,7 +178,8 @@ (l/trace :hint "update-file" :time (dt/format-duration elapsed))))))))) (defn update-file - [{:keys [::db/conn ::mtx/metrics] :as cfg} {:keys [id file features changes changes-with-metadata skip-validate] :as params}] + [{:keys [::db/conn ::mtx/metrics] :as cfg} + {:keys [id file features changes changes-with-metadata] :as params}] (binding [cfeat/*current* features cfeat/*previous* (:features file)] (let [update-fn (cond-> update-file* @@ -188,16 +189,6 @@ (contains? features "fdata/objects-map") (wrap-with-objects-map-context)) - ;; TODO: this ruins performance. - ;; We must find some other way to do general validation. - libraries (when (and (contains? cf/flags :file-validation) - (not skip-validate)) - (let [libs (->> (files/get-file-libraries conn (:id file)) - (map #(get-file conn (:id %))) - (map #(update % :data blob/decode)) - (d/index-by :id))] - (assoc libs (:id file) file))) - changes (if changes-with-metadata (->> changes-with-metadata (mapcat :changes) vec) (vec changes)) @@ -225,9 +216,9 @@ (let [file (assoc file :features features) params (-> params (assoc :file file) - (assoc :libraries libraries) (assoc :changes changes) (assoc ::created-at (dt/now)))] + (-> (update-fn cfg params) (vary-meta assoc ::audit/replace-props {:id (:id file) @@ -237,12 +228,13 @@ :team-id (:team-id file)})))))) (defn- update-file* - [{:keys [::db/conn] :as cfg} {:keys [profile-id file libraries changes session-id ::created-at skip-validate] :as params}] + [{:keys [::db/conn] :as cfg} + {:keys [profile-id file changes session-id ::created-at skip-validate] :as params}] (let [;; Process the file data in the CLIMIT context; scheduling it ;; to be executed on a separated executor for avoid to do the ;; CPU intensive operation on vthread. file (-> (climit/configure cfg :update-file) - (climit/submit! (partial update-file-data file libraries changes skip-validate)))] + (climit/submit! (partial update-file-data conn file changes skip-validate)))] (db/insert! conn :file-change {:id (uuid/next) @@ -276,36 +268,44 @@ (get-lagged-changes conn params)))) (defn- update-file-data - [file libraries changes skip-validate] - (let [validate (fn [file] - (when (and (cf/flags :file-validation) - (not skip-validate)) - (val/validate-file file libraries :throw? true))) - file (-> file - (update :revn inc) - (update :data (fn [data] - (cond-> data - :always - (-> (blob/decode) - (assoc :id (:id file)) - (pmg/migrate-data)) + [conn file changes skip-validate] + (let [file (update file :data (fn [data] + (-> data + (blob/decode) + (assoc :id (:id file)) + (pmg/migrate-data)))) - :always - (cp/process-changes changes)))) - (d/tap-r validate)) + ;; WARNING: this ruins performance; maybe we need to find + ;; some other way to do general validation + libs (when (and (contains? cf/flags :file-validation) + (not skip-validate)) + ;; FIXME: we need properly handle pointer-map here ???? + (->> (files/get-file-libraries conn (:id file)) + (into [file] (map (fn [{:keys [id]}] + (binding [pmap/*load-fn* (partial files/load-pointer conn id)] + (-> (db/get conn :file {:id id}) + (files/decode-row) + (files/process-pointers deref) ; ensure all pointers resolved + (pmg/migrate-file)))))) + (d/index-by :id)))] - file (if (and (contains? cfeat/*current* "fdata/objects-map") - (not (contains? cfeat/*previous* "fdata/objects-map"))) - (enable-objects-map file) - file) + (-> file + (update :revn inc) + (update :data cp/process-changes changes) - file (if (and (contains? cfeat/*current* "fdata/pointer-map") - (not (contains? cfeat/*previous* "fdata/pointer-map"))) - (enable-pointer-map file) - file) - ] + ;; If `libs` is defined, then full validation is performed + (val/validate-file! libs) + + (cond-> (and (contains? cfeat/*current* "fdata/objects-map") + (not (contains? cfeat/*previous* "fdata/objects-map"))) + (enable-objects-map)) + + (cond-> (and (contains? cfeat/*current* "fdata/pointer-map") + (not (contains? cfeat/*previous* "fdata/pointer-map"))) + (enable-pointer-map)) + + (update :data blob/encode)))) - (update file :data blob/encode))) (defn- take-snapshot? "Defines the rule when file `data` snapshot should be saved." From 1c75e5b46bc92ec75a8ecac432f8cad9c46e81c8 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:09:40 +0100 Subject: [PATCH 11/16] :lipstick: Add minor cosmetic improvement on profile rpc ns --- backend/src/app/rpc/commands/profile.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index c79e4c773..a5735d694 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -53,7 +53,7 @@ [:props {:optional true} [:map-of {:title "ProfileProps"} :keyword :any]]]) -(def profile? +(def valid-profile? (sm/pred-fn schema:profile)) ;; --- QUERY: Get profile (own) @@ -95,7 +95,7 @@ (dm/assert! "expected valid profile data" - (profile? params)) + (valid-profile? params)) (db/with-atomic [conn pool] ;; NOTE: we need to retrieve the profile independently if we use From 9b3964e6d760bc6813ed9e895d55a5b2c5f83ece Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:10:57 +0100 Subject: [PATCH 12/16] :lipstick: Add naming improvements to some file type predicates --- common/src/app/common/types/file.cljc | 4 ++-- frontend/src/app/main/data/workspace/libraries.cljs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index ad0e42e4b..b7a9800cd 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -58,10 +58,10 @@ [:media {:optional true} [:map-of {:gen/max 5} ::sm/uuid ::media-object]]]) -(def file-data? +(def valid-file-data? (sm/pred-fn ::data)) -(def media-object? +(def valid-media-object? (sm/pred-fn ::media-object)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 05f4d3698..c1fbac8df 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -182,7 +182,7 @@ (defn add-media [media] - (dm/assert! (ctf/media-object? media)) + (dm/assert! (ctf/valid-media-object? media)) (ptk/reify ::add-media ptk/WatchEvent (watch [it _ _] From d5e34df364479c5fbe064a8650aeb6b1a0f5b988 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:11:36 +0100 Subject: [PATCH 13/16] :bug: Fix incorrect frontend error handling on import code --- frontend/src/app/worker/import.cljs | 100 +++++++++++++++------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/frontend/src/app/worker/import.cljs b/frontend/src/app/worker/import.cljs index 8e46f3846..b1ef6253c 100644 --- a/frontend/src/app/worker/import.cljs +++ b/frontend/src/app/worker/import.cljs @@ -14,6 +14,7 @@ [app.common.geom.shapes.path :as gpa] [app.common.logging :as log] [app.common.media :as cm] + [app.common.pprint :as pp] [app.common.text :as ct] [app.common.uuid :as uuid] [app.main.repo :as rp] @@ -639,6 +640,7 @@ (let [error (or (.-message data) (tr "dashboard.import.analyze-error"))] (rx/of {:uri (:uri file) :error error})))))))))) + (defmethod impl/handler :import-files [{:keys [project-id files features]}] @@ -648,52 +650,60 @@ zip-files (filter #(= "application/zip" (:type %)) files) binary-files (filter #(= "application/octet-stream" (:type %)) files)] - (->> (rx/merge - (->> (create-files context zip-files) - (rx/flat-map - (fn [[file data]] - (->> (uz/load-from-url (:uri data)) - (rx/map #(-> context (assoc :zip %) (merge data))) - (rx/merge-map - (fn [context] - ;; process file retrieves a stream that will emit progress notifications - ;; and other that will emit the files once imported - (let [[progress-stream file-stream] (process-file context file)] - (rx/merge progress-stream - (->> file-stream - (rx/map - (fn [file] - {:status :import-finish - :errors (:errors file) - :file-id (:file-id data)}))))))) - (rx/catch (fn [cause] - (log/error :hint (ex-message cause) :file-id (:file-id data) :cause cause) - (rx/of {:status :import-error - :file-id (:file-id data) - :error (ex-message cause) - :error-data (ex-data cause)}))))))) + (rx/merge + (->> (create-files context zip-files) + (rx/flat-map + (fn [[file data]] + (->> (uz/load-from-url (:uri data)) + (rx/map #(-> context (assoc :zip %) (merge data))) + (rx/merge-map + (fn [context] + ;; process file retrieves a stream that will emit progress notifications + ;; and other that will emit the files once imported + (let [[progress-stream file-stream] (process-file context file)] + (rx/merge progress-stream + (->> file-stream + (rx/map + (fn [file] + {:status :import-finish + :errors (:errors file) + :file-id (:file-id data)}))))))) + (rx/catch (fn [cause] + (log/error :hint (ex-message cause) + :file-id (:file-id data) + :cause cause) + (rx/of {:status :import-error + :file-id (:file-id data) + :error (ex-message cause) + :error-data (ex-data cause)}))))))) - (->> (rx/from binary-files) - (rx/flat-map - (fn [data] - (->> (http/send! - {:uri (:uri data) - :response-type :blob - :method :get}) - (rx/map :body) - (rx/mapcat #(rp/cmd! :import-binfile {:file % - :project-id project-id})) - (rx/map - (fn [_] - {:status :import-finish - :file-id (:file-id data)}))))))) + (->> (rx/from binary-files) + (rx/flat-map + (fn [data] + (->> (http/send! + {:uri (:uri data) + :response-type :blob + :method :get}) + (rx/map :body) + (rx/mapcat #(rp/cmd! :import-binfile {:file % :project-id project-id})) + (rx/map (fn [_] + {:status :import-finish + :file-id (:file-id data)})) + (rx/catch (fn [cause] + (log/error :hint "unexpected error on import process" + :project-id project-id + ::log/sync? true) + ;; TODO: consider do thi son logging directly ? - (rx/catch (fn [cause] - (log/error :hint "unexpected error on import process" - :project-id project-id - :cause cause) - (if (map? cause) - (js/console.error (pr-str cause)) - (js/console.error cause))))))) + (when (map? cause) + (println "Error data:") + (pp/pprint (dissoc cause :explain) {:level 2 :length 10})) + (when (string? (:explain cause)) + (js/console.log (:explain cause))) + + (rx/of {:status :import-error + :file-id (:file-id data) + :error (:hint cause) + :error-data cause})))))))))) From 0a656e9e624301b9c9c620b366b7b2df92b10916 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 11 Nov 2023 00:13:01 +0100 Subject: [PATCH 14/16] :paperclip: Leave commented useful code that causes some warnings on load --- backend/dev/user.clj | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/backend/dev/user.clj b/backend/dev/user.clj index be13bb80f..043679aeb 100644 --- a/backend/dev/user.clj +++ b/backend/dev/user.clj @@ -122,23 +122,23 @@ (stop) (repl/refresh-all :after 'user/start)) -(defn compression-bench - [data] - (let [humanize (fn [v] (hum/filesize v :binary true :format " %.4f ")) - v1 (time (humanize (alength (blob/encode data {:version 1})))) - v3 (time (humanize (alength (blob/encode data {:version 3})))) - v4 (time (humanize (alength (blob/encode data {:version 4})))) - v5 (time (humanize (alength (blob/encode data {:version 5})))) - v6 (time (humanize (alength (blob/encode data {:version 6})))) - ] - (print-table - [{ - :v1 v1 - :v3 v3 - :v4 v4 - :v5 v5 - :v6 v6 - }]))) +;; (defn compression-bench +;; [data] +;; (let [humanize (fn [v] (hum/filesize v :binary true :format " %.4f ")) +;; v1 (time (humanize (alength (blob/encode data {:version 1})))) +;; v3 (time (humanize (alength (blob/encode data {:version 3})))) +;; v4 (time (humanize (alength (blob/encode data {:version 4})))) +;; v5 (time (humanize (alength (blob/encode data {:version 5})))) +;; v6 (time (humanize (alength (blob/encode data {:version 6})))) +;; ] +;; (print-table +;; [{ +;; :v1 v1 +;; :v3 v3 +;; :v4 v4 +;; :v5 v5 +;; :v6 v6 +;; }]))) (defonce debug-tap (do From 26d3d7f1a8214e8d56de20544aec1659f8f37b36 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 13 Nov 2023 18:09:25 +0100 Subject: [PATCH 15/16] :bug: Fix features related issues with render entrypoint (exporter) --- backend/src/app/rpc/commands/files.clj | 22 +- backend/src/app/rpc/commands/teams.clj | 14 +- common/src/app/common/schema.cljc | 5 + frontend/src/app/main/data/workspace.cljs | 4 +- frontend/src/app/main/render.cljs | 67 ++-- .../src/app/main/ui/viewer/thumbnails.cljs | 2 +- frontend/src/app/render.cljs | 373 ++++++++++-------- frontend/src/app/worker/thumbnails.cljs | 4 +- 8 files changed, 264 insertions(+), 227 deletions(-) diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index 28ff8ad61..7db6cd78a 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -443,10 +443,17 @@ "Given the page data and the object-id returns the page data with all other not needed objects removed from the `:objects` data structure." - [{:keys [objects] :as page} object-id] - (let [objects (->> (cph/get-children-with-self objects object-id) - (filter some?))] - (assoc page :objects (d/index-by :id objects)))) + [page id-or-ids] + (update page :objects (fn [objects] + (reduce (fn [result object-id] + (->> (cph/get-children-with-self objects object-id) + (filter some?) + (d/index-by :id) + (merge result))) + {} + (if (uuid? id-or-ids) + [id-or-ids] + id-or-ids))))) (defn- prune-thumbnails "Given the page data, removes the `:thumbnail` prop from all @@ -480,7 +487,7 @@ page)))] (cond-> (prune-thumbnails page) - (uuid? object-id) + (some? object-id) (prune-objects object-id)))) (def schema:get-page @@ -488,7 +495,7 @@ [:file-id ::sm/uuid] [:page-id {:optional true} ::sm/uuid] [:share-id {:optional true} ::sm/uuid] - [:object-id {:optional true} ::sm/uuid] + [:object-id {:optional true} [:or ::sm/uuid ::sm/coll-of-uuid]] [:features {:optional true} ::cfeat/features]]) (sv/defmethod ::get-page @@ -500,7 +507,8 @@ If you specify the object-id, the page-id parameter becomes mandatory. - Mainly used for rendering purposes." + Mainly used for rendering purposes on the exporter. It does not + accepts client features." {::doc/added "1.17" ::sm/params schema:get-page} [cfg {:keys [::rpc/profile-id file-id share-id] :as params}] diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index 39dc53495..92f0a2b2f 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -138,14 +138,20 @@ (declare get-team) (def ^:private schema:get-team - [:map {:title "get-team"} - [:id ::sm/uuid]]) + [:and + [:map {:title "get-team"} + [:id {:optional true} ::sm/uuid] + [:file-id {:optional true} ::sm/uuid]] + + [:fn (fn [params] + (or (contains? params :id) + (contains? params :file-id)))]]) (sv/defmethod ::get-team {::doc/added "1.17" ::sm/params schema:get-team} - [cfg {:keys [::rpc/profile-id id]}] - (db/tx-run! cfg #(get-team % :profile-id profile-id :team-id id))) + [cfg {:keys [::rpc/profile-id id file-id]}] + (db/tx-run! cfg #(get-team % :profile-id profile-id :team-id id :file-id file-id))) (defn get-team [conn & {:keys [profile-id team-id project-id file-id] :as params}] diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc index 82aca3ffe..047118742 100644 --- a/common/src/app/common/schema.cljc +++ b/common/src/app/common/schema.cljc @@ -143,6 +143,11 @@ ([s options transformer] (m/decoder s options transformer))) +(defn lazy-decoder + [s transformer] + (let [vfn (delay (decoder s transformer))] + (fn [v] (@vfn v)))) + (defn humanize-data [{:keys [schema errors value]} & {:keys [length level]}] (let [errors (mapv #(update % :schema form) errors)] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index cf26ed78e..dabc8ecf6 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -326,8 +326,8 @@ (rx/map deref) (rx/map bundle-fetched))) - (rx/take-until - (rx/filter (ptk/type? ::fetch-bundle) stream)))))) + (rx/take-until + (rx/filter (ptk/type? ::fetch-bundle) stream)))))) (defn initialize-file [project-id file-id] diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 5b7fbcaa8..93df65f9a 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -43,7 +43,6 @@ [app.main.ui.shapes.text :as text] [app.main.ui.shapes.text.fontfaces :as ff] [app.util.http :as http] - [app.util.object :as obj] [app.util.strings :as ust] [app.util.thumbnails :as th] [app.util.timers :as ts] @@ -83,11 +82,11 @@ (let [shape-wrapper (shape-wrapper-factory objects) frame-shape (frame/frame-shape shape-wrapper)] (mf/fnc frame-wrapper - [{:keys [shape] :as props}] - - (let [render-thumbnails? (mf/use-ctx muc/render-thumbnails) - childs (mapv #(get objects %) (:shapes shape))] - (if (and render-thumbnails? (some? (:thumbnail shape))) + {::mf/wrap-props false} + [{:keys [shape]}] + (let [thumbnails? (mf/use-ctx muc/render-thumbnails) + childs (mapv (d/getf objects) (:shapes shape))] + (if (and thumbnails? (some? (:thumbnail shape))) [:& frame/frame-thumbnail {:shape shape :bounds (:children-bounds shape)}] [:& frame-shape {:shape shape :childs childs}]))))) @@ -193,8 +192,8 @@ (mf/defc page-svg {::mf/wrap [mf/memo]} - [{:keys [data thumbnails? render-embed? include-metadata?] :as props - :or {render-embed? false include-metadata? false}}] + [{:keys [data use-thumbnails embed include-metadata] :as props + :or {embed false include-metadata false}}] (let [objects (:objects data) shapes (cph/get-immediate-children objects) dim (calculate-dimensions objects) @@ -206,20 +205,20 @@ (mf/deps objects) #(shape-wrapper-factory objects))] - [:& (mf/provider muc/render-thumbnails) {:value thumbnails?} - [:& (mf/provider embed/context) {:value render-embed?} - [:& (mf/provider export/include-metadata-ctx) {:value include-metadata?} + [:& (mf/provider muc/render-thumbnails) {:value use-thumbnails} + [:& (mf/provider embed/context) {:value embed} + [:& (mf/provider export/include-metadata-ctx) {:value include-metadata} [:svg {:view-box vbox :version "1.1" :xmlns "http://www.w3.org/2000/svg" :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") + :xmlns:penpot (when include-metadata "https://penpot.app/xmlns") :style {:width "100%" :height "100%" :background bgcolor} :fill "none"} - (when include-metadata? + (when include-metadata [:& export/export-page {:id (:id data) :options (:options data)}]) (let [shapes (->> shapes @@ -250,9 +249,9 @@ ;; the viewer and inspector (mf/defc frame-svg {::mf/wrap [mf/memo]} - [{:keys [objects frame zoom show-thumbnails?] :or {zoom 1} :as props}] - (let [frame-id (:id frame) - include-metadata? (mf/use-ctx export/include-metadata-ctx) + [{:keys [objects frame zoom use-thumbnails] :or {zoom 1} :as props}] + (let [frame-id (:id frame) + include-metadata (mf/use-ctx export/include-metadata-ctx) bounds (gsb/get-object-bounds objects frame) @@ -294,14 +293,14 @@ height (* (:height bounds) zoom) vbox (format-viewbox {:width (:width bounds 0) :height (:height bounds 0)})] - [:& (mf/provider muc/render-thumbnails) {:value show-thumbnails?} + [:& (mf/provider muc/render-thumbnails) {:value use-thumbnails} [:svg {:view-box vbox :width (ust/format-precision width viewbox-decimal-precision) :height (ust/format-precision height viewbox-decimal-precision) :version "1.1" :xmlns "http://www.w3.org/2000/svg" :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") + :xmlns:penpot (when include-metadata "https://penpot.app/xmlns") :fill "none"} [:& shape-wrapper {:shape frame}]]])) @@ -312,7 +311,7 @@ [{:keys [objects root-shape zoom] :or {zoom 1} :as props}] (when root-shape (let [root-shape-id (:id root-shape) - include-metadata? (mf/use-ctx export/include-metadata-ctx) + include-metadata (mf/use-ctx export/include-metadata-ctx) vector (mf/use-memo @@ -348,7 +347,7 @@ :version "1.1" :xmlns "http://www.w3.org/2000/svg" :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") + :xmlns:penpot (when include-metadata "https://penpot.app/xmlns") :fill "none"} [:> shape-container {:shape root-shape'} @@ -357,8 +356,8 @@ (mf/defc object-svg {::mf/wrap [mf/memo]} - [{:keys [objects object-id render-embed?] - :or {render-embed? false} + [{:keys [objects object-id embed] + :or {embed false} :as props}] (let [object (get objects object-id) object (cond-> object @@ -375,7 +374,7 @@ (shape-wrapper-factory objects))] [:& (mf/provider export/include-metadata-ctx) {:value false} - [:& (mf/provider embed/context) {:value render-embed?} + [:& (mf/provider embed/context) {:value embed} [:svg {:id (dm/str "screenshot-" object-id) :view-box vbox :width (ust/format-precision width viewbox-decimal-precision) @@ -439,20 +438,16 @@ :group [:& group-wrapper {:shape root-shape :view-box vbox}] :frame [:& frame-wrapper {:shape root-shape :view-box vbox}])]])) -(mf/defc components-sprite-svg +(mf/defc components-svg {::mf/wrap-props false} - [props] - (let [data (obj/get props "data") - children (obj/get props "children") - render-embed? (obj/get props "render-embed?") - include-metadata? (obj/get props "include-metadata?") - source (keyword (obj/get props "source" "components"))] - [:& (mf/provider embed/context) {:value render-embed?} - [:& (mf/provider export/include-metadata-ctx) {:value include-metadata?} + [{:keys [data children embed include-metadata source]}] + (let [source (keyword (d/nilv source "components"))] + [:& (mf/provider embed/context) {:value embed} + [:& (mf/provider export/include-metadata-ctx) {:value include-metadata} [:svg {:version "1.1" :xmlns "http://www.w3.org/2000/svg" :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") + :xmlns:penpot (when include-metadata "https://penpot.app/xmlns") :style {:display (when-not (some? children) "none")} :fill "none"} [:defs @@ -511,7 +506,7 @@ (->> (rx/of data) (rx/map (fn [data] - (let [elem (mf/element page-svg #js {:data data :render-embed? true :include-metadata? true})] + (let [elem (mf/element page-svg #js {:data data :embed true :include-metadata true})] (rds/renderToStaticMarkup elem))))))) (defn render-components @@ -531,8 +526,8 @@ (->> (rx/of data) (rx/map (fn [data] - (let [elem (mf/element components-sprite-svg - #js {:data data :render-embed? true :include-metadata? true + (let [elem (mf/element components-svg + #js {:data data :embed true :include-metadata true :source (name source)})] (rds/renderToStaticMarkup elem)))))))) diff --git a/frontend/src/app/main/ui/viewer/thumbnails.cljs b/frontend/src/app/main/ui/viewer/thumbnails.cljs index 4dc8c8394..6e8423c76 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/app/main/ui/viewer/thumbnails.cljs @@ -88,7 +88,7 @@ (assoc :thumbnail (get thumbnail-data (dm/str page-id (:id frame)))) (assoc :children-bounds children-bounds)) :objects objects - :show-thumbnails? true}]] + :use-thumbnails true}]] [:div.thumbnail-info [:span.name {:title (:name frame)} (:name frame)]]])) diff --git a/frontend/src/app/render.cljs b/frontend/src/app/render.cljs index 485d72081..1bb1d1387 100644 --- a/frontend/src/app/render.cljs +++ b/frontend/src/app/render.cljs @@ -8,125 +8,54 @@ "The main entry point for UI part needed by the exporter." (:require [app.common.geom.shapes.bounds :as gsb] - [app.common.logging :as l] + [app.common.logging :as log] [app.common.math :as mth] - [app.common.spec :as us] + [app.common.schema :as sm] [app.common.types.components-list :as ctkl] [app.common.uri :as u] [app.main.data.fonts :as df] - [app.main.features :as feat] + [app.main.data.users :as du] + [app.main.features :as features] [app.main.render :as render] [app.main.repo :as repo] [app.main.store :as st] [app.util.dom :as dom] [app.util.globals :as glob] [beicon.core :as rx] - [clojure.spec.alpha :as s] [cuerdas.core :as str] [garden.core :refer [css]] + [okulary.core :as l] + [potok.core :as ptk] [rumext.v2 :as mf])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; SETUP -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(log/setup! {:app :info}) -(l/setup! {:app :info}) - -(defonce app-root - (let [el (dom/get-element "app")] - (mf/create-root el))) - -(declare ^:private render-single-object) -(declare ^:private render-components) -(declare ^:private render-objects) - -(defn- parse-params - [loc] - (let [href (unchecked-get loc "href")] - (some-> href u/uri :query u/query-string->map))) - -(defn init-ui - [] - (when-let [params (parse-params glob/location)] - (when-let [component (case (:route params) - "objects" (render-objects params) - "components" (render-components params) - nil)] - (mf/render! app-root component)))) - -(defn ^:export init - [] - (st/emit! (feat/initialize)) - (init-ui)) - -(defn reinit - [] - (mf/unmount! app-root) - (init-ui)) - -(defn ^:dev/after-load after-load - [] - (reinit)) +(defn- fetch-team + [& {:keys [file-id]}] + (ptk/reify ::fetch-team + ptk/WatchEvent + (watch [_ _ _] + (->> (repo/cmd! :get-team {:file-id file-id}) + (rx/mapcat (fn [team] + (rx/of (du/set-current-team team) + (ptk/data-event ::team-fetched team)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; COMPONENTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ---- SINGLE OBJECT - -(defn use-resource - "A general purpose hook for retrieve or subscribe to remote changes - using the reactive-streams mechanism mechanism. - - It receives a function to execute for retrieve the stream that will - be used for creating the subscription. The function should be - stable, so is the responsibility of the user of this hook to - properly memoize it. - - TODO: this should be placed in some generic hooks namespace but his - right now is pending of refactor and it will be done later." - [f] - (let [[state ^js update-state!] (mf/useState {:loaded? false})] - (mf/with-effect [f] - (update-state! (fn [prev] (assoc prev :refreshing? true))) - (let [on-value (fn [data] - (update-state! #(-> % - (assoc :refreshing? false) - (assoc :loaded? true) - (merge data)))) - subs (rx/subscribe (f) on-value)] - #(rx/dispose! subs))) - state)) +(def ^:private ref:objects + (l/derived :objects st/state)) (mf/defc object-svg - [{:keys [page-id file-id share-id object-id render-embed?]}] - (let [components-v2 (feat/use-feature "components/v2") - fetch-state (mf/use-fn - (mf/deps file-id page-id share-id object-id components-v2) - (fn [] - (let [features (cond-> #{} components-v2 (conj "components/v2"))] - (->> (rx/zip - (repo/cmd! :get-font-variants {:file-id file-id :share-id share-id}) - (repo/cmd! :get-page {:file-id file-id - :page-id page-id - :share-id share-id - :object-id object-id - :features features})) - (rx/tap (fn [[fonts]] - (when (seq fonts) - (st/emit! (df/fonts-fetched fonts))))) - (rx/map (comp :objects second)) - (rx/map (fn [objects] - (let [objects (render/adapt-objects-for-shape objects object-id)] - {:objects objects - :object (get objects object-id)}))))))) - - {:keys [objects object]} (use-resource fetch-state)] + {::mf/wrap-props false} + [{:keys [object-id embed]}] + (let [objects (mf/deref ref:objects)] ;; Set the globa CSS to assign the page size, needed for PDF ;; exportation process. - (mf/with-effect [object] - (when object + (mf/with-effect [objects] + (when-let [object (get objects object-id)] (let [{:keys [width height]} (gsb/get-object-bounds [objects] object)] (dom/set-page-style! {:size (str/concat @@ -137,90 +66,107 @@ [:& render/object-svg {:objects objects :object-id object-id - :render-embed? render-embed?}]))) + :embed embed}]))) (mf/defc objects-svg - [{:keys [page-id file-id share-id object-ids render-embed?]}] - (let [components-v2 (feat/use-feature "components/v2") - fetch-state (mf/use-fn - (mf/deps file-id page-id share-id components-v2) - (fn [] - (let [features (cond-> #{} components-v2 (conj "components/v2"))] - (->> (rx/zip - (repo/cmd! :get-font-variants {:file-id file-id :share-id share-id}) - (repo/cmd! :get-page {:file-id file-id - :page-id page-id - :share-id share-id - :features features})) - (rx/tap (fn [[fonts]] - (when (seq fonts) - (st/emit! (df/fonts-fetched fonts))))) - (rx/map (fn [[_ page]] {:objects (:objects page)})))))) + {::mf/wrap-props false} + [{:keys [object-ids embed]}] + (when-let [objects (mf/deref ref:objects)] + (for [object-id object-ids] + (let [objects (render/adapt-objects-for-shape objects object-id)] + [:& render/object-svg + {:objects objects + :key (str object-id) + :object-id object-id + :embed embed}])))) - {:keys [objects]} (use-resource fetch-state)] +(defn- fetch-objects-bundle + [& {:keys [file-id page-id share-id object-id] :as options}] + (ptk/reify ::fetch-objects-bundle + ptk/WatchEvent + (watch [_ state _] + (let [features (features/get-team-enabled-features state)] + (->> (rx/zip + (repo/cmd! :get-font-variants {:file-id file-id :share-id share-id}) + (repo/cmd! :get-page {:file-id file-id + :page-id page-id + :share-id share-id + :object-id object-id + :features features})) + (rx/tap (fn [[fonts]] + (when (seq fonts) + (st/emit! (df/fonts-fetched fonts))))) + (rx/observe-on :async) + (rx/map (comp :objects second)) + (rx/map (fn [objects] + (let [objects (render/adapt-objects-for-shape objects object-id)] + #(assoc % :objects objects))))))))) - (when objects - (for [object-id object-ids] - (let [objects (render/adapt-objects-for-shape objects object-id)] - [:& render/object-svg - {:objects objects - :key (str object-id) - :object-id object-id - :render-embed? render-embed?}]))))) +(def ^:private schema:render-objects + [:map {:title "render-objets"} + [:page-id ::sm/uuid] + [:file-id ::sm/uuid] + [:share-id {:optional true} ::sm/uuid] + [:embed {:optional true} :boolean] + [:object-id + [:or + ::sm/uuid + ::sm/coll-of-uuid]]]) -(s/def ::page-id ::us/uuid) -(s/def ::file-id ::us/uuid) -(s/def ::share-id ::us/uuid) -(s/def ::object-id - (s/or :single ::us/uuid - :multiple (s/coll-of ::us/uuid))) -(s/def ::embed ::us/boolean) +(def ^:private render-objects-decoder + (sm/lazy-decoder schema:render-objects + sm/default-transformer)) -(s/def ::render-objects - (s/keys :req-un [::file-id ::page-id ::object-id] - :opt-un [::render-embed ::share-id])) +(def ^:private render-objects-validator + (sm/lazy-validator schema:render-objects)) (defn- render-objects [params] - (let [{:keys [file-id - page-id - render-embed - share-id] - :as params} - (us/conform ::render-objects params) + (let [{:keys [file-id page-id embed share-id object-id] :as params} (render-objects-decoder params)] + (if-not (render-objects-validator params) + (do + (js/console.error "invalid arguments") + (sm/pretty-explain schema:render-objects params) + nil) - [type object-id] (:object-id params)] - (case type - :single - (mf/html - [:& object-svg - {:file-id file-id - :page-id page-id - :share-id share-id - :object-id object-id - :render-embed? render-embed}]) + (do + (st/emit! (ptk/reify ::initialize-render-objects + ptk/WatchEvent + (watch [_ _ stream] + (rx/merge + (rx/of (fetch-team :file-id file-id)) - :multiple - (mf/html - [:& objects-svg - {:file-id file-id - :page-id page-id - :share-id share-id - :object-ids (into #{} object-id) - :render-embed? render-embed}])))) + (->> stream + (rx/filter (ptk/type? ::team-fetched)) + (rx/observe-on :async) + (rx/map (constantly params)) + (rx/map fetch-objects-bundle)))))) + + (if (uuid? object-id) + (mf/html + [:& object-svg + {:file-id file-id + :page-id page-id + :share-id share-id + :object-id object-id + :embed embed}]) + + (mf/html + [:& objects-svg + {:file-id file-id + :page-id page-id + :share-id share-id + :object-ids (into #{} object-id) + :embed embed}])))))) ;; ---- COMPONENTS SPRITE -(mf/defc components-sprite-svg - [{:keys [file-id embed] :as props}] - (let [fetch (mf/use-fn - (mf/deps file-id) - (fn [] (repo/cmd! :get-file {:id file-id}))) - - file (use-resource fetch) - state (mf/use-state nil)] - - (when file +(mf/defc components-svg + {::mf/wrap-props false} + [{:keys [embed component-id]}] + (let [file-ref (mf/with-memo [] (l/derived :file st/state)) + state (mf/use-state {:component-id component-id})] + (when-let [file (mf/deref file-ref)] [:* [:style (css [[:body @@ -266,7 +212,7 @@ [:a {:on-click on-click} (:name data)]]))] [:main - [:& render/components-sprite-svg + [:& render/components-svg {:data (:data file) :embed embed} @@ -275,16 +221,93 @@ ]))) -(s/def ::component-id ::us/uuid) -(s/def ::render-components - (s/keys :req-un [::file-id] - :opt-un [::embed ::component-id])) +(defn- fetch-components-bundle + [& {:keys [file-id]}] + (ptk/reify ::fetch-components-bundle + ptk/WatchEvent + (watch [_ state _] + (let [features (features/get-team-enabled-features state)] + (->> (repo/cmd! :get-file {:id file-id :features features}) + (rx/map (fn [file] #(assoc % :file file)))))))) + +(def ^:private schema:render-components + [:map {:title "render-components"} + [:file-id ::sm/uuid] + [:embed {:optional true} :boolean] + [:component-id {:optional true} ::sm/uuid]]) + +(def ^:private render-components-decoder + (sm/lazy-decoder schema:render-components + sm/default-transformer)) + +(def ^:private render-components-validator + (sm/lazy-validator schema:render-components)) (defn render-components [params] - (let [{:keys [file-id component-id embed]} (us/conform ::render-components params)] - (mf/html - [:& components-sprite-svg - {:file-id file-id - :component-id component-id - :embed embed}]))) + (let [{:keys [file-id component-id embed] :as params} (render-components-decoder params)] + (if-not (render-components-validator params) + (do + (js/console.error "invalid arguments") + (sm/pretty-explain schema:render-components params) + nil) + + (do + (st/emit! (ptk/reify ::initialize-render-components + ptk/WatchEvent + (watch [_ _ stream] + (rx/merge + (rx/of (fetch-team :file-id file-id)) + + (->> stream + (rx/filter (ptk/type? ::team-fetched)) + (rx/observe-on :async) + (rx/map (constantly params)) + (rx/map fetch-components-bundle)))))) + + (mf/html + [:& components-svg + {:component-id component-id + :embed embed}]))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; SETUP +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defonce app-root + (let [el (dom/get-element "app")] + (mf/create-root el))) + +(declare ^:private render-single-object) +(declare ^:private render-components) +(declare ^:private render-objects) + +(defn- parse-params + [loc] + (let [href (unchecked-get loc "href")] + (some-> href u/uri :query u/query-string->map))) + +(defn init-ui + [] + (when-let [params (parse-params glob/location)] + (when-let [component (case (:route params) + "objects" (render-objects params) + "components" (render-components params) + nil)] + (mf/render! app-root component)))) + +(defn ^:export init + [] + (st/emit! (features/initialize)) + (init-ui)) + +(defn reinit + [] + (init-ui)) + +(defn ^:dev/after-load after-load + [] + (reinit)) + + + diff --git a/frontend/src/app/worker/thumbnails.cljs b/frontend/src/app/worker/thumbnails.cljs index 56437a3d8..3e3cbbfef 100644 --- a/frontend/src/app/worker/thumbnails.cljs +++ b/frontend/src/app/worker/thumbnails.cljs @@ -63,8 +63,8 @@ (let [objects (:objects page) frame (some->> page :thumbnail-frame-id (get objects)) element (if frame - (mf/element render/frame-svg #js {:objects objects :frame frame :show-thumbnails? true}) - (mf/element render/page-svg #js {:data page :thumbnails? true :render-embed? true})) + (mf/element render/frame-svg #js {:objects objects :frame frame :use-thumbnails true}) + (mf/element render/page-svg #js {:data page :use-thumbnails true :embed true})) data (rds/renderToStaticMarkup element)] {:data data :fonts @fonts/loaded-hints From 5ebef181ae488fdf04fde72dc9c40de6eb575404 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 13 Nov 2023 18:27:51 +0100 Subject: [PATCH 16/16] :sparkles: Add the ability to setup custom tempdir on exporter --- exporter/src/app/config.cljs | 2 ++ exporter/src/app/util/shell.cljs | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/exporter/src/app/config.cljs b/exporter/src/app/config.cljs index c219857d0..832f6f86a 100644 --- a/exporter/src/app/config.cljs +++ b/exporter/src/app/config.cljs @@ -23,6 +23,7 @@ :host "localhost" :http-server-port 6061 :http-server-host "0.0.0.0" + :tempdir "/tmp/penpot-exporter" :redis-uri "redis://redis/0"}) (def ^:private schema:config @@ -32,6 +33,7 @@ [:tenant {:optional true} :string] [:flags {:optional true} ::sm/set-of-keywords] [:redis-uri {:optional true} :string] + [:tempdir {:optional true} :string] [:browser-pool-max {:optional true} :int] [:browser-pool-min {:optional true} :int]]) diff --git a/exporter/src/app/util/shell.cljs b/exporter/src/app/util/shell.cljs index 698efbc06..3a7440217 100644 --- a/exporter/src/app/util/shell.cljs +++ b/exporter/src/app/util/shell.cljs @@ -14,6 +14,7 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.uuid :as uuid] + [app.config :as cf] [cuerdas.core :as str] [promesa.core :as p])) @@ -22,12 +23,12 @@ (def tempfile-minage (* 1000 60 60 1)) ;; 1h (def tmpdir - (let [path (path/join (os/tmpdir) "penpot")] + (let [path (cf/get :tempdir)] + (l/inf :hint "tmptdir setup" :path path) (when-not (fs/existsSync path) (fs/mkdirSync path #js {:recursive true})) path)) - (defn- schedule-deletion! [path] (letfn [(remote-tempfile []