Compare commits

..
62 Commits
Author SHA1 Message Date
Ruben Fiszel 39f0752525 all 2024-08-19 21:43:22 +02:00
Ruben Fiszel 1ee78da4bf Merge branch 'main' into rf/foo2 2024-08-19 12:43:08 +02:00
Ruben Fiszel 451b6bb22d nit remove debug text 2024-08-19 12:34:56 +02:00
Ruben Fiszel 0a336467d8 all 2024-08-19 12:32:43 +02:00
Ruben Fiszel 250237793b fix: improve resource picker handling of objects 2024-08-19 11:35:16 +02:00
Faton RamadaniandRuben Fiszel 632bd18a2e use xyflow internally instead of svelvet for graph rendering (#4173)
* fix(frontend): wip

* fix(frontend): wip

* fix(frontend): fix while loops + theme

* fix(frontend): wip

* fix(frontend): wip

* fix(frontend): same view as before

* fix(frontend): wip

* fix(frontend): wip

* feat(frontend): flow status viewer

* fix(frontend): wip

* feat(frontend): migrate to xyflow

* feat(frontend): dataflow

* feat(frontend): clean up

* feat(frontend): clean up

* feat(frontend): clean up

* feat(frontend): fix min-height

* feat(frontend): clean up

* feat(frontend): add missing insert button for empty loops

* feat(frontend): Fix insert

* feat(frontend): Fix branch one

* feat(frontend): Support default branch for branch one

* feat(frontend): fix add and delete branches

* feat(frontend): Make the iterations menu work

* feat(frontend): Fix graph height in the flow status viewer

* feat(frontend): code improvement

* feat(frontend): add missing trigger

* feat(frontend): add support for triggers

* feat(frontend): fix move

* feat(frontend): fix No branch for branch one

* feat(frontend): fix light theme

* feat(frontend): fix build

* feat(frontend): remove dead code

* feat(frontend): fix move

* feat(frontend): fix vertical alignement

* feat(frontend): fix viewport

* feat(frontend): style fix

* feat(frontend): fix viewgraph

* feat(frontend): fix build

* feat(frontend): Fix node position

* feat(frontend): UI nits

* feat(frontend): UI nits

* feat(frontend): fix zoom level + fix insert button position

* feat(frontend): svelvet clean up

* feat(frontend): fix paste button position

* feat(frontend): clean up

* feat(frontend): migrate Decision Tree

* feat(frontend): clean up

* feat(frontend): add callback

* feat(frontend): migrate Decision Tree

* feat(frontend): fix build

* feat(frontend): fix branches

* feat(frontend): fix branches

* feat(frontend): fix insert

* feat(frontend): wip

* feat(frontend): done

* feat(frontend): done

* feat(frontend): fix reactivity

* feat(frontend): fix z-index issues

* feat(frontend): fix bg issue

* feat(frontend): fix border status

* add padding to flow height

* Update FlowGraphV2.svelte

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-08-19 09:17:56 +02:00
Ruben Fiszelandrubenfiszel de4f5e3015 chore(main): release 1.381.0 (#4248)
* chore(main): release 1.381.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-16 17:34:20 +02:00
HugoCasa 99f7828ebb feat: cache common hub scripts in image (#4249)
* feat: cache common hub scripts in image

* Delete backend/src/hubPaths.json

* fix: missing file

* fix: dockerfile

* fix: cache by path

* fix

* fix

* precreate cache folder
2024-08-16 17:30:44 +02:00
Ruben Fiszel 5a8fa1d724 fix: improve password field lifetime incorrectly recycled too early 2024-08-16 17:28:10 +02:00
Ruben Fiszel 49835ca6ca fix: workspace specific default tags do not override step level custom tags 2024-08-16 17:00:34 +02:00
HugoCasa 43459ce03a sqlx for refresh cgroup readings (#4251) 2024-08-16 14:11:56 +02:00
HugoCasa e23c3fad62 feat: add env to refresh cgroup readings (#4250) 2024-08-16 14:00:38 +02:00
Ruben Fiszel dffd5f7f7d fix(cli): add inject and define options 2024-08-15 19:04:11 +02:00
Ruben Fiszel 55340029db export runFlow and runFlowAsync 2024-08-15 15:17:32 +02:00
Ruben Fiszel c9ef2c8e97 fix(typescript-client): add runFlow and runFlowAsync 2024-08-15 14:21:33 +02:00
Ruben Fiszel 3cf4f00dca fix: improve row update of aggrid table actions II 2024-08-15 12:47:21 +02:00
Ruben Fiszel 2afdb66e75 improve row update of aggrid table actions 2024-08-15 12:07:46 +02:00
HugoCasa 6705b6706f email triggers panel loading (#4247) 2024-08-15 11:12:10 +02:00
Ruben Fiszel c4282a0656 nit highlight code 2024-08-15 10:46:57 +02:00
HugoCasa 6c9e32af10 fix(frontend): improve UI for email triggers (#4243)
* fix(frontend): improve UI for email triggers

* fix: nits
2024-08-15 10:06:35 +02:00
Ruben Fiszelandrubenfiszel 5416254aa1 chore(main): release 1.380.0 (#4242)
* chore(main): release 1.380.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-14 19:48:20 +02:00
Ruben Fiszel 11b81e3d9d nit npn check fix 2024-08-14 19:19:26 +02:00
HugoCasa 0cce27636d feat: opt-in job args in audit logs (#4241)
* feat: job args in audit logs

* update ee ref
2024-08-14 19:00:52 +02:00
Ruben Fiszel e0857c7178 feat: recoverable error handlers 2024-08-14 18:45:29 +02:00
Ruben Fiszel 0eb96ded5f fix tests 2024-08-14 17:35:30 +02:00
Ruben Fiszel be90b3e219 feat: togglable continue on disapproval/timeout of approvals 2024-08-14 17:24:21 +02:00
Ruben Fiszel fbd9b87c0f dockerfile improvements 2024-08-14 15:49:33 +02:00
Ruben Fiszel 75d96baf50 small resilience nits 2024-08-14 15:24:05 +02:00
Ruben Fiszelandrubenfiszel 58adf6ad2d chore(main): release 1.379.4 (#4238)
* chore(main): release 1.379.4

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-14 14:48:36 +02:00
Ruben Fiszel ad698768e8 fix: fix transformer issue after proxy change for apps 2024-08-14 14:45:55 +02:00
Ruben Fiszel ecea507670 all 2024-08-14 14:34:58 +02:00
Ruben Fiszel acfe6325a1 continue on error 2024-08-14 14:34:58 +02:00
HugoCasa 3bd461ffa1 fix: use job timeout for snowflake timeout (#4240) 2024-08-14 14:27:43 +02:00
HugoCasa c09f078928 fix: handle time with tz col type in pg (#4239) 2024-08-14 13:25:56 +02:00
HugoCasa f88efc2380 fix: add missing change for better key renewal (#4237) 2024-08-14 09:55:23 +02:00
Ruben Fiszel 2aa40c413e nits whitelabel 2024-08-14 02:09:22 +02:00
Ruben Fiszelandrubenfiszel 9363e6d05f chore(main): release 1.379.3 (#4235)
* chore(main): release 1.379.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-14 01:00:31 +02:00
Ruben Fiszel 0be8982ddc fix: improve scrolling performance of the runs page 2024-08-14 00:26:34 +02:00
Ruben Fiszelandrubenfiszel 969699ce47 chore(main): release 1.379.2 (#4234)
* chore(main): release 1.379.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-13 23:59:18 +02:00
Ruben Fiszel b9924d01d5 fix: add fetch connection limits to bun type fetcher 2024-08-13 23:40:00 +02:00
Ruben Fiszel 35fed96cd3 fix compile 2024-08-13 22:53:32 +02:00
Ruben Fiszel bcc94badbd fix: extend step_id being returned as part of the error of every languages 2024-08-13 22:37:05 +02:00
Ruben Fiszel 2021b8f86a add INSTANCE_IS_DEV 2024-08-13 21:44:14 +02:00
Ruben Fiszel b626e52324 add bun bundling error info 2024-08-13 20:42:45 +02:00
Ruben Fiszel 7a68cc76c2 fix: improve logviewer behavior when job is loading 2024-08-13 20:34:46 +02:00
Ruben Fiszel b424c61e95 fix(frontend): date input is more flexible and accept default html format as a fallback 2024-08-13 18:46:01 +02:00
Ruben Fiszelandrubenfiszel 41bf933fb7 chore(main): release 1.379.1 (#4231)
* chore(main): release 1.379.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-13 18:04:08 +02:00
Ruben Fiszel f00545f169 fix: add an option to disable bundling globally 2024-08-13 18:00:50 +02:00
Ruben Fiszel 95a898a5d5 fix compile 2024-08-13 17:06:51 +02:00
Ruben Fiszel 1d0807f542 fix(apps): improve tanstack table handling of objects 2024-08-13 16:17:20 +02:00
Ruben Fiszel 527183874c overscan nit for job list 2024-08-13 16:06:10 +02:00
Ruben Fiszelandrubenfiszel 0d5c2e5dd7 chore(main): release 1.379.0 (#4228)
* chore(main): release 1.379.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-13 16:01:49 +02:00
Ruben Fiszel 599399870f nit w-full 2024-08-13 16:00:36 +02:00
Ruben Fiszel 955a980703 feat(frontend): group fields are mutable 2024-08-13 15:14:26 +02:00
Ruben Fiszel 7eccebf5da improve type hints II 2024-08-13 11:29:16 +02:00
Ruben Fiszel 1719c2689e fix(apps): type hints for results are automatically widened 2024-08-13 11:05:56 +02:00
Ruben Fiszel 2b99789077 feat(frontend): improve display of waiting jobs + schedule filter + suspended jobs on runs page 2024-08-13 10:41:36 +02:00
Ruben Fiszel fd1c456fad feat: embeddable apps using jwt (#4229)
* all

* all

* all
2024-08-13 00:11:54 +02:00
Ruben Fiszel 3c4b8377ed fix: support NODE_PATH 2024-08-12 19:56:27 +02:00
Ruben Fiszelandrubenfiszel 36129fb470 chore(main): release 1.378.0 (#4219)
* chore(main): release 1.378.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-08-12 11:23:57 +02:00
Ruben Fiszel e779f96386 fix: improve default value handling for date & date-time in apps 2024-08-12 11:21:00 +02:00
Ruben Fiszel df33b281e8 fix compile 2024-08-12 10:37:51 +02:00
236 changed files with 4420 additions and 6567 deletions
+95
View File
@@ -1,5 +1,100 @@
# Changelog
## [1.381.0](https://github.com/windmill-labs/windmill/compare/v1.380.0...v1.381.0) (2024-08-16)
### Features
* add env to refresh cgroup readings ([#4250](https://github.com/windmill-labs/windmill/issues/4250)) ([e23c3fa](https://github.com/windmill-labs/windmill/commit/e23c3fad628cc718314472f3cb53c0f257c4c9e5))
* cache common hub scripts in image ([#4249](https://github.com/windmill-labs/windmill/issues/4249)) ([99f7828](https://github.com/windmill-labs/windmill/commit/99f7828ebb5fddf799afb52af7214ba4119e57b9))
### Bug Fixes
* **cli:** add inject and define options ([dffd5f7](https://github.com/windmill-labs/windmill/commit/dffd5f7f7d5c84624953bf8c778b14586a14e2d4))
* **frontend:** improve UI for email triggers ([#4243](https://github.com/windmill-labs/windmill/issues/4243)) ([6c9e32a](https://github.com/windmill-labs/windmill/commit/6c9e32af104a897e5c2a5b41867d2ffbbacc8acb))
* improve password field lifetime incorrectly recycled too early ([5a8fa1d](https://github.com/windmill-labs/windmill/commit/5a8fa1d72487ac2a29dca8833b8c92b8cac3726e))
* improve row update of aggrid table actions II ([3cf4f00](https://github.com/windmill-labs/windmill/commit/3cf4f00dca677fc592d5dde8a7b8fdcac5f08e0a))
* **typescript-client:** add runFlow and runFlowAsync ([c9ef2c8](https://github.com/windmill-labs/windmill/commit/c9ef2c8e97bdecb16b9a54805f1c90523e3b406f))
* workspace specific default tags do not override step level custom tags ([49835ca](https://github.com/windmill-labs/windmill/commit/49835ca6ca65e569ea2915b2ef4f7ce1c4988cae))
## [1.380.0](https://github.com/windmill-labs/windmill/compare/v1.379.4...v1.380.0) (2024-08-14)
### Features
* opt-in job args in audit logs ([#4241](https://github.com/windmill-labs/windmill/issues/4241)) ([0cce276](https://github.com/windmill-labs/windmill/commit/0cce27636d128972730427a33b7be954d00210dc))
* recoverable error handlers ([e0857c7](https://github.com/windmill-labs/windmill/commit/e0857c7178ac9657173e9ed597d072130e41fe47))
* togglable continue on disapproval/timeout of approvals ([be90b3e](https://github.com/windmill-labs/windmill/commit/be90b3e2192cdae88e38fd7e269ad8abc8f7c054))
## [1.379.4](https://github.com/windmill-labs/windmill/compare/v1.379.3...v1.379.4) (2024-08-14)
### Bug Fixes
* add missing change for better key renewal ([#4237](https://github.com/windmill-labs/windmill/issues/4237)) ([f88efc2](https://github.com/windmill-labs/windmill/commit/f88efc2380bf5b055b2633b24cb94fc46e6cdca5))
* fix transformer issue after proxy change for apps ([ad69876](https://github.com/windmill-labs/windmill/commit/ad698768e8fdbcc4a90f138f74ceee20d2d68a9b))
* handle time with tz col type in pg ([#4239](https://github.com/windmill-labs/windmill/issues/4239)) ([c09f078](https://github.com/windmill-labs/windmill/commit/c09f078928fb9f4b52df641580505d98bb728aa0))
* use job timeout for snowflake timeout ([#4240](https://github.com/windmill-labs/windmill/issues/4240)) ([3bd461f](https://github.com/windmill-labs/windmill/commit/3bd461ffa1fcbe19c9007b1b15970be21cff08ef))
## [1.379.3](https://github.com/windmill-labs/windmill/compare/v1.379.2...v1.379.3) (2024-08-13)
### Bug Fixes
* improve scrolling performance of the runs page ([0be8982](https://github.com/windmill-labs/windmill/commit/0be8982ddc263b76dab2c87cb6fa63e21effd58c))
## [1.379.2](https://github.com/windmill-labs/windmill/compare/v1.379.1...v1.379.2) (2024-08-13)
### Bug Fixes
* add fetch connection limits to bun type fetcher ([b9924d0](https://github.com/windmill-labs/windmill/commit/b9924d01d54f6dfaf5d719c13a38bb3dccbfe3b9))
* extend step_id being returned as part of the error of every languages ([bcc94ba](https://github.com/windmill-labs/windmill/commit/bcc94badbd848b6838dd0e353c3a30e7a1889ff8))
* **frontend:** date input is more flexible and accept default html format as a fallback ([b424c61](https://github.com/windmill-labs/windmill/commit/b424c61e95e3cc9213d4d3efbe4173857d4e80fd))
* improve logviewer behavior when job is loading ([7a68cc7](https://github.com/windmill-labs/windmill/commit/7a68cc76c2d5d7a66f080afdfcb6c5cd09fc25b5))
## [1.379.1](https://github.com/windmill-labs/windmill/compare/v1.379.0...v1.379.1) (2024-08-13)
### Bug Fixes
* add an option to disable bundling globally ([f00545f](https://github.com/windmill-labs/windmill/commit/f00545f1691c5107d1103f89bf68fdc3915ddd82))
* **apps:** improve tanstack table handling of objects ([1d0807f](https://github.com/windmill-labs/windmill/commit/1d0807f54238c8d8d242e3e5534c6d0868b3172b))
## [1.379.0](https://github.com/windmill-labs/windmill/compare/v1.378.0...v1.379.0) (2024-08-13)
### Features
* embeddable apps using jwt ([#4229](https://github.com/windmill-labs/windmill/issues/4229)) ([fd1c456](https://github.com/windmill-labs/windmill/commit/fd1c456fad16229112d9329e7c7eb9d60561efc0))
* **frontend:** group fields are mutable ([955a980](https://github.com/windmill-labs/windmill/commit/955a980703362b78bf24a47ec348878d7aac94e3))
* **frontend:** improve display of waiting jobs + schedule filter + suspended jobs on runs page ([2b99789](https://github.com/windmill-labs/windmill/commit/2b99789077b66991902819b26b140e2681a51344))
### Bug Fixes
* **apps:** type hints for results are automatically widened ([1719c26](https://github.com/windmill-labs/windmill/commit/1719c2689e2cdcbf4e88a5133e0d6c5626c8d6d9))
* support NODE_PATH ([3c4b837](https://github.com/windmill-labs/windmill/commit/3c4b8377ed4689704cb7baaee13e8acaa91e8611))
## [1.378.0](https://github.com/windmill-labs/windmill/compare/v1.377.1...v1.378.0) (2024-08-12)
### Features
* **cli:** add customerBundler support ([70d1e6c](https://github.com/windmill-labs/windmill/commit/70d1e6c7208b59890ad4d15cad28b1f32d7970d2))
* windmill embed ([d56a956](https://github.com/windmill-labs/windmill/commit/d56a956b9a471cca4042f43ba4e3922e42f121f1))
### Bug Fixes
* allow user resources in app to work within iframes ([6272c9f](https://github.com/windmill-labs/windmill/commit/6272c9ff41e5359f38ae13e879aa615da0fb0d09))
* **frontend:** improve default id of the components of the topbar ([#4222](https://github.com/windmill-labs/windmill/issues/4222)) ([7115a35](https://github.com/windmill-labs/windmill/commit/7115a3577947c52aa72edcc134dcb8e739208df0))
* **frontend:** support plus sign in emails format fields [#4223](https://github.com/windmill-labs/windmill/issues/4223) ([c0853ea](https://github.com/windmill-labs/windmill/commit/c0853eafc9f9bf6d454c6a70800c98c75c9d8ba3))
* **frontend:** user resource picker for app use lightweight component ([c03c0f6](https://github.com/windmill-labs/windmill/commit/c03c0f61726e7b6196fe5222c44d4d635e907b20))
* improve default value handling for date & date-time in apps ([e779f96](https://github.com/windmill-labs/windmill/commit/e779f963869fcb22d73f02b6a806e3fdac978959))
* improve license key ui ([#4220](https://github.com/windmill-labs/windmill/issues/4220)) ([0655803](https://github.com/windmill-labs/windmill/commit/06558038601d6f78b234519d4b48f229a2bf9fc8))
## [1.377.1](https://github.com/windmill-labs/windmill/compare/v1.377.0...v1.377.1) (2024-08-08)
+15 -5
View File
@@ -59,7 +59,9 @@ ARG features=""
COPY --from=planner /windmill/recipe.json recipe.json
RUN apt-get update && apt-get install -y libxml2-dev libxmlsec1-dev clang libclang-dev cmake
RUN apt-get update && apt-get install -y libxml2-dev=2.9.* libxmlsec1-dev=1.2.* clang=1:14.0-55.* libclang-dev=1:14.0-55.* cmake=3.25.* && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
@@ -92,14 +94,17 @@ ARG WITH_HELM=true
RUN apt-get update \
&& apt-get install -y ca-certificates wget curl git jq unzip build-essential unixodbc xmlsec1 software-properties-common \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN if [ "$WITH_POWERSHELL" = "true" ]; then \
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then apt-get update -y && apt install libicu-dev -y && wget -O 'pwsh.deb' "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/powershell_${POWERSHELL_DEB_VERSION}.deb_amd64.deb" && \
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then apt-get update -y && apt install libicu-dev -y && wget -O 'pwsh.deb' "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/powershell_${POWERSHELL_DEB_VERSION}.deb_amd64.deb" && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && \
dpkg --install 'pwsh.deb' && \
rm 'pwsh.deb'; \
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then apt-get update -y && apt install libicu-dev -y && wget -O powershell.tar.gz "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/powershell-${POWERSHELL_VERSION}-linux-arm64.tar.gz" && \
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then apt-get update -y && apt install libicu-dev -y && wget -O powershell.tar.gz "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/powershell-${POWERSHELL_VERSION}-linux-arm64.tar.gz" && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && \
mkdir -p /opt/microsoft/powershell/7 && \
tar zxf powershell.tar.gz -C /opt/microsoft/powershell/7 && \
chmod +x /opt/microsoft/powershell/7/pwsh && \
@@ -143,7 +148,8 @@ ENV PATH="${PATH}:/usr/local/go/bin"
ENV GO_PATH=/usr/local/go/bin/go
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get -y update && apt-get install -y curl nodejs awscli
RUN apt-get -y update && apt-get install -y curl nodejs awscli && apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# go build is slower the first time it is ran, so we prewarm it in the build
RUN mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm && printf "package foo\nimport (\"fmt\")\nfunc main() { fmt.Println(42) }" > warm.go && go mod tidy && go build -x && rm -rf /tmp/gobuildwarm
@@ -169,7 +175,11 @@ WORKDIR ${APP}
RUN ln -s ${APP}/windmill /usr/local/bin/windmill
RUN windmill cache
COPY ./frontend/src/lib/hubPaths.json ${APP}/hubPaths.json
RUN windmill cache ${APP}/hubPaths.json
RUN rm ${APP}/hubPaths.json
EXPOSE 8000
-8
View File
@@ -1,8 +0,0 @@
export default {
plugins: {
"postcss-import": {},
"tailwindcss/nesting": "postcss-nesting",
tailwindcss: {},
autoprefixer: {},
},
};
-950
View File
@@ -1,950 +0,0 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [],
theme: {
extend: {},
},
plugins: [],
};
const plugin = require("tailwindcss/plugin");
const lightTheme = {
surface: "#ffffff",
surfaceSecondary: "#f3f4f6",
surfaceHover: "#e5e7eb",
surfaceDisabled: "#f9fafb",
surfaceSelected: "#d1d5db",
textPrimary: "#2d3748",
textSecondary: "#4a5568",
textTertiary: "#505c70",
textDisabled: "#a0aec0",
border: "#ddd",
borderHover: "#ccc",
};
const lightThemeRgb = makeRgb(lightTheme);
const darkTheme = {
surface: "#2e3440",
surfaceSecondary: "#3b4252",
surfaceHover: "#454F64",
surfaceDisabled: "#212732",
surfaceSelected: "#434c5e",
textPrimary: "#EEEEEE",
textSecondary: "#C2C9D1",
textTertiary: "#A8AEB7",
textDisabled: "#989DA5",
border: "#3e4c60",
borderHover: "#3e4c60",
};
const darkThemeRgb = makeRgb(darkTheme);
function makeRgb(theme) {
return Object.fromEntries(
Object.entries(theme).map(([key, value]) => {
if (typeof value === "string" && value.startsWith("#")) {
return [key, hexToRgb(value)];
}
return [key, value];
})
);
}
function hexToRgb(hex) {
// Remove '#' symbol from the beginning of the hex value
hex = hex.replace("#", "");
// Convert the hex value to decimal
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
// Return the RGB string format
return `${r} ${g} ${b}`;
}
/** @type {import('tailwindcss').Config} */
const config = {
content: [
"./src/**/*.{html,js,svelte,ts}",
"./node_modules/windmill-components/**/*.{html,js,svelte,ts}",
],
safelist: [
"hljs",
"splitpanes__pane",
"splitpanes__splitter",
"wm-tab",
"autocomplete-list",
"autocomplete-list-item",
"autocomplete-list-item-create",
"selected",
"wm-tab-selected",
...(process.env.NODE_ENV === "production"
? [
{ pattern: /^m(\w?)-.*$/ },
{ pattern: /^p(\w?)-.*$/ },
{ pattern: /^rounded-.*$/ },
{ pattern: /^shadow-.*$/, variants: ["hover"] },
{ pattern: /^text-[^/]*$/, variants: ["hover", "active", "focus"] },
{ pattern: /^bg-[^/]*$/, variants: ["hover", "active", "focus"] },
{ pattern: /^border-[^/]*$/, variants: ["hover", "active", "focus"] },
{ pattern: /^ring-[^/]*$/, variants: ["hover", "active", "focus"] },
]
: []),
],
theme: {
colors: {
current: "currentcolor",
transparent: "transparent",
white: "#ffffff",
black: "#000000",
slate: {
50: "#f8fafc",
100: "#f1f5f9",
200: "#e2e8f0",
300: "#cbd5e1",
400: "#94a3b8",
500: "#64748b",
600: "#475569",
700: "#334155",
800: "#1e293b",
900: "#0f172a",
},
zinc: {
50: "#fafafa",
100: "#f4f4f5",
200: "#e4e4e7",
300: "#d4d4d8",
400: "#a1a1aa",
500: "#71717a",
600: "#52525b",
700: "#3f3f46",
800: "#27272a",
900: "#18181b",
},
neutral: {
50: "#fafafa",
100: "#f5f5f5",
200: "#e5e5e5",
300: "#d4d4d4",
400: "#a3a3a3",
500: "#737373",
600: "#525252",
700: "#404040",
800: "#262626",
900: "#171717",
},
stone: {
50: "#fafaf9",
100: "#f5f5f4",
200: "#e7e5e4",
300: "#d6d3d1",
400: "#a8a29e",
500: "#78716c",
600: "#57534e",
700: "#44403c",
800: "#292524",
900: "#1c1917",
},
orange: {
50: "#fff7ed",
100: "#ffedd5",
200: "#fed7aa",
300: "#fdba74",
400: "#fb923c",
500: "#f97316",
600: "#ea580c",
700: "#c2410c",
800: "#9a3412",
900: "#7c2d12",
},
amber: {
50: "#fffbeb",
100: "#fef3c7",
200: "#fde68a",
300: "#fcd34d",
400: "#fbbf24",
500: "#f59e0b",
600: "#d97706",
700: "#b45309",
800: "#92400e",
900: "#78350f",
},
lime: {
50: "#f7fee7",
100: "#ecfccb",
200: "#d9f99d",
300: "#bef264",
400: "#a3e635",
500: "#84cc16",
600: "#65a30d",
700: "#4d7c0f",
800: "#3f6212",
900: "#365314",
},
emerald: {
50: "#ecfdf5",
100: "#d1fae5",
200: "#a7f3d0",
300: "#6ee7b7",
400: "#34d399",
500: "#10b981",
600: "#059669",
700: "#047857",
800: "#065f46",
900: "#064e3b",
},
teal: {
50: "#f0fdfa",
100: "#ccfbf1",
200: "#99f6e4",
300: "#5eead4",
400: "#2dd4bf",
500: "#14b8a6",
600: "#0d9488",
700: "#0f766e",
800: "#115e59",
900: "#134e4a",
},
cyan: {
50: "#ecfeff",
100: "#cffafe",
200: "#a5f3fc",
300: "#67e8f9",
400: "#22d3ee",
500: "#06b6d4",
600: "#0891b2",
700: "#0e7490",
800: "#155e75",
900: "#164e63",
},
sky: {
50: "#f0f9ff",
100: "#e0f2fe",
200: "#bae6fd",
300: "#7dd3fc",
400: "#38bdf8",
500: "#0ea5e9",
600: "#0284c7",
700: "#0369a1",
800: "#075985",
900: "#0c4a6e",
},
violet: {
50: "#f5f3ff",
100: "#ede9fe",
200: "#ddd6fe",
300: "#c4b5fd",
400: "#a78bfa",
500: "#8b5cf6",
600: "#7c3aed",
700: "#6d28d9",
800: "#5b21b6",
900: "#4c1d95",
},
purple: {
50: "#faf5ff",
100: "#f3e8ff",
200: "#e9d5ff",
300: "#d8b4fe",
400: "#c084fc",
500: "#a855f7",
600: "#9333ea",
700: "#7e22ce",
800: "#6b21a8",
900: "#581c87",
},
fuchsia: {
50: "#fdf4ff",
100: "#fae8ff",
200: "#f5d0fe",
300: "#f0abfc",
400: "#e879f9",
500: "#d946ef",
600: "#c026d3",
700: "#a21caf",
800: "#86198f",
900: "#701a75",
},
pink: {
50: "#fdf2f8",
100: "#fce7f3",
200: "#fbcfe8",
300: "#f9a8d4",
400: "#f472b6",
500: "#ec4899",
600: "#db2777",
700: "#be185d",
800: "#9d174d",
900: "#831843",
},
rose: {
50: "#fff1f2",
100: "#ffe4e6",
200: "#fecdd3",
300: "#fda4af",
400: "#fb7185",
500: "#f43f5e",
600: "#e11d48",
700: "#be123c",
800: "#9f1239",
900: "#881337",
},
gray: {
50: "#f9fafb",
100: "#f3f4f6",
200: "#e5e7eb",
300: "#d1d5db",
400: "#9ca3af",
500: "#6b7280",
600: "#4b5563",
700: "#374151",
800: "#1f2937",
900: "#111827",
},
red: {
50: "#fef2f2",
100: "#fee2e2",
200: "#fecaca",
300: "#fca5a5",
400: "#f87171",
500: "#ef4444",
600: "#dc2626",
700: "#b91c1c",
800: "#991b1b",
900: "#7f1d1d",
},
orange: {
100: "#ffedd5",
200: "#ffedd5",
300: "#FDC089",
400: "#fb923c",
500: "#f97316",
600: "#ea580c",
700: "#c2410c",
800: "#c2410c",
},
yellow: {
50: "#fefce8",
100: "#fef9c3",
200: "#fef08a",
300: "#fde047",
400: "#facc15",
500: "#eab308",
600: "#ca8a04",
700: "#a16207",
800: "#854d0e",
900: "#713f12",
},
green: {
50: "#f0fdf4",
100: "#dcfce7",
200: "#bbf7d0",
300: "#86efac",
400: "#4ade80",
500: "#22c55e",
600: "#16a34a",
700: "#15803d",
800: "#166534",
900: "#14532d",
},
blue: {
50: "#eff6ff",
100: "#dbeafe",
200: "#bfdbfe",
300: "#93c5fd",
400: "#60a5fa",
500: "#3b82f6",
600: "#2563eb",
700: "#1d4ed8",
800: "#1e40af",
900: "#1e3a8a",
},
indigo: {
50: "#eef2ff",
100: "#e0e7ff",
200: "#c7d2fe",
300: "#a5b4fc",
400: "#818cf8",
500: "#6366f1",
600: "#4f46e5",
700: "#4338ca",
800: "#3730a3",
900: "#312e81",
},
frost: {
50: "#f5f7fa",
100: "#eaeef4",
200: "#cfd9e8",
300: "#a6bad3",
400: "#7594bb",
500: "#5e81ac",
600: "#415f88",
700: "#364d6e",
800: "#2f425d",
900: "#2b394f",
950: "#1d2534",
},
marine: {
50: "#E9E9F4",
100: "#859AC7",
200: "#586F9E",
300: "#4A5F8A",
400: "#394A6D",
500: "#323F5B",
},
surface: "rgb(var(--color-surface) / <alpha-value>)",
"surface-secondary":
"rgb(var(--color-surface-secondary) / <alpha-value>)",
"surface-hover": "rgb(var(--color-surface-hover) / <alpha-value>)",
"surface-disabled": "rgb(var(--color-surface-disabled) / <alpha-value>)",
"surface-selected": "rgb(var(--color-surface-selected) / <alpha-value>)",
primary: "rgb(var(--color-text-primary) / <alpha-value>)",
secondary: "rgb(var(--color-text-secondary) / <alpha-value>)",
tertiary: "rgb(var(--color-text-tertiary) / <alpha-value>)",
disabled: "rgb(var(--color-text-disabled) / <alpha-value>)",
"surface-inverse": "rgb(var(--color-surface-inverse) / <alpha-value>)",
"surface-secondary-inverse":
"rgb(var(--color-surface-secondary-inverse) / <alpha-value>)",
"surface-hover-inverse":
"rgb(var(--color-surface-hover-inverse) / <alpha-value>)",
"surface-disabled-inverse":
"rgb(var(--color-surface-disabled-inverse) / <alpha-value>)",
"surface-selected-inverse":
"rgb(var(--color-surface-selected-inverse) / <alpha-value>)",
"primary-inverse":
"rgb(var(--color-text-primary-inverse) / <alpha-value>)",
"secondary-inverse":
"rgb(var(--color-text-secondary-inverse) / <alpha-value>)",
"tertiary-inverse":
"rgb(var(--color-text-tertiary-inverse) / <alpha-value>)",
"disabled-inverse":
"rgb(var(--color-text-disabled-inverse) / <alpha-value>)",
},
fontFamily: {
// add double quotes if there is space in font name
main: ["Inter", "sans-serif"],
mono: [
"ui-monospace",
"SFMono-Regular",
"Menlo",
"Monaco",
"Consolas",
'"Liberation Mono"',
'"Courier New"',
"monospace",
],
},
extend: {
border: {
color: "red",
},
borderRadius: {
component: "0.250rem",
},
maxHeight: {
"1/2": "50vh",
"2/3": "66vh",
"3/4": "75vh",
},
minWidth: {
"1/4": "25%",
"1/3": "33%",
"1/2": "50%",
"2/3": "66%",
"3/4": "75%",
},
minHeight: {
"1/2": "50vh",
},
height: {
"2/3": "66vh",
},
transitionProperty: {
height: "height",
},
fontSize: {
"2xs": "0.7rem",
},
screens: {
fhd: "1900px",
qhd: "2500px",
"4k": "3800px",
},
animation: {
"spin-counter-clockwise": "spin-counter-clockwise 1s linear infinite",
},
keyframes: {
"spin-counter-clockwise": {
to: { transform: "rotate(-360deg)" },
},
},
},
},
plugins: [
require("@tailwindcss/forms"),
require("@tailwindcss/typography"),
// scopedPreflightStyles({
// isolationStrategy: isolateInsideOfContainer("#windmill-app"),
// }),
plugin(({ addBase, addComponents, addUtilities, theme }) => {
addBase({
fontFamily: theme("fontFamily.main"),
fontSize: theme("fontSize.base"),
fontWeight: theme("fontWeight.normal"),
backgroundColor: "rgb(var(--color-surface) / <alpha-value>)",
color: lightTheme.textPrimary,
"--color-surface": lightThemeRgb.surface,
"--color-surface-secondary": lightThemeRgb.surfaceSecondary,
"--color-surface-hover": lightThemeRgb.surfaceHover,
"--color-surface-disabled": lightThemeRgb.surfaceDisabled,
"--color-surface-selected": lightThemeRgb.surfaceSelected,
"--color-text-primary": lightThemeRgb.textPrimary,
"--color-text-secondary": lightThemeRgb.textSecondary,
"--color-text-tertiary": lightThemeRgb.textTertiary,
"--color-text-disabled": lightThemeRgb.textDisabled,
"--color-surface-inverse": darkThemeRgb.surface,
"--color-surface-secondary-inverse": darkThemeRgb.surfaceSecondary,
"--color-surface-hover-inverse": darkThemeRgb.surfaceHover,
"--color-surface-disabled-inverse": darkThemeRgb.surfaceDisabled,
"--color-surface-selected-inverse": darkThemeRgb.surfaceSelected,
"--color-text-primary-inverse": darkThemeRgb.textPrimary,
"--color-text-secondary-inverse": darkThemeRgb.textSecondary,
"--color-text-tertiary-inverse": darkThemeRgb.textTertiary,
"--color-text-disabled-inverse": darkThemeRgb.textDisabled,
"--color-border": lightThemeRgb.border,
"--color-border-hover": lightThemeRgb.borderHover,
"--vscode-editorSuggestWidget-background": "#f3f3f3",
"--vscode-editorHoverWidget-foreground": "#616161",
"--vscode-editorHoverWidget-border": "#c8c8c8",
"--vscode-editorHoverWidget-statusBarBackground": "#e7e7e7",
"--vscode-editorSuggestWidget-foreground": "#eeffff",
"--vscode-editorSuggestWidget-highlightForeground": "#80cbc4",
"--vscode-editorSuggestWidget-selectedBackground":
"rgba(0, 0, 0, 0.31)",
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: theme("fontSize.lg"),
},
"&.dark": {
backgroundColor: darkTheme.surface,
color: darkTheme.textPrimary,
"--color-surface": darkThemeRgb.surface,
"--color-surface-secondary": darkThemeRgb.surfaceSecondary,
"--color-surface-hover": darkThemeRgb.surfaceHover,
"--color-surface-disabled": darkThemeRgb.surfaceDisabled,
"--color-surface-selected": darkThemeRgb.surfaceSelected,
"--color-text-primary": darkThemeRgb.textPrimary,
"--color-text-secondary": darkThemeRgb.textSecondary,
"--color-text-tertiary": darkThemeRgb.textTertiary,
"--color-text-disabled": darkThemeRgb.textDisabled,
"--color-surface-inverse": lightThemeRgb.surface,
"--color-surface-secondary-inverse": lightThemeRgb.surfaceSecondary,
"--color-surface-hover-inverse": lightThemeRgb.surfaceHover,
"--color-surface-disabled-inverse": lightThemeRgb.surfaceDisabled,
"--color-surface-selected-inverse": lightThemeRgb.surfaceSelected,
"--color-text-primary-inverse": lightThemeRgb.textPrimary,
"--color-text-secondary-inverse": lightThemeRgb.textSecondary,
"--color-text-tertiary-inverse": lightThemeRgb.textTertiary,
"--color-text-disabled-inverse": lightThemeRgb.textDisabled,
"--color-border": darkThemeRgb.border,
"--color-border-hover": darkThemeRgb.borderHover,
"--vscode-editorSuggestWidget-background": "#252526",
"--vscode-editorHoverWidget-foreground": "#cccccc",
"--vscode-editorHoverWidget-border": "#454545",
"--vscode-editorHoverWidget-statusBarBackground": "#2c2c2d",
},
h1: {
fontSize: "24px",
fontWeight: theme("fontWeight.extrabold"),
lineHeight: "1.05",
[`@media (min-width: ${theme("screens.lg")})`]: {
fontSize: "26px",
},
[`@media (min-width: ${theme("screens.fhd")})`]: {
fontSize: "29px",
},
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "34px",
},
},
h2: {
fontSize: "20px",
fontWeight: theme("fontWeight.extrabold"),
lineHeight: "1.1",
[`@media (min-width: ${theme("screens.fhd")})`]: {
fontSize: "22px",
},
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "25px",
},
},
mark: {
backgroundColor: theme("colors.yellow.200"),
borderRadius: theme("borderRadius.sm"),
},
h3: {
fontSize: "18px",
fontWeight: theme("fontWeight.bold"),
lineHeight: "1.2",
[`@media (min-width: ${theme("screens.fhd")})`]: {
fontSize: "20px",
},
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "22px",
},
},
h4: {
fontSize: "18px",
fontWeight: theme("fontWeight.semibold"),
lineHeight: "1.3",
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "20px",
},
},
h5: {
fontSize: "16px",
fontWeight: theme("fontWeight.semibold"),
lineHeight: "1.5",
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "18px",
},
},
h6: {
fontSize: "16px",
fontWeight: theme("fontWeight.medium"),
lineHeight: "1.5",
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "18px",
},
},
button: {
fontWeight: theme("fontWeight.semibold"),
},
a: {
color: theme("colors.blue.500"),
},
".dark input::placeholder": {
color: theme("colors.gray.400"),
},
"[type='checkbox']:checked": {
backgroundImage: `url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");`,
},
".dark [type='checkbox']:checked": {
backgroundImage: `url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");`,
},
'input:not(.windmillapp),input[type="text"]:not(.windmillapp),input[type="email"]:not(.windmillapp),input[type="url"]:not(.windmillapp),input[type="password"]:not(.windmillapp),input[type="number"]:not(.windmillapp),input[type="date"]:not(.windmillapp),input[type="datetime-local"]:not(.windmillapp),input[type="month"]:not(.windmillapp),input[type="search"]:not(.windmillapp),input[type="tel"]:not(.windmillapp),input[type="time"]:not(.windmillapp),input[type="week"]:not(.windmillapp),textarea:not(.windmillapp):not(.monaco-mouse-cursor-text),select:not(.windmillapp)':
{
display: "block",
fontSize: theme("fontSize.sm"),
boxShadow: theme("boxShadow.sm"),
width: "100%",
padding: `${theme("spacing.1")} ${theme("spacing.2")}`,
border: `1px solid ${theme("colors.gray.300")}`,
borderRadius: theme("borderRadius.md"),
"&:focus": {
"--tw-ring-color": theme("colors.frost.100"),
"--tw-ring-offset-shadow":
"var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)",
"--tw-ring-shadow":
"var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)",
boxShadow:
"var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000)",
},
"&:disabled,[disabled]": {
backgroundColor: theme("colors.gray.100") + " !important",
".dark &": {
backgroundColor: theme("colors.gray.700") + " !important",
},
},
},
'.dark input:not(.windmillapp),.dark input[type="text"]:not(.windmillapp),.dark input[type="email"]:not(.windmillapp),.dark input[type="url"]:not(.windmillapp),.dark input[type="password"]:not(.windmillapp),.dark input[type="number"]:not(.windmillapp),.dark input[type="date"]:not(.windmillapp),.dark input[type="datetime-local"]:not(.windmillapp),.dark input[type="month"]:not(.windmillapp),.dark input[type="search"]:not(.windmillapp),.dark input[type="tel"]:not(.windmillapp),.dark input[type="time"]:not(.windmillapp),.dark input[type="week"]:not(.windmillapp),.dark textarea:not(.windmillapp):not(.monaco-mouse-cursor-text),.dark select:not(.windmillapp)':
{
backgroundColor: theme("colors.gray.700"),
color: theme("colors.gray.200"),
borderColor: theme("colors.gray.600"),
"&:focus": {
"--tw-ring-color": theme("colors.frost.700"),
},
},
"button:disabled,button[disabled=true],a:disabled,a[disabled=true]": {
pointerEvents: "none",
cursor: "default",
opacity: "0.90",
},
"pre code.hljs": {
padding: "0px !important",
fontFamily: theme("fontFamily.mono"),
fontSize: theme("fontSize.xs") + " !important",
lineHeight: theme("lineHeight.4") + " !important",
},
".h1-textarea": {
fontSize: "24px !important",
fontWeight: `${theme("fontWeight.extrabold")} !important`,
lineHeight: "1.05 !important",
color: theme("!colors.gray.800"),
[`@media (min-width: ${theme("screens.lg")})`]: {
fontSize: "26px !important",
},
[`@media (min-width: ${theme("screens.fhd")})`]: {
fontSize: "29px !important",
},
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "34px !important",
},
},
".h3-textarea": {
fontSize: "18px !important",
fontWeight: `${theme("fontWeight.bold")} !important`,
lineHeight: "1.2 !important",
color: theme("!colors.gray.600"),
[`@media (min-width: ${theme("screens.fhd")})`]: {
fontSize: "20px !important",
},
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "22px !important",
},
},
".p-textarea": {
fontSize: "16px !important",
fontWeight: `${theme("fontWeight.normal")} !important`,
lineHeight: "1.5 !important",
color: theme("!colors.gray.600"),
[`@media (min-width: ${theme("screens.fhd")})`]: {
fontSize: "18px !important",
},
[`@media (min-width: ${theme("screens.qhd")})`]: {
fontSize: "20px !important",
},
},
input: {
backgroundColor: theme("colors.surface-secondary") + " !important",
},
textarea: {
backgroundColor: theme("colors.surface-secondary") + " !important",
},
});
addComponents({
".table-custom": {
"& th": {
paddingTop: theme("spacing.3"),
paddingRight: theme("spacing.1"),
paddingLeft: theme("spacing.1"),
paddingBottom: theme("spacing.3"),
fontSize: theme("fontSize.sm"),
textAlign: "left",
fontWeight: theme("fontWeight.semibold"),
color: theme("colors.gray.900"),
textTransform: "capitalize",
},
".dark & th": {
color: theme("colors.gray.200"),
},
"& td": {
paddingLeft: theme("spacing.1"),
paddingRight: theme("spacing.1"),
paddingTop: theme("spacing.2"),
paddingBottom: theme("spacing.2"),
fontSize: theme("fontSize.sm"),
color: theme("colors.gray.700"),
},
".dark & td ": {
color: theme("colors.gray.200"),
},
"& tbody > :not([hidden]) ~ :not([hidden])": {
borderTop: `1px solid ${theme("colors.gray.200")}`,
},
".dark & tbody > :not([hidden]) ~ :not([hidden])": {
borderTop: `1px solid ${theme("colors.gray.700")}`,
},
"& tbody > tr:hover": {
backgroundColor: theme("colors.gray.50"),
},
".dark & tbody > tr:hover": {
backgroundColor: theme("colors.gray.800"),
},
},
".commit-hash": {
fontSize: theme("fontSize.2xs"),
color: theme("colors.gray.500"),
backgroundColor: theme("colors.gray.200"),
fontFamily: theme("fontFamily.mono"),
},
".input-error": {
borderColor: `${theme("colors.red.500")} !important`,
},
".box": {
borderWidth: "1px",
borderRadius: theme("borderRadius.sm"),
boxShadow: theme("boxShadow.sm"),
padding: theme("spacing.4"),
},
".animate-skeleton": {
animation: theme("animation.pulse"),
backgroundColor: theme("colors.blue.100"),
borderRadius: theme("borderRadius.DEFAULT"),
},
".text-black-gradient": {
color: "transparent",
backgroundClip: "text",
backgroundImage: `linear-gradient(to right, ${theme(
"colors.black"
)}, ${theme("colors.gray.600")})`,
},
".splitpanes__pane": {
backgroundColor: lightTheme.surface + " !important",
overflow: "auto !important",
},
".dark .splitpanes__pane": {
backgroundColor: darkTheme.surface + " !important",
overflow: "auto !important",
},
".splitpanes__splitter": {
backgroundColor: lightTheme.border + " !important",
margin: "0 !important",
border: "none !important",
"&::after": {
backgroundColor: lightTheme.border + " !important",
margin: "0 !important",
transform: "none !important",
transition: "opacity 200ms !important",
opacity: "0",
"--splitter-hover-size": "5px",
"--splitter-hover-adjustment": "-2px",
},
"&:hover::after": {
opacity: "1",
zIndex: "1001 !important",
},
},
".dark .splitpanes__splitter": {
backgroundColor: darkTheme.border + " !important",
"&::after": {
backgroundColor: darkTheme.border + " !important",
},
},
".splitpanes--vertical>.splitpanes__splitter": {
width: "1px !important",
"&::before": {
left: "1px !important",
width: "0px !important",
marginLeft: "0 !important",
},
"&::after": {
top: "0 !important",
height: "100% !important",
left: "var(--splitter-hover-adjustment) !important",
width: "var(--splitter-hover-size) !important",
},
},
".splitpanes--horizontal>.splitpanes__splitter": {
height: "1px !important",
"&::before": {
top: "1px !important",
height: "0px !important",
marginTop: "0 !important",
},
"&::after": {
top: "var(--splitter-hover-adjustment) !important",
height: "var(--splitter-hover-size) !important",
left: "0 !important",
width: "100% !important",
},
},
// Windmill Tab classes
".wm-tab-active": {
borderColor: darkTheme.border,
color: lightTheme.textPrimary,
},
".dark .wm-tab-active": {
borderColor: lightTheme.border,
color: darkTheme.textPrimary,
},
});
addUtilities({
".separator": {
backgroundColor: `${lightTheme.border} !important`,
},
".dark .separator": {
backgroundColor: `${darkTheme.border} !important`,
},
".center-center": {
display: "flex",
justifyContent: "center",
alignItems: "center",
},
".inner-border": {
boxShadow: `inset 0 0 0 1px ${lightTheme.border}`,
},
".dark .inner-border": {
boxShadow: `inset 0 0 0 1px ${darkTheme.border}`,
},
".z5000": {
zIndex: "5000 !important",
},
".ellipsize": {
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
},
/** Set the '-webkit-line-clamp' property to the desired number of lines.
*
* Eg.: `class="ellipsize-multi-line [-webkit-line-clamp:3]"`
*/
".ellipsize-multi-line": {
display: "-webkit-box",
"-webkit-box-orient": "vertical",
overflow: "hidden",
},
".disabled": {
pointerEvents: "none",
cursor: "default",
filter: "grayscale(1)",
},
".scrollbar-hidden": {
"-ms-overflow-style": "none",
"scrollbar-width": "none",
"&::-webkit-scrollbar": {
display: "none",
width: "0px",
},
},
});
}),
],
darkMode: "class",
important: ".windmill-app",
};
module.exports = config;
@@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "SELECT coalesce(COUNT(*) FILTER(WHERE suspend = 0 AND running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE suspend > 0), 0) as \"suspended!\" FROM queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now()",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "database_length!",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "suspended!",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool"
]
},
"nullable": [
null,
null
]
},
"hash": "28a878c59b6d52f42d315eabb34c96133b69542b01232295c26cc9e093c372f9"
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "bool",
"name": "?column?",
"type_info": "Bool"
}
],
@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now() AND running = false",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "database_length!",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool"
]
},
"nullable": [
null
]
},
"hash": "8481d1bd91b7d23f946b4cf0d312acfc0ed4973309b4860e93882a37a21a0bd0"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at > now() RETURNING path",
"query": "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at < now() RETURNING path",
"describe": {
"columns": [
{
@@ -16,5 +16,5 @@
false
]
},
"hash": "bac12eff366f6b76f97d1a44041e0c1b5fdcb2c4205c1c1c0026b71ce404f089"
"hash": "b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a"
}
@@ -18,8 +18,8 @@
"Left": []
},
"nullable": [
false,
true
true,
false
]
},
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
@@ -1,12 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM completed_job WHERE workspace_id = $1",
"query": "SELECT coalesce(COUNT(*), 0) as \"database_length!\", null::bigint as suspended FROM completed_job WHERE workspace_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "database_length!",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "suspended",
"type_info": "Int8"
}
],
"parameters": {
@@ -15,8 +20,9 @@
]
},
"nullable": [
null,
null
]
},
"hash": "f5681df5935ec753f4a6d9ab8b927bbf890f0b4fe2de16dc764b24f24d7bbdca"
"hash": "bf04a017bc55f6e1f5f25f697b3c4828611591c76dd32541735095c010d6cdf2"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, current_job_id = NULL, current_job_workspace_id = NULL WHERE worker = $6",
"query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, current_job_id = NULL, current_job_workspace_id = NULL, vcpus = COALESCE($7, vcpus), memory = COALESCE($8, memory) WHERE worker = $6",
"describe": {
"columns": [],
"parameters": {
@@ -10,10 +10,12 @@
"Float4",
"Int8",
"Int8",
"Text"
"Text",
"Int8",
"Int8"
]
},
"nullable": []
},
"hash": "4398bc6f701dcba647a259ec2d091a70938a3ecd7ff8a89277cc35d119e2bad4"
"hash": "c1de24c68933ab35934395d74c5d2747b3da80c96e255ccf0c4e053206a9ad1b"
}
+139 -131
View File
@@ -377,7 +377,7 @@ dependencies = [
"arrow-schema",
"chrono",
"half",
"indexmap 2.3.0",
"indexmap 2.4.0",
"lexical-core",
"num",
"serde",
@@ -845,9 +845,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
version = "1.37.0"
version = "1.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1074e818fbe4f9169242d78448b15be8916a79daa38ea1231f2e2e10d993fcd2"
checksum = "fca5e0b9fb285638f1007e9d961d963b9e504ab968fe5a3807cce94070bd0ce3"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -867,9 +867,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
version = "1.38.0"
version = "1.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29755c51e33fa3f678598f64324a169cf4b7d3c4865d2709d4308f53366a92a4"
checksum = "bc3e48ec239bb734db029ceef83599f4c9b3ce5d25c961b5bcd3f031c15bed54"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -889,9 +889,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
version = "1.37.0"
version = "1.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e52dc3fd7dfa6c01a69cf3903e00aa467261639138a05b06cd92314d2c8fb07"
checksum = "ede095dfcc5c92b224813c24a82b65005a475c98d737e2726a898cf583e2e8bd"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -985,9 +985,9 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime"
version = "1.6.2"
version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce87155eba55e11768b8c1afa607f3e864ae82f03caf63258b37455b0ad02537"
checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225"
dependencies = [
"aws-smithy-async",
"aws-smithy-http",
@@ -1029,9 +1029,9 @@ dependencies = [
[[package]]
name = "aws-smithy-types"
version = "1.2.0"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe321a6b21f5d8eabd0ade9c55d3d0335f3c3157fc2b3e87f05f34b539e4df5"
checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8"
dependencies = [
"base64-simd 0.8.0",
"bytes",
@@ -1105,7 +1105,7 @@ dependencies = [
"serde_urlencoded",
"sync_wrapper 1.0.1",
"tokio",
"tower",
"tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
@@ -1483,18 +1483,18 @@ dependencies = [
[[package]]
name = "bytemuck"
version = "1.16.3"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83"
checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.7.0"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b"
checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
dependencies = [
"proc-macro2",
"quote",
@@ -1613,12 +1613,13 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.10"
version = "1.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
dependencies = [
"jobserver",
"libc",
"shlex",
]
[[package]]
@@ -1717,9 +1718,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.15"
version = "4.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc"
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1757,9 +1758,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "cmake"
version = "0.1.50"
version = "0.1.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a"
dependencies = [
"cc",
]
@@ -1952,9 +1953,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.12"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
dependencies = [
"libc",
]
@@ -2264,7 +2265,7 @@ dependencies = [
"glob",
"half",
"hashbrown 0.14.5",
"indexmap 2.3.0",
"indexmap 2.4.0",
"itertools 0.12.1",
"log",
"num_cpus",
@@ -2433,7 +2434,7 @@ dependencies = [
"datafusion-expr",
"datafusion-physical-expr",
"hashbrown 0.14.5",
"indexmap 2.3.0",
"indexmap 2.4.0",
"itertools 0.12.1",
"log",
"regex-syntax 0.8.4",
@@ -2462,7 +2463,7 @@ dependencies = [
"half",
"hashbrown 0.14.5",
"hex",
"indexmap 2.3.0",
"indexmap 2.4.0",
"itertools 0.12.1",
"log",
"paste",
@@ -2506,7 +2507,7 @@ dependencies = [
"futures",
"half",
"hashbrown 0.14.5",
"indexmap 2.3.0",
"indexmap 2.4.0",
"itertools 0.12.1",
"log",
"once_cell",
@@ -2664,7 +2665,7 @@ dependencies = [
"tokio-rustls 0.26.0",
"tokio-socks",
"tokio-util",
"tower",
"tower 0.4.13",
"tower-http",
"tower-service",
]
@@ -3884,7 +3885,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.3.0",
"indexmap 2.4.0",
"slab",
"tokio",
"tokio-util",
@@ -3903,7 +3904,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.1.0",
"indexmap 2.3.0",
"indexmap 2.4.0",
"slab",
"tokio",
"tokio-util",
@@ -4284,7 +4285,7 @@ dependencies = [
"pin-project-lite",
"socket2 0.5.7",
"tokio",
"tower",
"tower 0.4.13",
"tower-service",
"tracing",
]
@@ -4357,9 +4358,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
@@ -4512,9 +4513,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.69"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
dependencies = [
"wasm-bindgen",
]
@@ -4539,7 +4540,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee7893dab2e44ae5f9d0173f26ff4aa327c10b01b06a72b52dd9405b628640d"
dependencies = [
"indexmap 2.3.0",
"indexmap 2.4.0",
]
[[package]]
@@ -4659,9 +4660,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.155"
version = "0.2.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a"
[[package]]
name = "libgit2-sys"
@@ -4726,9 +4727,9 @@ dependencies = [
[[package]]
name = "libz-sys"
version = "1.1.18"
version = "1.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647"
dependencies = [
"cc",
"libc",
@@ -5053,6 +5054,16 @@ dependencies = [
"unicase",
]
[[package]]
name = "minicov"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169"
dependencies = [
"cc",
"walkdir",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -5068,18 +5079,6 @@ dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
[[package]]
name = "mio"
version = "1.0.2"
@@ -5156,9 +5155,9 @@ dependencies = [
[[package]]
name = "mysql_async"
version = "0.34.1"
version = "0.34.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbfe87d7e35cb72363326216cc1712b865d8d4f70abf3b2d2e6b251fb6b2f427"
checksum = "a0b66e411c31265e879d9814d03721f2daa7ad07337b6308cb4bb0cde7e6fd47"
dependencies = [
"bytes",
"crossbeam",
@@ -5167,12 +5166,9 @@ dependencies = [
"futures-sink",
"futures-util",
"keyed_priority_queue",
"lazy_static",
"lru",
"mio 0.8.11",
"mysql_common",
"native-tls",
"once_cell",
"pem 3.0.4",
"percent-encoding",
"pin-project",
@@ -5819,7 +5815,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
"indexmap 2.3.0",
"indexmap 2.4.0",
]
[[package]]
@@ -7470,9 +7466,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
[[package]]
name = "serde"
version = "1.0.206"
version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
dependencies = [
"serde_derive",
]
@@ -7511,9 +7507,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.206"
version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
dependencies = [
"proc-macro2",
"quote",
@@ -7533,11 +7529,11 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.124"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
dependencies = [
"indexmap 2.3.0",
"indexmap 2.4.0",
"itoa",
"memchr",
"ryu",
@@ -7596,9 +7592,9 @@ dependencies = [
[[package]]
name = "serde_tokenstream"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8790a7c3fe883e443eaa2af6f705952bc5d6e8671a220b9335c8cae92c037e74"
checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1"
dependencies = [
"proc-macro2",
"quote",
@@ -7641,7 +7637,7 @@ dependencies = [
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.3.0",
"indexmap 2.4.0",
"serde",
"serde_derive",
"serde_json",
@@ -7667,7 +7663,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.3.0",
"indexmap 2.4.0",
"itoa",
"ryu",
"serde",
@@ -8047,7 +8043,7 @@ dependencies = [
"hashbrown 0.14.5",
"hashlink",
"hex",
"indexmap 2.3.0",
"indexmap 2.4.0",
"log",
"memchr",
"once_cell",
@@ -8424,7 +8420,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b67e115ab136fe0eb03558bb0508ca7782eeb446a96d165508c48617e3fd94"
dependencies = [
"anyhow",
"indexmap 2.3.0",
"indexmap 2.4.0",
"serde",
"serde_json",
"swc_cached",
@@ -8575,7 +8571,7 @@ checksum = "d37dc505c92af56d0f77cf6f31a6ccd37ac40cad1e01ff77277e0b1c70e8f8ff"
dependencies = [
"better_scoped_tls",
"bitflags 2.6.0",
"indexmap 2.3.0",
"indexmap 2.4.0",
"once_cell",
"phf",
"rustc-hash 1.1.0",
@@ -8644,7 +8640,7 @@ checksum = "446da32cac8299973aaf1d37496562bfd0c1e4f3c3ab5d0af6f07f42e8184102"
dependencies = [
"base64 0.21.7",
"dashmap",
"indexmap 2.3.0",
"indexmap 2.4.0",
"once_cell",
"serde",
"sha1",
@@ -8683,7 +8679,7 @@ version = "0.130.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e62b199454a576c5fdbd7e1bef8ab88a395427456d8a713d994b7d469833aa"
dependencies = [
"indexmap 2.3.0",
"indexmap 2.4.0",
"num_cpus",
"once_cell",
"rustc-hash 1.1.0",
@@ -9273,7 +9269,7 @@ dependencies = [
"backtrace",
"bytes",
"libc",
"mio 1.0.2",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
@@ -9442,7 +9438,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.3.0",
"indexmap 2.4.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -9455,7 +9451,7 @@ version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
"indexmap 2.3.0",
"indexmap 2.4.0",
"toml_datetime",
"winnow 0.5.40",
]
@@ -9466,7 +9462,7 @@ version = "0.22.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
dependencies = [
"indexmap 2.3.0",
"indexmap 2.4.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -9502,6 +9498,16 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7"
dependencies = [
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-cookies"
version = "0.10.0"
@@ -9542,15 +9548,15 @@ dependencies = [
[[package]]
name = "tower-layer"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
@@ -10181,19 +10187,20 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
@@ -10206,9 +10213,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
dependencies = [
"cfg-if",
"js-sys",
@@ -10218,9 +10225,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -10228,9 +10235,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
@@ -10241,18 +10248,19 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.42"
version = "0.3.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b"
checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9"
dependencies = [
"console_error_panic_hook",
"js-sys",
"minicov",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -10261,9 +10269,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.42"
version = "0.3.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021"
dependencies = [
"proc-macro2",
"quote",
@@ -10294,9 +10302,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.69"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -10401,7 +10409,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"axum",
@@ -10441,7 +10449,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"argon2",
@@ -10470,7 +10478,7 @@ dependencies = [
"hmac",
"http 1.1.0",
"hyper 1.4.1",
"itertools 0.10.5",
"itertools 0.13.0",
"jsonwebtoken",
"lazy_static",
"magic-crypt",
@@ -10504,7 +10512,7 @@ dependencies = [
"tokio-native-tls",
"tokio-tar",
"tokio-util",
"tower",
"tower 0.5.0",
"tower-cookies",
"tower-http",
"tracing",
@@ -10525,7 +10533,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"base64 0.21.7",
"chrono",
@@ -10543,7 +10551,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"chrono",
"serde",
@@ -10556,7 +10564,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"async-stream",
@@ -10571,8 +10579,8 @@ dependencies = [
"hex",
"hmac",
"hyper 1.4.1",
"indexmap 2.3.0",
"itertools 0.10.5",
"indexmap 2.4.0",
"itertools 0.13.0",
"lazy_static",
"magic-crypt",
"mail-send",
@@ -10597,7 +10605,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"regex",
"rsmq_async",
@@ -10612,7 +10620,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"bytes",
@@ -10633,7 +10641,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"serde",
"serde_json",
@@ -10641,7 +10649,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10652,11 +10660,11 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"gosyn",
"itertools 0.10.5",
"itertools 0.13.0",
"lazy_static",
"regex",
"windmill-parser",
@@ -10664,7 +10672,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10675,11 +10683,11 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
"itertools 0.10.5",
"itertools 0.13.0",
"lazy_static",
"php-parser-rs",
"regex",
@@ -10689,10 +10697,10 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"itertools 0.10.5",
"itertools 0.13.0",
"rustpython-parser",
"serde_json",
"windmill-parser",
@@ -10700,11 +10708,11 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"async-recursion",
"itertools 0.10.5",
"itertools 0.13.0",
"lazy_static",
"phf",
"regex",
@@ -10717,7 +10725,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10728,7 +10736,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -10747,7 +10755,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"getrandom 0.2.15",
@@ -10766,7 +10774,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -10778,7 +10786,7 @@ dependencies = [
"futures-core",
"hex",
"hmac",
"itertools 0.10.5",
"itertools 0.13.0",
"lazy_static",
"prometheus",
"regex",
@@ -10799,7 +10807,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -10809,7 +10817,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.377.1"
version = "1.381.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -10834,7 +10842,7 @@ dependencies = [
"gcp_auth",
"git-version",
"hex",
"itertools 0.10.5",
"itertools 0.13.0",
"jsonwebtoken",
"lazy_static",
"mappable-rc",
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.377.1"
version = "1.381.0"
authors.workspace = true
edition.workspace = true
@@ -25,7 +25,7 @@ members = [
]
[workspace.package]
version = "1.377.1"
version = "1.381.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
+1 -1
View File
@@ -1 +1 @@
80d9e507977b55d0b76272905c969c3d6e830083
7cf76a8c6694b59f4615b0c8fa61fb02411326a7
+35 -4
View File
@@ -6,15 +6,20 @@
* LICENSE-AGPL for a copy of the license.
*/
use anyhow::Context;
use gethostname::gethostname;
use git_version::git_version;
use rand::Rng;
use sqlx::{postgres::PgListener, Pool, Postgres};
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr, SocketAddr},
time::Duration,
};
use tokio::fs::DirBuilder;
use tokio::{
fs::{create_dir_all, DirBuilder, File},
io::AsyncReadExt,
};
use windmill_api::HTTP_CLIENT;
#[cfg(feature = "enterprise")]
@@ -54,9 +59,10 @@ use windmill_common::METRICS_ADDR;
use windmill_common::global_settings::OBJECT_STORE_CACHE_CONFIG_SETTING;
use windmill_worker::{
BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, BUN_DEPSTAR_CACHE_DIR, DENO_CACHE_DIR,
DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM, GO_BIN_CACHE_DIR, GO_CACHE_DIR, HUB_CACHE_DIR,
LOCK_CACHE_DIR, PIP_CACHE_DIR, POWERSHELL_CACHE_DIR, TAR_PIP_CACHE_DIR, TMP_LOGS_DIR,
get_hub_script_content_and_requirements, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR,
BUN_DEPSTAR_CACHE_DIR, DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM,
GO_BIN_CACHE_DIR, GO_CACHE_DIR, HUB_CACHE_DIR, LOCK_CACHE_DIR, PIP_CACHE_DIR,
POWERSHELL_CACHE_DIR, TAR_PIP_CACHE_DIR, TMP_LOGS_DIR,
};
use crate::monitor::{
@@ -111,6 +117,28 @@ pub fn main() -> anyhow::Result<()> {
create_and_run_current_thread_inner(windmill_main())
}
async fn cache_hub_scripts(file_path: Option<String>) -> anyhow::Result<()> {
let file_path = file_path.unwrap_or("./hubPaths.json".to_string());
let mut file = File::open(&file_path)
.await
.with_context(|| format!("Could not open {}, make sure it exists", &file_path))?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
let paths = serde_json::from_str::<HashMap<String, String>>(&contents).with_context(|| {
format!(
"Could not parse {}, make sure it is a valid JSON object with string keys and values",
&file_path
)
})?;
create_dir_all(HUB_CACHE_DIR).await?;
for path in paths.values() {
get_hub_script_content_and_requirements(Some(path.to_string()), None).await?;
}
Ok(())
}
async fn windmill_main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
@@ -141,6 +169,9 @@ async fn windmill_main() -> anyhow::Result<()> {
{
tracing::warn!("Embeddings are not enabled, ignoring...");
}
cache_hub_scripts(std::env::args().nth(2)).await?;
return Ok(());
}
"-v" | "--version" | "version" => {
+1 -1
View File
@@ -407,7 +407,7 @@ pub async fn delete_expired_items(db: &DB) -> () {
}
let deleted_expired_variables = sqlx::query_scalar!(
"DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at > now() RETURNING path",
"DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at < now() RETURNING path",
)
.fetch_all(db)
.await;
+2 -2
View File
@@ -372,7 +372,7 @@ mod suspend_resume {
server.close().await.unwrap();
assert_eq!(
json!( {"error": {"name": "SuspendedDisapproved", "message": "Disapproved by Some(\"ruben\")"}}),
json!( {"error": {"name": "SuspendedDisapproved", "message": "Disapproved by ruben"}}),
result
);
}
@@ -433,7 +433,7 @@ mod suspend_resume {
let result = completed_job(flow, &db).await.json_result().unwrap();
assert_eq!(
json!( {"error": {"name": "SuspendedDisapproved", "message": "Disapproved by None"}}),
json!( {"error": {"name": "SuspendedDisapproved", "message": "Disapproved by unknown"}}),
result
);
}
+4 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.377.1
version: 1.381.0
title: Windmill API
contact:
@@ -5631,6 +5631,8 @@ paths:
properties:
database_length:
type: integer
suspended:
type: integer
required:
- database_length
@@ -5812,6 +5814,7 @@ paths:
- $ref: "#/components/parameters/CreatedOrStartedAfter"
- $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob"
- $ref: "#/components/parameters/JobKinds"
- $ref: "#/components/parameters/Suspended"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/Tag"
- $ref: "#/components/parameters/ResultFilter"
+4 -19
View File
@@ -29,7 +29,6 @@ use sha2::{Digest, Sha256};
use sql_builder::{bind::Bind, SqlBuilder};
use sqlx::{types::Uuid, FromRow};
use std::str;
use tower_http::cors::{Any, CorsLayer};
use windmill_audit::audit_ee::audit_log;
use windmill_audit::ActionKind;
use windmill_common::{
@@ -66,24 +65,10 @@ pub fn workspaced_service() -> Router {
}
pub fn unauthed_service() -> Router {
let cors = CorsLayer::new()
.allow_methods([http::Method::GET])
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
.allow_origin(Any);
Router::new()
.route(
"/execute_component/*path",
post(execute_component).layer(cors.clone()),
)
.route(
"/public_app/:secret",
get(get_public_app_by_secret).layer(cors.clone()),
)
.route(
"/public_resource/*path",
get(get_public_resource).layer(cors.clone()),
)
.route("/execute_component/*path", post(execute_component))
.route("/public_app/:secret", get(get_public_app_by_secret))
.route("/public_resource/*path", get(get_public_resource))
}
pub fn global_service() -> Router {
@@ -720,7 +705,7 @@ pub async fn get_hub_app_by_id(
&format!("{}/apps/{}/json", *HUB_BASE_URL.read().await, id),
false,
None,
&db,
Some(&db),
)
.await?
.json()
+4 -4
View File
@@ -313,7 +313,7 @@ impl EmbeddingsDb {
&format!("{}/scripts/embeddings", hub_base_url),
false,
None,
pg_db,
Some(pg_db),
)
.await?
} else {
@@ -326,7 +326,7 @@ impl EmbeddingsDb {
&format!("{}/scripts/embeddings", hub_base_url),
false,
None,
pg_db,
Some(pg_db),
)
.await?
}
@@ -375,7 +375,7 @@ impl EmbeddingsDb {
&format!("{}/resource_types/embeddings", hub_base_url),
false,
None,
pg_db,
Some(pg_db),
)
.await?
} else {
@@ -388,7 +388,7 @@ impl EmbeddingsDb {
&format!("{}/resource_types/embeddings", hub_base_url),
false,
None,
pg_db,
Some(pg_db),
)
.await?
}
+1 -1
View File
@@ -230,7 +230,7 @@ pub async fn get_hub_flow_by_id(
&format!("{}/flows/{}/json", *HUB_BASE_URL.read().await, id),
false,
None,
&db,
Some(&db),
)
.await?
.json()
+3 -3
View File
@@ -1462,7 +1462,6 @@ async fn cancel_selection(
Path(w_id): Path<String>,
Json(jobs): Json<Vec<Uuid>>,
) -> error::JsonResult<Vec<Uuid>> {
let mut tx = user_db.begin(&authed).await?;
let jobs_to_cancel = sqlx::query_scalar!(
"SELECT id FROM queue WHERE id = ANY($1) AND schedule_path IS NULL",
@@ -1508,6 +1507,7 @@ async fn list_filtered_uuids(
#[derive(Serialize, Debug, FromRow)]
struct QueueStats {
database_length: i64,
suspended: Option<i64>,
}
#[derive(Deserialize)]
@@ -1523,7 +1523,7 @@ async fn count_queue_jobs(
Ok(Json(
sqlx::query_as!(
QueueStats,
"SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now() AND running = false",
"SELECT coalesce(COUNT(*) FILTER(WHERE suspend = 0 AND running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE suspend > 0), 0) as \"suspended!\" FROM queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now()",
w_id,
w_id == "admins" && cq.all_workspaces.unwrap_or(false),
)
@@ -1539,7 +1539,7 @@ async fn count_completed_jobs(
Ok(Json(
sqlx::query_as!(
QueueStats,
"SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM completed_job WHERE workspace_id = $1",
"SELECT coalesce(COUNT(*), 0) as \"database_length!\", null::bigint as suspended FROM completed_job WHERE workspace_id = $1",
w_id
)
.fetch_one(&db)
+2 -2
View File
@@ -334,7 +334,7 @@ pub async fn run_server(
)
.nest(
"/w/:workspace_id/capture_u",
capture::global_service().layer(cors.clone()),
capture::global_service().layer(cors),
)
.nest(
"/auth",
@@ -346,7 +346,7 @@ pub async fn run_server(
)
.route("/version", get(git_v))
.route("/uptodate", get(is_up_to_date))
.route("/ee_license", get(ee_license).layer(cors.clone()))
.route("/ee_license", get(ee_license))
.route("/openapi.yaml", get(openapi))
.route("/openapi.json", get(openapi_json)),
)
+2 -1
View File
@@ -817,7 +817,8 @@ pub async fn get_full_hub_script_by_path(
Extension(db): Extension<DB>,
) -> JsonResult<HubScript> {
Ok(Json(
windmill_common::scripts::get_full_hub_script_by_path(path, &HTTP_CLIENT, &db).await?,
windmill_common::scripts::get_full_hub_script_by_path(path, &HTTP_CLIENT, Some(&db))
.await?,
))
}
+31 -11
View File
@@ -21,6 +21,9 @@ use axum::{
Json, Router,
};
#[cfg(feature = "enterprise")]
use axum::extract::Query;
use serde::Deserialize;
use windmill_common::{
error::{self, JsonResult, Result},
@@ -237,12 +240,12 @@ pub async fn set_global_setting_internal(
}
v => {
sqlx::query!(
"INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2, updated_at = now()",
key,
v
)
.execute(db)
.await?;
"INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2, updated_at = now()",
key,
v
)
.execute(db)
.await?;
tracing::info!("Set global setting {} to {}", key, v);
}
};
@@ -339,6 +342,12 @@ pub async fn get_latest_key_renewal_attempt(
}
}
#[cfg(feature = "enterprise")]
#[derive(Deserialize)]
pub struct LicenseQuery {
license_key: Option<String>,
}
#[cfg(not(feature = "enterprise"))]
pub async fn renew_license_key() -> Result<String> {
return Err(error::Error::BadRequest(
@@ -347,15 +356,23 @@ pub async fn renew_license_key() -> Result<String> {
}
#[cfg(feature = "enterprise")]
pub async fn renew_license_key(Extension(db): Extension<DB>, authed: ApiAuthed) -> Result<String> {
pub async fn renew_license_key(
Extension(db): Extension<DB>,
Query(LicenseQuery { license_key }): Query<LicenseQuery>,
authed: ApiAuthed,
) -> Result<String> {
require_super_admin(&db, &authed.email).await?;
windmill_common::stats_ee::send_stats(&"manual".to_string(), &HTTP_CLIENT, &db).await?;
let result = windmill_common::ee::renew_license_key(&HTTP_CLIENT, &db, None).await;
let result = windmill_common::ee::renew_license_key(&HTTP_CLIENT, &db, license_key).await;
if result != "success" {
return Err(error::Error::BadRequest(format!(
"Failed to renew license key: {}",
result
if result == "Unauthorized" {
"Invalid key".to_string()
} else {
result
}
)));
} else {
return Ok("Renewed license key".to_string());
@@ -370,8 +387,11 @@ pub async fn create_customer_portal_session() -> Result<String> {
}
#[cfg(feature = "enterprise")]
pub async fn create_customer_portal_session() -> Result<String> {
let url = windmill_common::ee::create_customer_portal_session(&HTTP_CLIENT, None).await?;
pub async fn create_customer_portal_session(
Query(LicenseQuery { license_key }): Query<LicenseQuery>,
) -> Result<String> {
let url =
windmill_common::ee::create_customer_portal_session(&HTTP_CLIENT, license_key).await?;
return Ok(url);
}
+3 -19
View File
@@ -44,7 +44,6 @@ use time::OffsetDateTime;
#[cfg(feature = "enterprise")]
use tokio::sync::RwLock;
use tower_cookies::{Cookie, Cookies};
use tower_http::cors::{Any, CorsLayer};
use tracing::{Instrument, Span};
use windmill_audit::audit_ee::{audit_log, AuditAuthor};
use windmill_audit::ActionKind;
@@ -67,11 +66,6 @@ const COOKIE_NAME: &str = "token";
const COOKIE_PATH: &str = "/";
pub fn workspaced_service() -> Router {
let cors = CorsLayer::new()
.allow_methods([http::Method::GET])
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
.allow_origin(Any);
Router::new()
.route("/list", get(list_users))
.route("/list_usage", get(list_user_usage))
@@ -82,21 +76,16 @@ pub fn workspaced_service() -> Router {
.route("/delete/:user", delete(delete_workspace_user))
.route("/is_owner/*path", get(is_owner_of_path))
.route("/whois/:username", get(whois))
.route("/whoami", get(whoami).layer(cors))
.route("/whoami", get(whoami))
.route("/leave", post(leave_workspace))
.route("/username_to_email/:username", get(username_to_email))
}
pub fn global_service() -> Router {
let cors = CorsLayer::new()
.allow_methods([http::Method::GET])
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
.allow_origin(Any);
Router::new()
.route("/exists/:email", get(exists_email))
.route("/email", get(get_email))
.route("/whoami", get(global_whoami).layer(cors))
.route("/whoami", get(global_whoami))
.route("/list_invites", get(list_invites))
.route("/decline_invite", post(decline_invite))
.route("/accept_invite", post(accept_invite))
@@ -129,13 +118,8 @@ pub fn global_service() -> Router {
}
pub fn make_unauthed_service() -> Router {
let cors = CorsLayer::new()
.allow_methods([http::Method::POST])
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
.allow_origin(Any);
Router::new()
.route("/login", post(login).layer(cors))
.route("/login", post(login))
.route("/logout", post(logout))
.route("/logout", get(logout))
}
+9 -14
View File
@@ -21,8 +21,6 @@ use crate::{
webhook_util::WebhookShared,
};
use tower_http::cors::{Any, CorsLayer};
use axum::{
extract::{Extension, Path, Query},
response::IntoResponse,
@@ -126,15 +124,10 @@ pub fn workspaced_service() -> Router {
router
}
pub fn global_service() -> Router {
let cors = CorsLayer::new()
.allow_methods([http::Method::GET])
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
.allow_origin(Any);
Router::new()
.route("/list_as_superadmin", get(list_workspaces_as_super_admin))
.route("/list", get(list_workspaces))
.route("/users", get(user_workspaces).layer(cors.clone()))
.route("/users", get(user_workspaces))
.route("/create", post(create_workspace))
.route("/exists", post(exists_workspace))
.route("/exists_username", post(exists_username))
@@ -175,9 +168,9 @@ pub struct WorkspaceSettings {
pub error_handler: Option<String>,
pub error_handler_extra_args: Option<serde_json::Value>,
pub error_handler_muted_on_cancel: Option<bool>,
pub large_file_storage: Option<serde_json::Value>, // effectively: DatasetsStorage
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
pub deploy_ui: Option<serde_json::Value>, // effectively: WorkspaceDeploymentUISettings
pub large_file_storage: Option<serde_json::Value>, // effectively: DatasetsStorage
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
pub deploy_ui: Option<serde_json::Value>, // effectively: WorkspaceDeploymentUISettings
pub default_app: Option<String>,
pub automatic_billing: bool,
pub default_scripts: Option<serde_json::Value>,
@@ -1078,6 +1071,7 @@ async fn edit_deploy_ui_config(
));
}
#[cfg(feature = "enterprise")]
async fn edit_deploy_ui_config(
authed: ApiAuthed,
@@ -1103,9 +1097,8 @@ async fn edit_deploy_ui_config(
.await?;
if let Some(deploy_ui_settings) = new_config.deploy_ui_settings {
let serialized_config =
serde_json::to_value::<WorkspaceDeploymentUISettings>(deploy_ui_settings)
.map_err(|err| Error::InternalErr(err.to_string()))?;
let serialized_config = serde_json::to_value::<WorkspaceDeploymentUISettings>(deploy_ui_settings)
.map_err(|err| Error::InternalErr(err.to_string()))?;
sqlx::query!(
"UPDATE workspace_settings SET deploy_ui = $1 WHERE workspace_id = $2",
@@ -1127,6 +1120,8 @@ async fn edit_deploy_ui_config(
Ok(format!("Edit deployment UI config for workspace {}", &w_id))
}
#[derive(Deserialize)]
pub struct EditDefaultApp {
pub default_app_path: Option<String>,
+6
View File
@@ -213,6 +213,12 @@ pub struct Suspend {
pub self_approval_disabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hide_cancel: Option<bool>,
#[serde(skip_serializing_if = "false_or_empty")]
pub continue_on_disapprove_timeout: Option<bool>,
}
fn false_or_empty(v: &Option<bool>) -> bool {
v.is_none() || v.as_ref().is_some_and(|x| !x)
}
#[derive(Deserialize, Serialize, Debug, Clone)]
@@ -30,7 +30,7 @@ pub const DEV_INSTANCE_SETTING: &str = "dev_instance";
pub const JWT_SECRET_SETTING: &str = "jwt_secret";
pub const EMAIL_DOMAIN_SETTING: &str = "email_domain";
pub const ENV_SETTINGS: [&str; 50] = [
pub const ENV_SETTINGS: [&str; 51] = [
"DISABLE_NSJAIL",
"MODE",
"NUM_WORKERS",
@@ -81,4 +81,5 @@ pub const ENV_SETTINGS: [&str; 50] = [
"MAX_WAIT_FOR_SIGTERM",
"WORKER_GROUP",
"SAML_METADATA",
"INSTANCE_IS_DEV",
];
+3 -3
View File
@@ -384,7 +384,7 @@ pub async fn get_hub_script_by_path(
&format!("{}/raw/{}.ts", hub_base_url, path),
true,
None,
db,
Some(db),
)
.await?
.text()
@@ -409,7 +409,7 @@ pub async fn get_hub_script_by_path(
&format!("{}/raw/{}.ts", DEFAULT_HUB_BASE_URL, path),
true,
None,
db,
Some(db),
)
.await?
.text()
@@ -427,7 +427,7 @@ pub async fn get_hub_script_by_path(
pub async fn get_full_hub_script_by_path(
path: StripPath,
http_client: &reqwest::Client,
db: &DB,
db: Option<&DB>,
) -> crate::error::Result<HubScript> {
let path = path
.to_path()
+13 -6
View File
@@ -96,7 +96,7 @@ pub async fn query_elems_from_hub(
reqwest::header::HeaderMap,
axum::body::Body,
)> {
let response = http_get_from_hub(http_client, url, false, query_params, db).await?;
let response = http_get_from_hub(http_client, url, false, query_params, Some(db)).await?;
let status = response.status();
@@ -112,9 +112,18 @@ pub async fn http_get_from_hub(
url: &str,
plain: bool,
query_params: Option<Vec<(&str, String)>>,
db: &Pool<Postgres>,
db: Option<&Pool<Postgres>>,
) -> Result<reqwest::Response> {
let uid = get_uid(db).await;
let uid = match db {
Some(db) => match get_uid(db).await {
Ok(uid) => Some(uid),
Err(err) => {
tracing::info!("No valid uid found: {}", err);
None
}
},
None => None,
};
let mut request = http_client.get(url).header(
"Accept",
@@ -125,10 +134,8 @@ pub async fn http_get_from_hub(
},
);
if let Ok(uid) = uid {
if let Some(uid) = uid {
request = request.header("X-uid", uid);
} else {
tracing::info!("No valid uid found: {}", uid.err().unwrap())
}
if let Some(query_params) = query_params {
+7 -1
View File
@@ -78,6 +78,11 @@ lazy_static::lazy_static! {
static ref CUSTOM_TAG_REGEX: Regex = Regex::new(r"^(\w+)\(((?:\w+)\+?)+\)$").unwrap();
pub static ref DISABLE_BUNDLING: bool = std::env::var("DISABLE_BUNDLING")
.ok()
.and_then(|x| x.parse::<bool>().ok())
.unwrap_or(false);
}
pub async fn make_suspended_pull_query(wc: &WorkerConfig) {
@@ -232,7 +237,8 @@ pub fn get_annotation(inner_content: &str) -> Annotations {
let native_mode: bool = annotations.contains(&"native".to_string());
//TODO: remove || npm_mode when bun build is more powerful
let nobundling: bool = annotations.contains(&"nobundling".to_string()) || nodejs_mode;
let nobundling: bool =
annotations.contains(&"nobundling".to_string()) || nodejs_mode || *DISABLE_BUNDLING;
Annotations { npm_mode, nodejs_mode, native_mode, nobundling }
}
+21 -21
View File
@@ -53,7 +53,6 @@ use windmill_common::{
jobs::{
get_payload_tag_from_prefixed_path, CompletedJob, JobKind, JobPayload, QueuedJob, RawCode,
},
oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH,
schedule::Schedule,
scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang},
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
@@ -111,6 +110,11 @@ lazy_static::lazy_static! {
pub static ref JOB_TOKEN: Option<String> = std::env::var("JOB_TOKEN").ok();
static ref JOB_ARGS_AUDIT_LOGS: bool = std::env::var("JOB_ARGS_AUDIT_LOGS")
.ok()
.and_then(|x| x.parse().ok())
.unwrap_or(false);
}
#[cfg(feature = "cloud")]
@@ -1474,16 +1478,6 @@ pub async fn push_error_handler<
}
}
// TODO(gbouv): REMOVE THIS after December 1st 2023 and ping users to re-save their error handlers
if on_failure_path
.to_string()
.eq("script/hub/5792/workspace-or-schedule-error-handler-slack")
{
// default slack error handler being used -> we need to inject the slack token
let slack_resource = format!("$res:{WORKSPACE_SLACK_BOT_TOKEN_PATH}");
extra.insert("slack".to_string(), to_raw_value(&slack_resource));
}
let result = sanitize_result(result);
let tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
@@ -1597,15 +1591,6 @@ async fn handle_recovered_schedule<
));
}
}
// TODO(gbouv): REMOVE THIS after December 1st 2023 and ping users to re-save their error handlers
if on_recovery_path
.to_string()
.eq("script/hub/2430/slack/schedule-recovery-handler-slack")
{
// default slack error handler being used -> we need to inject the slack token
let slack_resource = format!("$res:{WORKSPACE_SLACK_BOT_TOKEN_PATH}");
extra.insert("slack".to_string(), to_raw_value(&slack_resource));
}
let args = error_job
.result
@@ -3132,7 +3117,8 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
}
let hub_script =
get_full_hub_script_by_path(StripPath(path.clone()), &HTTP_CLIENT, _db).await?;
get_full_hub_script_by_path(StripPath(path.clone()), &HTTP_CLIENT, Some(_db))
.await?;
(
None,
@@ -3690,6 +3676,16 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
.map_err(|e| Error::InternalErr(format!("Could not insert concurrency_key={concurrency_key} for job_id={job_id} script_path={script_path:?} workspace_id={workspace_id}: {e:#}")))?;
}
let stringified_args = if *JOB_ARGS_AUDIT_LOGS {
Some(serde_json::to_string(&args).map_err(|e| {
Error::InternalErr(format!(
"Could not serialize args for audit log of job {job_id}: {e:#}"
))
})?)
} else {
None
};
tracing::debug!("Pushing job {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}");
let uuid = sqlx::query_scalar!(
"INSERT INTO queue
@@ -3831,6 +3827,10 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
}
};
if let Some(ref stringified_args) = stringified_args {
hm.insert("args", stringified_args);
}
audit_log(
&mut tx,
&audit_author,
+12 -7
View File
@@ -18,8 +18,8 @@ use crate::{
write_file_binary,
},
AuthedClientBackgroundTask, BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR,
BUN_DEPSTAR_CACHE_DIR, BUN_PATH, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_PATH,
NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, TZ_ENV,
BUN_DEPSTAR_CACHE_DIR, BUN_PATH, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH,
NODE_PATH, NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, TZ_ENV,
};
use tokio::{fs::File, process::Command};
@@ -1165,7 +1165,7 @@ try {{
"--config",
"run.config.proto",
"--",
&NODE_PATH,
&NODE_BIN_PATH,
"/tmp/nodejs/wrapper.mjs",
]
} else if codebase.is_some() || has_bundle_cache {
@@ -1207,7 +1207,7 @@ try {{
let cmd = if annotation.nodejs_mode {
let script_path = format!("{job_dir}/wrapper.mjs");
let mut bun_cmd = Command::new(&*NODE_PATH);
let mut bun_cmd = Command::new(&*NODE_BIN_PATH);
bun_cmd
.current_dir(job_dir)
.env_clear()
@@ -1248,7 +1248,7 @@ try {{
start_child_process(
cmd,
if annotation.nodejs_mode {
&*NODE_PATH
&*NODE_BIN_PATH
} else {
&*BUN_PATH
},
@@ -1274,7 +1274,7 @@ try {{
}
pub async fn get_common_bun_proc_envs(base_internal_url: &str) -> HashMap<String, String> {
let bun_envs: HashMap<String, String> = HashMap::from([
let mut bun_envs: HashMap<String, String> = HashMap::from([
(String::from("PATH"), PATH_ENV.clone()),
(String::from("HOME"), HOME_ENV.clone()),
(String::from("TZ"), TZ_ENV.clone()),
@@ -1291,6 +1291,11 @@ pub async fn get_common_bun_proc_envs(base_internal_url: &str) -> HashMap<String
BUN_CACHE_DIR.to_string(),
),
]);
if let Some(ref node_path) = NODE_PATH.as_ref() {
bun_envs.insert(String::from("NODE_PATH"), node_path.to_string());
}
return bun_envs;
}
@@ -1507,7 +1512,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
let script_path = format!("{job_dir}/wrapper.mjs");
handle_dedicated_process(
&*NODE_PATH,
&*NODE_BIN_PATH,
job_dir,
context_envs,
envs,
@@ -561,6 +561,9 @@ pub fn pg_cell_to_json_value(
Type::TIME => get_basic(row, column, column_i, |a: chrono::NaiveTime| {
Ok(JSONValue::String(a.to_string()))
})?,
Type::TIMETZ => get_basic(row, column, column_i, |a: TimeTZStr| {
Ok(JSONValue::String(a.0))
})?,
Type::TIMESTAMPTZ => get_basic(row, column, column_i, |a: chrono::DateTime<Utc>| {
Ok(JSONValue::String(a.to_string()))
})?,
@@ -633,6 +636,9 @@ pub fn pg_cell_to_json_value(
Type::TIME_ARRAY => get_array(row, column, column_i, |a: chrono::NaiveTime| {
Ok(JSONValue::String(a.to_string()))
})?,
Type::TIMETZ_ARRAY => get_array(row, column, column_i, |a: TimeTZStr| {
Ok(JSONValue::String(a.0))
})?,
Type::TIMESTAMPTZ_ARRAY => get_array(row, column, column_i, |a: chrono::DateTime<Utc>| {
Ok(JSONValue::String(a.to_string()))
})?,
@@ -699,6 +705,28 @@ impl<'a> FromSql<'a> for IntervalStr {
}
}
struct TimeTZStr(String);
impl<'a> FromSql<'a> for TimeTZStr {
fn from_sql(
_: &Type,
mut raw: &'a [u8],
) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
let microsecond = raw.get_i64();
let offset = raw.get_i32();
let utc_sec = (microsecond / 1_000_000) + offset as i64;
let utc = chrono::NaiveTime::from_num_seconds_from_midnight_opt(
((utc_sec + 3600 * 24) % (3600 * 24)) as u32,
((microsecond % 1_000_000) * 1_000) as u32,
)
.ok_or_else(|| anyhow::anyhow!("Invalid time value"))?;
Ok(TimeTZStr(format!("{:?} UTC", utc)))
}
fn accepts(ty: &Type) -> bool {
matches!(ty, &Type::TIMETZ)
}
}
fn get_array<'a, T: FromSql<'a>>(
row: &'a Row,
column: &Column,
@@ -17,7 +17,7 @@ use windmill_queue::{CanceledBy, HTTP_CLIENT};
use serde::{Deserialize, Serialize};
use crate::common::run_future_with_polling_update_job_poller;
use crate::common::{resolve_job_timeout, run_future_with_polling_update_job_poller};
use crate::{common::build_args_values, AuthedClientBackgroundTask};
#[derive(Serialize)]
@@ -304,7 +304,11 @@ pub async fn do_snowflake(
json!(database.database.unwrap().to_uppercase()),
);
}
body.insert("timeout".to_string(), json!(10)); // in seconds
let timeout = resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout)
.await
.0
.as_secs();
body.insert("timeout".to_string(), json!(timeout));
let queries = parse_sql_blocks(query);
+45 -12
View File
@@ -7,7 +7,7 @@
*/
use windmill_common::{
auth::{fetch_authed_from_permissioned_as, JWTAuthClaims, JobPerms, JWT_SECRET}, scripts::PREVIEW_IS_TAR_CODEBASE_HASH, worker::{get_windmill_memory_usage, get_worker_memory_usage, TMP_DIR}
auth::{fetch_authed_from_permissioned_as, JWTAuthClaims, JobPerms, JWT_SECRET}, scripts::PREVIEW_IS_TAR_CODEBASE_HASH, worker::{get_memory, get_vcpus, get_windmill_memory_usage, get_worker_memory_usage, TMP_DIR}
};
use anyhow::{Context, Result};
@@ -245,6 +245,7 @@ pub const POWERSHELL_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "powershell");
pub const COMPOSER_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "composer");
const NUM_SECS_PING: u64 = 5;
const NUM_SECS_READINGS: u64 = 60;
const INCLUDE_DEPS_PY_SH_CONTENT: &str = include_str!("../nsjail/download_deps.py.sh");
@@ -317,13 +318,15 @@ lazy_static::lazy_static! {
pub static ref DENO_PATH: String = std::env::var("DENO_PATH").unwrap_or_else(|_| "/usr/bin/deno".to_string());
pub static ref BUN_PATH: String = std::env::var("BUN_PATH").unwrap_or_else(|_| "/usr/bin/bun".to_string());
pub static ref NPM_PATH: String = std::env::var("NPM_PATH").unwrap_or_else(|_| "/usr/bin/npm".to_string());
pub static ref NODE_PATH: String = std::env::var("NODE_PATH").unwrap_or_else(|_| "/usr/bin/node".to_string());
pub static ref NODE_BIN_PATH: String = std::env::var("NODE_BIN_PATH").unwrap_or_else(|_| "/usr/bin/node".to_string());
pub static ref POWERSHELL_PATH: String = std::env::var("POWERSHELL_PATH").unwrap_or_else(|_| "/usr/bin/pwsh".to_string());
pub static ref PHP_PATH: String = std::env::var("PHP_PATH").unwrap_or_else(|_| "/usr/bin/php".to_string());
pub static ref COMPOSER_PATH: String = std::env::var("COMPOSER_PATH").unwrap_or_else(|_| "/usr/bin/composer".to_string());
pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string());
pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new());
pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
pub static ref NODE_PATH: Option<String> = std::env::var("NODE_PATH").ok();
pub static ref TZ_ENV: String = std::env::var("TZ").unwrap_or_else(|_| String::new());
pub static ref GOPRIVATE: Option<String> = std::env::var("GOPRIVATE").ok();
pub static ref GOPROXY: Option<String> = std::env::var("GOPROXY").ok();
@@ -371,6 +374,12 @@ lazy_static::lazy_static! {
.and_then(|x| x.parse::<u64>().ok());
pub static ref REFRESH_CGROUP_READINGS: bool = std::env::var("REFRESH_CGROUP_READINGS")
.ok()
.and_then(|x| x.parse().ok())
.unwrap_or(false);
}
//only matter if CLOUD_HOSTED
pub const MAX_RESULT_SIZE: usize = 1024 * 1024 * 2; // 2MB
@@ -1354,6 +1363,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
}
};
let mut suspend_first_success = false;
let mut last_reading = Instant::now() - Duration::from_secs(NUM_SECS_READINGS + 1);
loop {
#[cfg(feature = "benchmark")]
@@ -1383,15 +1393,23 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
let memory_usage = get_worker_memory_usage();
let wm_memory_usage = get_windmill_memory_usage();
let (vcpus, memory) = if *REFRESH_CGROUP_READINGS && last_reading.elapsed().as_secs() > NUM_SECS_READINGS {
last_reading = Instant::now();
(get_vcpus(), get_memory())
} else {
(None, None)
};
if let Err(e) = sqlx::query!(
"UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, current_job_id = NULL, current_job_workspace_id = NULL WHERE worker = $6",
"UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, current_job_id = NULL, current_job_workspace_id = NULL, vcpus = COALESCE($7, vcpus), memory = COALESCE($8, memory) WHERE worker = $6",
jobs_executed,
tags.as_slice(),
worker_code_execution_metric / start_time.elapsed().as_secs_f32(),
memory_usage,
wm_memory_usage,
&worker_name
&worker_name,
vcpus,
memory
).execute(db).await {
tracing::error!("failed to update worker ping, exiting: {}", e);
killpill_tx.send(()).unwrap_or_default();
@@ -2168,9 +2186,20 @@ pub async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clon
tracing::error!(job_id = %job.id, "error handling job: {err:?} {} {} {}", job.id, job.workspace_id, job.created_by);
}
fn extract_error_value(log_lines: &str, i: i32) -> Box<RawValue> {
#[derive(Debug, Serialize)]
struct SerializedError {
message: String,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
step_id: Option<String>,
}
fn extract_error_value(log_lines: &str, i: i32, step_id: Option<String>) -> Box<RawValue> {
return to_raw_value(
&json!({"message": format!("ExitCode: {i}, last log lines:\n{}", ANSI_ESCAPE_RE.replace_all(log_lines.trim(), "").to_string()), "name": "ExecutionErr"}),
&SerializedError {
message: format!("ExitCode: {i}, last log lines:\n{}", ANSI_ESCAPE_RE.replace_all(log_lines.trim(), "").to_string()),
name: "ExecutionErr".to_string(),
step_id,
},
);
}
@@ -2638,11 +2667,15 @@ async fn process_result(
.last()
.unwrap_or(&last_10_log_lines);
extract_error_value(log_lines, i)
extract_error_value(log_lines, i, job.flow_step_id.clone())
}
}
err @ _ => to_raw_value(
&json!({"message": format!("error during execution of the script:\n{}", err), "name": "ExecutionErr"}),
&SerializedError {
message: format!("error during execution of the script:\n{}", err),
name: "ExecutionErr".to_string(),
step_id: job.flow_step_id.clone(),
},
),
};
@@ -2703,9 +2736,9 @@ pub struct ContentReqLangEnvs {
pub codebase: Option<String>,
}
async fn get_hub_script_content_and_requirements(
pub async fn get_hub_script_content_and_requirements(
script_path: Option<String>,
db: &DB,
db: Option<&DB>,
) -> error::Result<ContentReqLangEnvs> {
let script_path = script_path
.clone()
@@ -2750,7 +2783,7 @@ async fn get_script_content_by_path(
.clone()
.ok_or_else(|| Error::InternalErr(format!("expected script path")))?;
return if script_path.starts_with("hub/") {
get_hub_script_content_and_requirements(Some(script_path), db).await
get_hub_script_content_and_requirements(Some(script_path), Some(db)).await
} else {
let (script_hash, ..) =
get_latest_deployed_hash_for_path(db, w_id, script_path.as_str()).await?;
@@ -2837,7 +2870,7 @@ async fn handle_code_execution_job(
codebase
}},
JobKind::Script_Hub => {
get_hub_script_content_and_requirements(job.script_path.clone(), db).await?
get_hub_script_content_and_requirements(job.script_path.clone(), Some(db)).await?
}
JobKind::Script => {
get_script_content_by_hash(
+42 -5
View File
@@ -162,6 +162,11 @@ pub struct SkipIfStopped {
pub args: Option<Json<HashMap<String, Box<RawValue>>>>,
}
#[derive(Deserialize)]
struct RecoveryObject {
recover: Option<bool>,
}
#[derive(sqlx::FromRow, Deserialize)]
pub struct RowFlowStatus {
pub flow_status: sqlx::types::Json<Box<serde_json::value::RawValue>>,
@@ -758,8 +763,9 @@ pub async fn update_flow_status_after_job_completion_internal<
let module = get_module(&flow_job, module_index);
// tracing::error!(
// "UPDATE FLOW STATUS 3: {module:#?} {unrecoverable} {} {is_last_step} {success} {skip_error_handler}", flow_job.canceled
// "UPDATE FLOW STATUS 3: {module:#?} {unrecoverable} {} {is_last_step} {success} {skip_error_handler} is_failure_step {is_failure_step}", flow_job.canceled
// );
let should_continue_flow = match success {
_ if stop_early => false,
_ if flow_job.canceled => false,
@@ -873,7 +879,13 @@ pub async fn update_flow_status_after_job_completion_internal<
save_in_cache(db, client, &flow_job, cached_res_path, &nresult).await;
}
let success = success && !is_failure_step && !skip_error_handler;
fn result_has_recover_true(nresult: Arc<Box<RawValue>>) -> bool {
let recover = serde_json::from_str::<RecoveryObject>(nresult.get());
return recover.map(|r| r.recover.unwrap_or(false)).unwrap_or(false);
}
let success = success
&& (!is_failure_step || result_has_recover_true(nresult.clone()))
&& !skip_error_handler;
if success {
add_completed_job(
db,
@@ -1715,7 +1727,26 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
}
let is_disapproved = resumes.iter().find(|x| !x.approved);
if is_disapproved.is_none() && resume_messages.len() >= required_events as usize {
let can_be_resumed =
is_disapproved.is_none() && resume_messages.len() >= required_events as usize;
let disapproved_or_timeout_but_continue = !can_be_resumed
&& (is_disapproved.is_some()
|| !matches!(
&status_module,
FlowStatusModule::WaitingForPriorSteps { .. }
))
&& suspend.continue_on_disapprove_timeout.unwrap_or(false);
if can_be_resumed || disapproved_or_timeout_but_continue {
if disapproved_or_timeout_but_continue {
let js = if let Some(disapproved) = is_disapproved.as_ref() {
json!({"error": {"message": format!("Disapproved by {}", disapproved.approver.clone().unwrap_or_else( || "unknown".to_string())), "name": "SuspendedDisapproved"}})
} else {
json!({"error": {"message": "Timed out waiting to be resumed", "name": "SuspendedTimedOut"}})
};
resume_messages.push(to_raw_value(&js));
}
sqlx::query(
"UPDATE queue
SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2)
@@ -1787,7 +1818,13 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
let (logs, error_name) = if let Some(disapprover) = is_disapproved {
(
format!("Disapproved by {:?}", disapprover.approver),
format!(
"Disapproved by {}",
disapprover
.approver
.clone()
.unwrap_or_else(|| "unknown".to_string())
),
"SuspendedDisapproved",
)
} else {
@@ -2342,7 +2379,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
continue_on_same_worker,
err,
flow_job.visible_to_owner,
if flow_job.tag == "flow" {
if flow_job.tag == "flow" || flow_job.tag == format!("flow-{}", flow_job.workspace_id) {
payload_tag.tag
} else {
Some(flow_job.tag.clone())
+1 -1
View File
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.377.1";
export const VERSION = "v1.381.0";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+2
View File
@@ -34,6 +34,8 @@ export interface Codebase {
}[];
customBundler?: string;
external?: string[];
define?: { [key: string]: string };
inject?: string[];
}
export async function readConfigFile(): Promise<SyncOptions> {
+25 -4
View File
@@ -12,7 +12,7 @@ import {
yamlParse,
} from "./deps.ts";
import { requireLogin, resolveWorkspace, validatePath } from "./context.ts";
import { resolve, track_job } from "./script.ts";
import { findGlobalDeps, resolve, track_job } from "./script.ts";
import { defaultFlowDefinition } from "./bootstrap/flow_bootstrap.ts";
import { generateFlowLockInternal } from "./metadata.ts";
import { SyncOptions, mergeConfigWithConfigFile } from "./conf.ts";
@@ -237,8 +237,15 @@ async function generateLocks(
await requireLogin(opts);
opts = await mergeConfigWithConfigFile(opts);
if (folder) {
const globalDeps = await findGlobalDeps();
// read script metadata file
await generateFlowLockInternal(folder, false, workspace);
await generateFlowLockInternal(
folder,
false,
workspace,
globalDeps,
opts.defaultTs
);
} else {
const ignore = await ignoreF(opts);
const elems = Object.keys(
@@ -258,8 +265,16 @@ async function generateLocks(
).map((x) => x.substring(0, x.lastIndexOf(SEP)));
let hasAny = false;
const globalDeps = await findGlobalDeps();
for (const folder of elems) {
const candidate = await generateFlowLockInternal(folder, true, workspace);
const candidate = await generateFlowLockInternal(
folder,
true,
workspace,
globalDeps,
opts.defaultTs
);
if (candidate) {
hasAny = true;
log.info(colors.green(`+ ${candidate}`));
@@ -281,7 +296,13 @@ async function generateLocks(
return;
}
for (const folder of elems) {
await generateFlowLockInternal(folder, false, workspace);
await generateFlowLockInternal(
folder,
false,
workspace,
globalDeps,
opts.defaultTs
);
}
}
}
+1 -1
View File
@@ -34,7 +34,7 @@ addEventListener("error", (event) => {
}
});
export const VERSION = "v1.377.1";
export const VERSION = "v1.381.0";
let command: any = new Command()
.name("wmill")
+115 -44
View File
@@ -46,7 +46,7 @@ import { FlowFile, replaceInlineScripts } from "./flow.ts";
export async function generateAllMetadata() {}
function findClosestRawReqs(
lang: "bun" | "python3" | "php" | undefined,
lang: "bun" | "python3" | "php" | string | undefined,
remotePath: string,
globalDeps: GlobalDeps
): string | undefined {
@@ -84,20 +84,56 @@ function findClosestRawReqs(
}
const TOP_HASH = "__flow_hash";
async function generateFlowHash(folder: string) {
async function generateFlowHash(
folder: string,
globalDeps: GlobalDeps,
defaultTs: "bun" | "deno" | undefined
): Promise<{
hashes: Record<string, string>;
pathsWithRawReqs: string[];
}> {
const elems = await FSFSElement(path.join(Deno.cwd(), folder), []);
const hashes: Record<string, string> = {};
const pathsWithRawReqs: string[] = [];
for await (const f of elems.getChildren()) {
if (exts.some((e) => f.path.endsWith(e))) {
hashes[f.path] = await generateHash(await f.getContentText());
const found_ext = exts.find((e) => f.path.endsWith(e));
if (found_ext) {
const lang = inferContentTypeFromFilePath(f.path, defaultTs);
// log.info(remote_path + "/script" + ext);
const raw = findClosestRawReqs(
lang as "bun" | "php" | "python3" | undefined,
path.join(folder, f.path),
globalDeps
);
// if (raw) {
// rawReqs[ext] = raw;
// }
let text = await f.getContentText();
if (raw) {
text = raw + text;
pathsWithRawReqs.push(f.path);
}
hashes[f.path] = await generateHash(text);
}
}
return { ...hashes, [TOP_HASH]: await generateHash(JSON.stringify(hashes)) };
return {
hashes: {
...hashes,
[TOP_HASH]: await generateHash(JSON.stringify(hashes)),
},
pathsWithRawReqs,
};
}
export async function generateFlowLockInternal(
folder: string,
dryRun: boolean,
workspace: Workspace,
globalDeps: GlobalDeps,
defaultTs: "bun" | "deno" | undefined,
justUpdateMetadataLock?: boolean
): Promise<string | undefined> {
if (folder.endsWith(SEP)) {
@@ -110,7 +146,11 @@ export async function generateFlowLockInternal(
log.info(`Generating lock for flow ${folder} at ${remote_path}`);
}
let hashes = await generateFlowHash(folder);
let { hashes, pathsWithRawReqs } = await generateFlowHash(
folder,
globalDeps,
defaultTs
);
const conf = await readLockfile();
if (await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH)) {
@@ -126,6 +166,7 @@ export async function generateFlowLockInternal(
await Deno.readTextFile(folder! + SEP + "flow.yaml")
) as FlowFile;
const fullReqs: Partial<Record<ScriptLanguage, string>> = {};
if (!justUpdateMetadataLock) {
const changedScripts = [];
//find hashes that do not correspond to previous hashes
@@ -139,6 +180,19 @@ export async function generateFlowLockInternal(
}
log.info(`Recomputing locks of ${changedScripts.join(", ")} in ${folder}`);
for (const script of changedScripts) {
if (pathsWithRawReqs.includes(script)) {
const lang = inferContentTypeFromFilePath(script, defaultTs);
log.info(`Inline script ${script} has raw requirements`);
let req = fullReqs[lang];
if (!req) {
const rawReq = findClosestRawReqs(lang, script, globalDeps) ?? "";
const fullReq = await getLockfileFromWindmill();
fullReqs[lang] = req;
}
}
}
replaceInlineScripts(
flowValue.value.modules,
folder + SEP!,
@@ -161,7 +215,7 @@ export async function generateFlowLockInternal(
});
}
hashes = await generateFlowHash(folder);
hashes = (await generateFlowHash(folder, globalDeps, defaultTs)).hashes;
for (const [path, hash] of Object.entries(hashes)) {
await updateMetadataGlobalLock(folder, hash, path);
@@ -308,27 +362,13 @@ export async function updateScriptSchema(
metadataContent.schema = newSchema;
}
async function updateScriptLock(
async function getLockfileFromWindmill(
workspace: Workspace,
scriptContent: string,
language: ScriptLanguage,
remotePath: string,
metadataContent: Record<string, any>,
rawDeps: string | undefined
): Promise<void> {
if (
!(
language == "bun" ||
language == "python3" ||
language == "go" ||
language == "deno" ||
language == "php"
)
) {
return;
}
// generate the script lock running a dependency job in Windmill and update it inplace
// TODO: update this once the client is released
): Promise<{ lock: string | undefined }> {
const rawResponse = await fetch(
`${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies`,
{
@@ -354,27 +394,7 @@ async function updateScriptLock(
let responseText = "reading response failed";
try {
responseText = await rawResponse.text();
const response = JSON.parse(responseText);
const lock = response.lock;
if (lock === undefined) {
throw new Error(
`Failed to generate lockfile. Full response was: ${JSON.stringify(
response
)}`
);
}
const lockPath = remotePath + ".script.lock";
if (lock != "") {
await Deno.writeTextFile(lockPath, lock);
metadataContent.lock = "!inline " + lockPath.replaceAll(SEP, "/");
} else {
try {
if (await Deno.stat(lockPath)) {
await Deno.remove(lockPath);
}
} catch {}
metadataContent.lock = "";
}
return JSON.parse(responseText);
} catch (e) {
throw new Error(
`Failed to generate lockfile. Status was: ${rawResponse.statusText}, ${responseText}, ${e}`
@@ -382,6 +402,57 @@ async function updateScriptLock(
}
}
async function updateScriptLock(
workspace: Workspace,
scriptContent: string,
language: ScriptLanguage,
remotePath: string,
metadataContent: Record<string, any>,
rawDeps: string | undefined
): Promise<void> {
if (
!(
language == "bun" ||
language == "python3" ||
language == "go" ||
language == "deno" ||
language == "php"
)
) {
return;
}
// generate the script lock running a dependency job in Windmill and update it inplace
// TODO: update this once the client is released
const response = getLockfileFromWindmill(
workspace,
scriptContent,
language,
remotePath,
rawDeps
);
const lock = response.lock;
if (lock === undefined) {
throw new Error(
`Failed to generate lockfile. Full response was: ${JSON.stringify(
response
)}`
);
}
const lockPath = remotePath + ".script.lock";
if (lock != "") {
await Deno.writeTextFile(lockPath, lock);
metadataContent.lock = "!inline " + lockPath.replaceAll(SEP, "/");
} else {
try {
if (await Deno.stat(lockPath)) {
await Deno.remove(lockPath);
}
} catch {}
metadataContent.lock = "";
}
}
export async function updateFlow(
workspace: Workspace,
flow_value: FlowValue,
+2
View File
@@ -173,6 +173,8 @@ export async function handleFile(
bundle: true,
write: false,
external: codebase.external,
inject: codebase.inject,
define: codebase.define,
platform: "node",
packages: "bundle",
target: "node20.15.1",
+8 -1
View File
@@ -1070,7 +1070,14 @@ export async function pull(opts: GlobalOptions & SyncOptions) {
}
for (const change of changedFlows) {
log.info(`Updating lock for flow ${change}`);
await generateFlowLockInternal(change, false, workspace, true);
await generateFlowLockInternal(
change,
false,
workspace,
globalDeps,
opts.defaultTs,
true
);
}
log.info(
colors.bold.green.underline(
+5 -1
View File
@@ -27,7 +27,11 @@ COPY --from=denoland/deno:1.44.4 --chmod=755 /usr/bin/deno /usr/bin/deno
RUN ln -s ${APP}/windmill /usr/local/bin/windmill
RUN windmill cache
COPY ./frontend/src/lib/hubPaths.json ${APP}/hubPaths.json
RUN windmill cache ${APP}/hubPaths.json
RUN rm ${APP}/hubPaths.json
EXPOSE 8000
+106 -76
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.377.3",
"version": "1.381.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.377.3",
"version": "1.381.0",
"license": "AGPL-3.0",
"dependencies": {
"@aws-crypto/sha256-js": "^4.0.0",
@@ -16,6 +16,7 @@
"@redocly/json-to-json-schema": "^0.0.1",
"@tanstack/svelte-table": "^8.9.9",
"@windmill-labs/svelte-dnd-action": "latest",
"@xyflow/svelte": "^0.1.14",
"ag-charts-community": "^9.0.1",
"ag-charts-enterprise": "^9.0.1",
"ag-grid-community": "^31.0.0",
@@ -40,6 +41,7 @@
"monaco-languageclient": "~7.0.1",
"ol": "^7.4.0",
"openai": "^4.47.1",
"p-limit": "^6.1.0",
"pdfjs-dist": "^3.8.162",
"quill": "^1.3.7",
"rfc4648": "^1.5.3",
@@ -1777,6 +1779,14 @@
"resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz",
"integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ=="
},
"node_modules/@svelte-put/shortcut": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-3.1.1.tgz",
"integrity": "sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==",
"peerDependencies": {
"svelte": "^3.55.0 || ^4.0.0 || ^5.0.0"
}
},
"node_modules/@sveltejs/adapter-static": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.1.tgz",
@@ -2022,8 +2032,7 @@
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"dev": true
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
},
"node_modules/@types/d3-contour": {
"version": "3.0.6",
@@ -2051,7 +2060,6 @@
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
"dev": true,
"dependencies": {
"@types/d3-selection": "*"
}
@@ -2108,7 +2116,6 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"dev": true,
"dependencies": {
"@types/d3-color": "*"
}
@@ -2155,8 +2162,7 @@
"node_modules/@types/d3-selection": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz",
"integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==",
"dev": true
"integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg=="
},
"node_modules/@types/d3-shape": {
"version": "3.1.5",
@@ -2189,7 +2195,6 @@
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz",
"integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==",
"dev": true,
"dependencies": {
"@types/d3-selection": "*"
}
@@ -2198,7 +2203,6 @@
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
"dev": true,
"dependencies": {
"@types/d3-interpolate": "*",
"@types/d3-selection": "*"
@@ -2290,8 +2294,7 @@
"node_modules/@types/pug": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.9.tgz",
"integrity": "sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA==",
"dev": true
"integrity": "sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA=="
},
"node_modules/@types/semver": {
"version": "7.5.5",
@@ -2511,6 +2514,34 @@
"svelte": ">=3.23.0 || ^5.0.0-next.0"
}
},
"node_modules/@xyflow/svelte": {
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@xyflow/svelte/-/svelte-0.1.14.tgz",
"integrity": "sha512-DdlFz+JKW1sQt8Q8O1DoSsroTvFk1RzmpcEZcGARt1REbnLy4BYG/03piaiclaC4iHK6feaPDvxpF057CkMCTw==",
"dependencies": {
"@svelte-put/shortcut": "^3.1.0",
"@xyflow/system": "0.0.37",
"classcat": "^5.0.4",
"svelte-preprocess": "^5.1.3"
},
"peerDependencies": {
"svelte": "^3.0.0 || ^4.0.0"
}
},
"node_modules/@xyflow/system": {
"version": "0.0.37",
"resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.37.tgz",
"integrity": "sha512-hSIhezhxgftPUpC+xiQVIorcRILZUOWlLjpYPTyGWRu8s4RJvM4GqvrsFmD5OnMKXLgpU7/PqqUibDVO67oWQQ==",
"dependencies": {
"@types/d3-drag": "^3.0.7",
"@types/d3-selection": "^3.0.10",
"@types/d3-transition": "^3.0.8",
"@types/d3-zoom": "^3.0.8",
"d3-drag": "^3.0.0",
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0"
}
},
"node_modules/@zerodevx/svelte-toast": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@zerodevx/svelte-toast/-/svelte-toast-0.9.5.tgz",
@@ -2973,7 +3004,6 @@
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"engines": {
"node": "*"
}
@@ -3241,6 +3271,11 @@
"consola": "^3.2.3"
}
},
"node_modules/classcat": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="
},
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
@@ -3308,8 +3343,7 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"devOptional": true
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/confbox": {
"version": "0.1.6",
@@ -3921,7 +3955,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
"integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -4148,8 +4181,7 @@
"node_modules/es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
"integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
"dev": true
"integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg=="
},
"node_modules/esbuild": {
"version": "0.21.5",
@@ -4843,8 +4875,7 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"devOptional": true
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
"version": "2.3.3",
@@ -4962,7 +4993,6 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"devOptional": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -4994,7 +5024,6 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"devOptional": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -5004,7 +5033,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"devOptional": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -5121,8 +5149,7 @@
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"node_modules/graphemer": {
"version": "1.4.0",
@@ -5430,7 +5457,6 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"devOptional": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -5439,8 +5465,7 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"devOptional": true
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "1.3.8",
@@ -5920,7 +5945,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
"dev": true,
"devOptional": true,
"engines": {
"node": ">=10"
}
@@ -6999,7 +7024,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -7088,7 +7112,6 @@
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"dependencies": {
"minimist": "^1.2.6"
},
@@ -7201,7 +7224,7 @@
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true,
"devOptional": true,
"funding": [
{
"type": "github",
@@ -7510,7 +7533,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"devOptional": true,
"dependencies": {
"wrappy": "1"
}
@@ -7574,15 +7596,15 @@
}
},
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.1.0.tgz",
"integrity": "sha512-H0jc0q1vOzlEk0TqAKXKZxdl7kX3OFUzCnNVUnq5Pc3DGo0kpeaMuPqxQn235HibwBEb0/pm9dgKTjXy66fBkg==",
"license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
"yocto-queue": "^1.1.1"
},
"engines": {
"node": ">=10"
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -7603,6 +7625,35 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate/node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate/node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pako": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
@@ -7678,7 +7729,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"devOptional": true,
"engines": {
"node": ">=0.10.0"
}
@@ -7766,8 +7816,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"dev": true,
"license": "ISC"
"devOptional": true
},
"node_modules/picomatch-browser": {
"version": "2.2.6",
@@ -7857,7 +7906,7 @@
"version": "8.4.41",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
"integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
"dev": true,
"devOptional": true,
"funding": [
{
"type": "opencollective",
@@ -7872,7 +7921,6 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.1",
@@ -8033,7 +8081,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
"integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
"dev": true,
"devOptional": true,
"dependencies": {
"lilconfig": "^2.0.5",
"yaml": "^2.1.1"
@@ -9000,7 +9048,6 @@
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
"integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==",
"dev": true,
"dependencies": {
"es6-promise": "^3.1.2",
"graceful-fs": "^4.1.3",
@@ -9012,7 +9059,6 @@
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -9182,7 +9228,6 @@
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
"integrity": "sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.14",
"buffer-crc32": "^0.2.5",
@@ -9837,27 +9882,26 @@
}
},
"node_modules/svelte-preprocess": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.0.tgz",
"integrity": "sha512-EkErPiDzHAc0k2MF5m6vBNmRUh338h2myhinUw/xaqsLs7/ZvsgREiLGj03VrSzbY/TB5ZXgBOsKraFee5yceA==",
"dev": true,
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz",
"integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==",
"hasInstallScript": true,
"dependencies": {
"@types/pug": "^2.0.6",
"detect-indent": "^6.1.0",
"magic-string": "^0.27.0",
"magic-string": "^0.30.5",
"sorcery": "^0.11.0",
"strip-indent": "^3.0.0"
},
"engines": {
"node": ">= 14.10.0"
"node": ">= 16.0.0"
},
"peerDependencies": {
"@babel/core": "^7.10.2",
"coffeescript": "^2.5.1",
"less": "^3.11.3 || ^4.0.0",
"postcss": "^7 || ^8",
"postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0",
"postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
"pug": "^3.0.0",
"sass": "^1.26.8",
"stylus": "^0.55.0",
@@ -9898,23 +9942,10 @@
}
}
},
"node_modules/svelte-preprocess/node_modules/magic-string": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
},
"engines": {
"node": ">=12"
}
},
"node_modules/svelte-preprocess/node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"dev": true,
"dependencies": {
"min-indent": "^1.0.0"
},
@@ -10294,7 +10325,7 @@
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"devOptional": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -10757,8 +10788,7 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"devOptional": true
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/write-file-atomic": {
"version": "5.0.1",
@@ -10927,12 +10957,12 @@
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz",
"integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==",
"license": "MIT",
"engines": {
"node": ">=10"
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
+3 -9
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.377.8",
"version": "1.381.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -97,6 +97,7 @@
"@redocly/json-to-json-schema": "^0.0.1",
"@tanstack/svelte-table": "^8.9.9",
"@windmill-labs/svelte-dnd-action": "latest",
"@xyflow/svelte": "^0.1.14",
"ag-charts-community": "^9.0.1",
"ag-charts-enterprise": "^9.0.1",
"ag-grid-community": "^31.0.0",
@@ -121,6 +122,7 @@
"monaco-languageclient": "~7.0.1",
"ol": "^7.4.0",
"openai": "^4.47.1",
"p-limit": "^6.1.0",
"pdfjs-dist": "^3.8.162",
"quill": "^1.3.7",
"rfc4648": "^1.5.3",
@@ -325,11 +327,6 @@
"types": "./package/components/DropdownV2.svelte.d.ts",
"svelte": "./package/components/DropdownV2.svelte",
"default": "./package/components/DropdownV2.svelte"
},
"./components/AppEmbed.svelte": {
"types": "./package/components/AppEmbed.svelte.d.ts",
"svelte": "./package/components/AppEmbed.svelte",
"default": "./package/components/AppEmbed.svelte"
}
},
"files": [
@@ -447,9 +444,6 @@
],
"components/DropdownV2.svelte": [
"./package/components/DropdownV2.svelte.d.ts"
],
"components/AppEmbed.svelte": [
"./package/components/AppEmbed.svelte.d.ts"
]
}
},
+18 -12
View File
@@ -1,5 +1,7 @@
// https://github.com/jsdelivr/data.jsdelivr.com
import pLimit from 'p-limit'
export const getNPMVersionsForModule = (moduleName: string, resLimit: ResLimit) => {
const url = `https://data.jsdelivr.com/v1/package/npm/${moduleName}`
return api<{ tags: Record<string, string>; versions: string[] }>(url, resLimit, {
@@ -51,7 +53,7 @@ export const getDTSFileForModuleWithVersion = async (
) => {
// file comes with a prefix /
const url = `https://cdn.jsdelivr.net/npm/${moduleName}@${version}${file}`
const res = await fetch(url)
const res = await limit(() => fetch(url))
if (res.ok) {
return res.text()
} else {
@@ -67,6 +69,8 @@ export function isOverlimit(resLimit: ResLimit) {
return resLimit.usage > 5000000
}
export const limit = pLimit(6)
function api<T>(url: string, resLimit: ResLimit, init?: RequestInit): Promise<T | Error> {
if (isOverlimit(resLimit)) {
console.warn(
@@ -75,16 +79,18 @@ function api<T>(url: string, resLimit: ResLimit, init?: RequestInit): Promise<T
return new Promise(() => new Error('Exceeded limit of 100MB of data downloaded.'))
}
return fetch(url, init).then((res) => {
if (res.ok) {
return res.text().then((text) => {
resLimit.usage += text.length
console.log('resLimit', url, resLimit.usage)
return limit(() =>
fetch(url, init).then((res) => {
if (res.ok) {
return res.text().then((text) => {
resLimit.usage += text.length
console.log('resLimit', url, resLimit.usage)
return JSON.parse(text) as T
}) as Promise<T | Error>
} else {
return new Error('OK')
}
})
return JSON.parse(text) as T
}) as Promise<T | Error>
} else {
return new Error('OK')
}
})
)
}
+2
View File
@@ -4,6 +4,7 @@ import {
getNPMVersionForModuleReference,
getNPMVersionsForModule,
isOverlimit,
limit,
type NPMTreeMeta,
type ResLimit
} from './apis'
@@ -49,6 +50,7 @@ export const setupTypeAcquisition = (config: ATABootstrapConfig) => {
const moduleMap = new Map<string, ModuleMeta>()
const fsMap = new Map<string, string>()
limit.clearQueue()
let estimatedToDownload = 0
let estimatedDownloaded = 0
-167
View File
@@ -1,167 +0,0 @@
<script lang="ts">
import { BROWSER } from 'esm-env'
import { base } from '$lib/base'
import AppPreview from '$lib/components/apps/editor/AppPreview.svelte'
import { IS_APP_PUBLIC_CONTEXT_KEY, type EditorBreakpoint } from '$lib/components/apps/types'
import { Alert, Skeleton } from '$lib/components/common'
import { WindmillIcon } from '$lib/components/icons'
import { AppService, type AppWithLastVersion } from '$lib/gen'
import { enterpriseLicense, userStore } from '$lib/stores'
import { twMerge } from 'tailwind-merge'
import { setContext } from 'svelte'
import { writable } from 'svelte/store'
import { setLicense } from '$lib/enterpriseUtils'
import { isCloudHosted } from '$lib/cloud'
import Login from '$lib/components/Login.svelte'
import { getUserExt } from '$lib/user'
import { User, UserRoundX } from 'lucide-svelte'
export let workspace: string
export let secret: string | undefined = undefined
export let path: string | undefined = undefined
export let replaceStateFn: (path: string) => void = (path: string) =>
window.history.replaceState(null, '', path)
export let gotoFn: (path: string, opt?: Record<string, any> | undefined) => void = (
path: string,
opt?: Record<string, any>
) => window.history.pushState(null, '', path)
export let hideUser = false
let app: (AppWithLastVersion & { value: any }) | undefined = undefined
let notExists = false
let noPermission = false
let neitherSecretNorPath = !secret && !path
setContext(IS_APP_PUBLIC_CONTEXT_KEY, true)
async function loadApp() {
try {
neitherSecretNorPath = !secret && !path
if (neitherSecretNorPath) {
throw new Error('No secret or path provided')
}
app = secret
? await AppService.getPublicAppBySecret({
workspace: workspace,
path: secret
})
: await AppService.getAppByPath({
workspace: workspace,
path: path!
})
noPermission = false
notExists = false
} catch (e) {
if (e.status == 401) {
noPermission = true
} else {
notExists = true
}
}
}
if (BROWSER) {
setLicense()
loadUser().then(() => {
loadApp()
})
}
async function loadUser() {
try {
userStore.set(await getUserExt(workspace))
} catch (e) {
console.warn('Anonymous user')
}
}
const breakpoint = writable<EditorBreakpoint>('lg')
const darkMode =
window.localStorage.getItem('dark-mode') ??
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
if (darkMode === 'dark') {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
<div class="windmil-app relative h-full w-full">
<div
class="z-50 text-xs fixed bottom-1 right-2 {$enterpriseLicense && !isCloudHosted()
? 'transition-opacity delay-1000 duration-1000 opacity-20 hover:delay-0 hover:opacity-100'
: ''}"
>
<a href="https://windmill.dev" class="whitespace-nowrap text-tertiary inline-flex items-center"
>Powered by &nbsp;<WindmillIcon />&nbsp;Windmill</a
>
</div>
{#if !hideUser}
<div class="z-50 text-2xs text-tertiary absolute top-3 left-2"
>{#if $userStore}
<div class="flex gap-1 items-center"><User size={14} />{$userStore.username}</div>
{:else}<UserRoundX size={14} />{/if}
</div>
{/if}
{#if notExists}
<div class="px-4 mt-20"
><Alert type="error" title="Not found"
>There was an error loading the app, is the url correct? <a href={base}>Go to Windmill</a>
</Alert></div
>
{:else if noPermission}
<div class="px-4 mt-20 w-full text-center font-bold text-xl"
>{#if $userStore}You are logged in but have no read access for this app{:else}You must be
logged in and have read access for this app{/if}</div
>
<div class="px-2 mx-auto mt-20 max-w-xl w-full">
<Login
loginPasswordRequireEEOnPublicApps
on:login={() => {
// window.location.reload()
loadUser().then(() => {
loadApp()
})
app = app
}}
popup
rd={window.location.href}
/>
</div>
{:else if app}
{#key app}
<div
class={twMerge(
'min-h-screen h-full w-full',
app?.value?.['css']?.['app']?.['viewer']?.class,
'wm-app-viewer'
)}
style={app?.value?.['css']?.['app']?.['viewer']?.style}
>
<AppPreview
noBackend={false}
username={$userStore?.username ?? 'anonymous'}
email={$userStore?.email ?? 'anonymous'}
groups={$userStore?.groups ?? []}
{workspace}
summary={app.summary}
app={app.value}
appPath={app.path}
{breakpoint}
author={app.policy.on_behalf_of_email ?? ''}
isEditor={false}
{replaceStateFn}
{gotoFn}
/>
</div>
{/key}
{:else}
<Skeleton layout={[[4], 0.5, [50]]} />
{/if}
</div>
+33 -3
View File
@@ -407,8 +407,24 @@
enum_={itemsType?.enum ?? []}
enumLabels={extra['enumLabels']}
/>
{:else if itemsType?.type == 'resource' && itemsType?.resourceType}
<ResourcePicker bind:value={v} resourceType={itemsType?.resourceType} />
{:else if itemsType?.type == 'resource' && itemsType?.resourceType && resourceTypes?.includes(itemsType.resourceType)}
<ObjectResourceInput
bind:value={v}
format={'resource-' + itemsType?.resourceType}
defaultValue={undefined}
/>
{:else if itemsType?.type == 'resource'}
<JsonEditor
bind:editor
on:focus={(e) => {
dispatch('focus')
}}
on:blur={(e) => {
dispatch('blur')
}}
code={JSON.stringify(v, null, 2)}
bind:value={v}
/>
{:else if itemsType?.type === 'object' && itemsType?.properties}
<div class="p-8 border rounded-md w-full">
<SchemaForm
@@ -455,7 +471,20 @@
if (value == undefined || !Array.isArray(value)) {
value = []
}
value = value.concat('')
if (itemsType?.type == 'number') {
value = value.concat(0)
} else if (
itemsType?.type == 'object' ||
(itemsType?.type == 'resource' &&
!(
itemsType?.resourceType &&
resourceTypes?.includes(itemsType?.resourceType)
))
) {
value = value.concat({})
} else {
value = value.concat('')
}
}}
id="arg-input-add-item"
startIcon={{ icon: Plus }}
@@ -495,6 +524,7 @@
{disablePortal}
{format}
bind:value
bind:editor
{showSchemaExplorer}
/>
{:else if inputCat == 'resource-object' && format.split('-').length > 1 && format
+32 -3
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { format, isValid } from 'date-fns'
import { format, isValid, parse } from 'date-fns'
import { sendUserToast } from '$lib/toast'
export let value: string | undefined = undefined
export let autofocus: boolean | null = false
@@ -8,14 +9,42 @@
export let maxDate: string | undefined = undefined
export let dateFormat: string | undefined = 'dd-MM-yyyy'
let date: string | undefined = undefined
let date: string | undefined = computeDate(value)
const dispatch = createEventDispatcher()
const defaultDateFormat = 'dd-MM-yyyy'
const defaultHtmlDateFormat = 'yyyy-MM-dd'
function computeDate(value: string | undefined) {
if (dateFormat === undefined) {
dateFormat = defaultDateFormat
}
if (value && value.length > 0) {
try {
let date = parse(value, dateFormat, new Date())
if (date.toString() === 'Invalid Date') {
console.debug('falling back to default html date format')
date = parse(value, defaultHtmlDateFormat, new Date())
}
const res = format(date, defaultHtmlDateFormat)
return res
} catch (error) {
sendUserToast(
`Failed to parse date: ${value} with format ${dateFormat} and ${defaultHtmlDateFormat}`,
true
)
console.error(`Failed to parse date: ${value}`, error)
return undefined
}
} else {
return undefined
}
}
function updateValue(newDate: string | undefined) {
if (newDate && isValid(new Date(newDate))) {
if (dateFormat === undefined) {
dateFormat = 'dd-MM-yyyy'
dateFormat = defaultDateFormat
}
try {
@@ -16,6 +16,7 @@
ArrowDownFromLine
} from 'lucide-svelte'
import Portal from '$lib/components/Portal.svelte'
import ObjectViewer from './propertyPicker/ObjectViewer.svelte'
import S3FilePicker from './S3FilePicker.svelte'
import Alert from './common/alert/Alert.svelte'
@@ -44,6 +45,7 @@
export let noControls: boolean = false
export let drawerOpen = false
export let nodeId: string | undefined = undefined
export let language: string | undefined = undefined
const IMG_MAX_SIZE = 10000000
const TABLE_MAX_SIZE = 5000000
@@ -68,6 +70,7 @@
| 'plain'
| 'markdown'
| 'map'
| 'nondisplayable'
| undefined
$: resultKind = inferResultKind(result)
@@ -119,9 +122,13 @@
let is_render_all = false
let download_as_csv = false
function inferResultKind(result: any) {
if (result == 'WINDMILL_TOO_BIG') {
largeObject = true
return 'json'
try {
if (result == 'WINDMILL_TOO_BIG') {
largeObject = true
return 'json'
}
} catch (err) {
return 'nondisplayable'
}
if (result !== undefined) {
@@ -243,7 +250,12 @@
let s3FileViewer: S3FilePicker
function toJsonStr(result: any) {
return JSON.stringify(result ?? null, null, 4) ?? 'null'
try {
// console.log(result)
return JSON.stringify(result ?? null, null, 4) ?? 'null'
} catch (e) {
return 'error stringifying object: ' + e.toString()
}
}
function contentOrRootString(obj: string | { filename: string; content: string }) {
@@ -377,17 +389,15 @@
/>
{/each}</div
>
{:else}
<div
{:else if resultKind == 'nondisplayable'}<div class="text-red-400">Non displayable object</div
>{:else}<div
class="inline-highlight relative grow {['plain', 'markdown'].includes(resultKind ?? '')
? ''
: 'min-h-[200px]'}"
>
{#if result != undefined && length != undefined && largeObject != undefined}
<div class="flex justify-between items-center w-full">
<div class="text-tertiary text-sm">
{#if !hideAsJson && !['json', 's3object'].includes(resultKind ?? '') && typeof result === 'object'}
<ToggleButtonGroup
>{#if result != undefined && length != undefined && largeObject != undefined}<div
class="flex justify-between items-center w-full"
><div class="text-tertiary text-sm">
{#if !hideAsJson && !['json', 's3object'].includes(resultKind ?? '') && typeof result === 'object'}<ToggleButtonGroup
class="h-6"
selected={forceJson ? 'json' : resultKind?.startsWith('table-') ? 'table' : 'pretty'}
on:selected={(ev) => {
@@ -537,13 +547,24 @@
</div>
{:else if !forceJson && resultKind == 'error' && result?.error}
<div class="flex flex-col items-start">
<span class="text-red-500 font-semibold text-sm whitespace-pre-wrap"
<span class="text-red-500 pt-2 font-semibold !text-xs whitespace-pre-wrap"
>{#if result.error.name || result.error.message}{result.error.name}: {result.error
.message}{:else}{JSON.stringify(result.error, null, 4)}{/if}</span
>
<pre class="text-sm whitespace-pre-wrap text-primary">{result.error.stack ?? ''}</pre>
<pre class="text-xs pt-2 whitespace-pre-wrap text-primary"
>{result.error.stack ?? ''}</pre
>
<slot />
</div>
{#if language == 'bun'}
<div class="pt-20" />
<Alert size="xs" type="info" title="Seeing an odd error?">
Bun script are bundled for performance reasons. If you see an odd error that doesn't
appear when testing (which doesn't use bundling), try putting <code>//nobundling</code
> at the top of your script to disable bundling and feel free to mention it to the Windmill's
team.
</Alert>
{/if}
{:else if !forceJson && resultKind == 'approval'}<div class="flex flex-col gap-3 mt-2 mx-4">
<Button
color="green"
@@ -19,9 +19,10 @@
import { hubBaseUrlStore } from '$lib/stores'
import { CheckCircle2, Loader2, RotateCw, XCircle } from 'lucide-svelte'
import { hubPaths } from '$lib/hub'
const slackRecoveryHandler = 'hub/2430/slack/schedule-recovery-handler-slack'
const slackHandlerScriptPath = 'hub/6512/workspace-or-schedule-error-handler-slack'
const slackRecoveryHandler = hubPaths.slackRecoveryHandler
const slackHandlerScriptPath = hubPaths.slackErrorHandler
export let errorOrRecovery: 'error' | 'recovery'
export let isEditable: boolean
@@ -1,12 +1,11 @@
<script lang="ts">
import { FlowGraph } from './graph'
import type { FlowModule, FlowValue } from '$lib/gen'
import { createEventDispatcher } from 'svelte'
import { twMerge } from 'tailwind-merge'
import FlowGraphViewerStep from './FlowGraphViewerStep.svelte'
import FlowGraphV2 from './graph/FlowGraphV2.svelte'
export let flow: {
summary: string
@@ -30,7 +29,7 @@
class="{noSide ? 'col-span-3' : 'sm:col-span-2 col-span-3'} w-full border max-h-full"
class:overflow-auto={overflowAuto}
>
<FlowGraph
<FlowGraphV2
{download}
minHeight={400}
modules={flow?.value?.modules}
@@ -39,7 +38,6 @@
stepDetail = e.detail
dispatch('select', stepDetail)
}}
rebuildOnChange={flow}
/>
</div>
{/if}
@@ -10,6 +10,7 @@
import type { DurationStatus } from './graph'
import type { Writable } from 'svelte/store'
export let waitingForExecutor: boolean = false
export let result: any
export let logs: string | undefined
export let col: boolean = false
@@ -17,6 +18,7 @@
export let loading: boolean
export let filename: string | undefined = undefined
export let jobId: string | undefined = undefined
export let tag: string | undefined = undefined
export let workspaceId: string | undefined = undefined
export let refreshLog: boolean = false
export let durationStates: Writable<Record<string, DurationStatus>> | undefined
@@ -79,9 +81,9 @@
{/if}
</div>
<div class="overflow-auto {col ? '' : 'max-h-80'} relative">
<div class="absolute z-40 text-sm top-1.5 left-2"
<div class="absolute z-40 text-xs top-0 left-1"
><button class="" on:click={drawer.openDrawer}>explore all steps' logs</button></div
>
<LogViewer content={logs ?? ''} {jobId} isLoading={false} tag={undefined} />
<LogViewer content={logs ?? ''} {jobId} isLoading={waitingForExecutor} {tag} />
</div>
</div>
@@ -11,6 +11,7 @@
export let workspaceId: string | undefined = undefined
export let flowStateStore: Writable<FlowState> = writable({})
export let selectedJobStep: string | undefined = undefined
export let hideFlowResult = false
export let isOwner = false
export let wideResults = false
@@ -45,6 +46,7 @@
</script>
<FlowStatusViewerInner
{hideFlowResult}
on:jobsLoaded={({ detail }) => {
let { job } = detail
if (job.script_path != lastScriptPath && job.script_path) {
@@ -16,12 +16,7 @@
import { Badge, Button, Tab } from './common'
import DisplayResult from './DisplayResult.svelte'
import Tabs from './common/tabs/Tabs.svelte'
import {
FlowGraph,
type DurationStatus,
type FlowStatusViewerContext,
type GraphModuleState
} from './graph'
import { type DurationStatus, type FlowStatusViewerContext, type GraphModuleState } from './graph'
import ModuleStatus from './ModuleStatus.svelte'
import { emptyString, msToSec, truncateRev } from '$lib/utils'
import JobArgs from './JobArgs.svelte'
@@ -33,6 +28,7 @@
import { writable, type Writable } from 'svelte/store'
import Alert from './common/alert/Alert.svelte'
import FlowGraphViewerStep from './FlowGraphViewerStep.svelte'
import FlowGraphV2 from './graph/FlowGraphV2.svelte'
const dispatch = createEventDispatcher()
@@ -69,6 +65,7 @@
export let reducedPolling = false
export let wideResults = false
export let hideFlowResult = false
let jobResults: any[] =
flowJobIds?.flowJobs?.map((x, id) => `iter #${id + 1} not loaded by frontend yet`) ?? []
@@ -170,7 +167,7 @@
mod.type === 'WaitingForEvents' &&
$localModuleStates?.[innerModules?.[i - 1]?.id ?? '']?.type == 'Success'
) {
setModuleState(mod.id ?? '', { type: mod.type, args: job?.args })
setModuleState(mod.id ?? '', { type: mod.type, args: job?.args, tag: job?.tag })
} else if (
mod.type === 'WaitingForExecutor' &&
$localModuleStates[mod.id ?? '']?.scheduled_for == undefined
@@ -186,7 +183,8 @@
scheduled_for: job?.['scheduled_for'],
job_id: job?.id,
parent_module: mod['parent_module'],
args: job?.args
args: job?.args,
tag: job?.tag
}
setModuleState(mod.id ?? '', newState)
})
@@ -398,6 +396,7 @@
job_id: job.id,
logs: job.logs,
args: job.args,
tag: job.tag,
started_at,
parent_module: mod['parent_module']
},
@@ -416,6 +415,7 @@
logs: job.logs,
result: job['result'],
job_id: job.id,
tag: job.tag,
parent_module: mod['parent_module'],
duration_ms: job['duration_ms'],
started_at: started_at,
@@ -590,6 +590,7 @@
let stepDetail: FlowModule | string | undefined = undefined
let storedListJobs: Record<number, Job> = {}
let wrapperHeight: number = 0
</script>
{#if notAnonynmous}
@@ -642,16 +643,19 @@
<Loader2 class="animate-spin" />
</div>
{:else if `result` in job}
<div class="w-full h-full">
<FlowJobResult
workspaceId={job?.workspace_id}
jobId={job?.id}
loading={job['running'] == true}
result={job.result}
logs={job.logs}
durationStates={localDurationStatuses}
/>
</div>
{#if !hideFlowResult}
<div class="w-full h-full">
<FlowJobResult
workspaceId={job?.workspace_id}
jobId={job?.id}
tag={job?.tag}
loading={job['running'] == true}
result={job.result}
logs={job.logs}
durationStates={localDurationStatuses}
/>
</div>
{/if}
{:else if job.flow_status?.modules?.[job?.flow_status?.step]?.type === 'WaitingForEvents'}
<FlowStatusWaitingForEvents {workspaceId} {job} {isOwner} />
{:else if $suspendStatus && Object.keys($suspendStatus).length > 0}
@@ -898,7 +902,7 @@
{#if render}
{#if job.raw_flow && !isListJob}
<div class="{selected != 'graph' ? 'hidden' : ''} grow mt-4">
<div class="grid grid-cols-3 border h-full">
<div class="grid grid-cols-3 border h-full" bind:clientHeight={wrapperHeight}>
<div class="col-span-2 bg-surface-secondary">
<div class="flex flex-col">
{#each Object.values($retryStatus) as count}
@@ -917,8 +921,9 @@
{/each}
</div>
<FlowGraph
<FlowGraphV2
download
minHeight={wrapperHeight}
success={jobId != undefined && isSuccess(job?.['success'])}
flowModuleStates={$localModuleStates}
on:select={(e) => {
@@ -943,6 +948,7 @@
}}
on:selectedIteration={(e) => {
let detail = e.detail
setModuleState(detail.moduleId, {
selectedForloop: detail.id,
selectedForloopIndex: detail.index
@@ -950,7 +956,6 @@
globalRefreshes[detail.moduleId]?.({ job: detail.id, index: detail.index })
}}
modules={job.raw_flow?.modules ?? []}
failureModule={job.raw_flow?.failure_module}
/>
</div>
<div
@@ -984,6 +989,7 @@
jobId={job?.id}
filename={job.id}
loading={job['running']}
tag={job?.tag}
noBorder
col
result={job['result']}
@@ -1047,14 +1053,17 @@
/>
</div>
{/if}
<FlowJobResult
workspaceId={job?.workspace_id}
jobId={node.job_id}
noBorder
loading={node.type != 'Success' && node.type != 'Failure'}
waitingForExecutor={node.type == 'WaitingForExecutor'}
refreshLog={node.type == 'InProgress'}
col
result={node.result}
tag={node.tag}
logs={node.logs}
durationStates={localDurationStatuses}
/>
@@ -73,7 +73,7 @@
}}
iconOnly
/>
{#if code?.length < 5000}
{#if code?.length < 10000}
{#if !lines}
<Highlight class="nowrap {$$props.class}" language={lang} {code} />
{:else}
@@ -82,7 +82,7 @@
</Highlight>
{/if}
{:else}
<pre class="overflow-auto max-h-screen {$$props.class}"
<pre class="overflow-auto max-h-screen text-xs {$$props.class}"
><code class="language-{language}">{code}</code></pre
>
{/if}
@@ -718,6 +718,7 @@
/>
{:else if setting.fieldType == 'password'}
<input
autocomplete="new-password"
type="password"
placeholder={setting.placeholder}
bind:value={values[setting.key]}
@@ -181,9 +181,17 @@
}
let prevDefaultValue: any = undefined
let defaultChange = 0
async function changeDefaultValue(inputCat, defaultValue) {
if (value == null || value == undefined || deepEqual(value, prevDefaultValue)) {
if (
value == null ||
value == undefined ||
deepEqual(value, prevDefaultValue) ||
(prevDefaultValue != undefined && !deepEqual(defaultValue, prevDefaultValue))
) {
value = defaultValue
defaultChange += 1
}
prevDefaultValue = structuredClone(defaultValue)
@@ -498,11 +506,13 @@
{/each}
</select>
{:else if inputCat == 'date'}
{#if format === 'date'}
<DateInput bind:value dateFormat={extra['dateFormat']} />
{:else}
<DateTimeInput useDropdown bind:value />
{/if}
{#key defaultChange}
{#if format === 'date'}
<DateInput bind:value dateFormat={extra['dateFormat']} />
{:else}
<DateTimeInput useDropdown bind:value />
{/if}
{/key}
{:else if inputCat == 'base64'}
<div class="flex flex-col my-6 w-full">
<input
@@ -71,7 +71,10 @@
}
</script>
<div class={twMerge('w-full flex flex-col px-0.5 pb-2', largeGap ? 'gap-8' : 'gap-2')}>
<div
class={twMerge('w-full flex flex-col px-0.5 pb-2', largeGap ? 'gap-8' : 'gap-2')}
on:pointerdown
>
{#each keys as argName (argName)}
{#if typeof args == 'object' && schema?.properties[argName] && args}
<LightweightArgInput
@@ -96,10 +96,6 @@
return content
}
$: if (content != undefined && isLoading) {
isLoading = false
}
$: truncatedContent && scrollToBottom()
$: html = ansi_up.ansi_to_html(
+72 -85
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { goto } from '$lib/navigation'
import Github from '$lib/components/icons/brands/Github.svelte'
import Gitlab from '$lib/components/icons/brands/Gitlab.svelte'
import Google from '$lib/components/icons/brands/Google.svelte'
@@ -6,7 +7,7 @@
import Okta from '$lib/components/icons/brands/Okta.svelte'
import { OauthService, UserService, WorkspaceService } from '$lib/gen'
import { usersWorkspaceStore, workspaceStore, userStore, enterpriseLicense } from '$lib/stores'
import { usersWorkspaceStore, workspaceStore, userStore } from '$lib/stores'
import { classNames, emptyString, parseQueryParams } from '$lib/utils'
import { base } from '$lib/base'
import { getUserExt } from '$lib/user'
@@ -21,10 +22,6 @@
export let password: string | undefined = undefined
export let error: string | undefined = undefined
export let popup: boolean = false
export let loginPasswordRequireEEOnPublicApps: boolean = false
export let gotoFn: ((path: string, opt?: Record<string, any> | undefined) => void) | undefined =
undefined
export let baseApiUrl: string | undefined = undefined
const providers = [
{
@@ -86,19 +83,15 @@
}
// Finally, we check whether the user is a superadmin
refreshSuperadmin(gotoFn)
if (gotoFn) {
redirectUser()
} else {
dispatch('login')
}
refreshSuperadmin()
redirectUser()
}
async function redirectUser() {
const firstTimeCookie =
document.cookie.match('(^|;)\\s*first_time\\s*=\\s*([^;]+)')?.pop() || '0'
if (Number(firstTimeCookie) > 0 && email === 'admin@windmill.dev') {
gotoFn?.('/user/first-time')
goto('/user/first-time')
return
}
@@ -107,12 +100,12 @@
return
}
if ($workspaceStore) {
gotoFn?.(rd ?? '/')
goto(rd ?? '/')
} else {
let workspaceTarget = parseQueryParams(rd ?? undefined)['workspace']
if (rd && workspaceTarget) {
$workspaceStore = workspaceTarget
gotoFn?.(rd)
goto(rd)
return
}
@@ -133,19 +126,19 @@
workspace: $workspaceStore!
})
if (!emptyString(defaultApp.default_app_path)) {
gotoFn?.(`/apps/get/${defaultApp.default_app_path}`)
goto(`/apps/get/${defaultApp.default_app_path}`)
} else {
gotoFn?.(rd ?? '/')
goto(rd ?? '/')
}
} else {
gotoFn?.(rd ?? '/')
goto(rd ?? '/')
}
} else if (rd?.startsWith('/user/workspaces')) {
gotoFn?.(rd)
goto(rd)
} else if (rd == '/#user-settings') {
gotoFn?.(`/user/workspaces#user-settings`)
goto(`/user/workspaces#user-settings`)
} else {
gotoFn?.(`/user/workspaces${rd ? `?rd=${encodeURIComponent(rd)}` : ''}`)
goto(`/user/workspaces${rd ? `?rd=${encodeURIComponent(rd)}` : ''}`)
}
}
}
@@ -269,74 +262,68 @@
{/if}
{#if showPassword}
{#if loginPasswordRequireEEOnPublicApps && !$enterpriseLicense}
<p class="text-xs text-red-400 italic p-6">
To log in with email and password on a public app, Windmill requires an enterprise license
</p>
{:else}
<div>
<div class="space-y-6">
{#if isCloudHosted()}
<p class="text-xs text-tertiary italic pb-6">
To get credentials without the OAuth providers above, send an email at
contact@windmill.dev
</p>
{/if}
<div>
<div class="space-y-6">
{#if isCloudHosted()}
<p class="text-xs text-tertiary italic pb-6">
To get credentials without the OAuth providers above, send an email at
contact@windmill.dev
</p>
{/if}
<div>
<label for="email" class="block text-sm font-medium leading-6 text-primary">
Email
</label>
<div>
<label for="email" class="block text-sm font-medium leading-6 text-primary">
Email
</label>
<div>
<input
type="email"
bind:value={email}
id="email"
autocomplete="email"
class="block w-full rounded-md border-0 py-1.5 text-primary shadow-sm ring-1 ring-inset placeholder:text-secondary focus:ring-2 focus:ring-inset focus:ring-frost-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<label for="password" class="block text-sm font-medium leading-6 text-primary">
Password
</label>
<div>
<input
on:keyup={handleKeyUp}
bind:value={password}
id="password"
type="password"
autocomplete="current-password"
class="block w-full rounded-md border-0 py-1.5 text-shadow shadow-sm ring-1 ring-inset placeholder:text-secondary focus:ring-2 focus:ring-inset focus:ring-frost-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div class="pt-2">
<button
on:click={login}
disabled={!email || !password}
class="flex w-full justify-center rounded-md bg-frost-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-frost-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-frost-600"
>
Sign in
</button>
<input
type="email"
bind:value={email}
id="email"
autocomplete="email"
class="block w-full rounded-md border-0 py-1.5 text-primary shadow-sm ring-1 ring-inset placeholder:text-secondary focus:ring-2 focus:ring-inset focus:ring-frost-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
</div>
{/if}
{/if}
{#if isCloudHosted()}
<p class="text-2xs text-tertiary italic mt-10 text-center">
By logging in, you agree to our
<a href="https://windmill.dev/terms_of_service" target="_blank" rel="noreferrer">
Terms of Service
</a>
and
<a href="https://windmill.dev/privacy_policy" target="_blank" rel="noreferrer">
Privacy Policy
</a>
</p>
<div>
<label for="password" class="block text-sm font-medium leading-6 text-primary">
Password
</label>
<div>
<input
on:keyup={handleKeyUp}
bind:value={password}
id="password"
type="password"
autocomplete="current-password"
class="block w-full rounded-md border-0 py-1.5 text-shadow shadow-sm ring-1 ring-inset placeholder:text-secondary focus:ring-2 focus:ring-inset focus:ring-frost-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div class="pt-2">
<button
on:click={login}
disabled={!email || !password}
class="flex w-full justify-center rounded-md bg-frost-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-frost-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-frost-600"
>
Sign in
</button>
</div>
</div>
{#if isCloudHosted()}
<p class="text-2xs text-tertiary italic mt-10 text-center">
By logging in, you agree to our
<a href="https://windmill.dev/terms_of_service" target="_blank" rel="noreferrer">
Terms of Service
</a>
and
<a href="https://windmill.dev/privacy_policy" target="_blank" rel="noreferrer">
Privacy Policy
</a>
</p>
{/if}
</div>
{/if}
</div>
@@ -2,6 +2,7 @@
import { createPopperActions } from 'svelte-popperjs'
import type { PopoverPlacement } from './Popover.model'
import Portal from '$lib/components/Portal.svelte'
import { fade } from 'svelte/transition'
import { twMerge } from 'tailwind-merge'
@@ -138,7 +138,7 @@
duration={testJob?.['duration_ms']}
mem={testJob?.['mem_peak']}
content={testJob?.logs}
isLoading={testIsLoading}
isLoading={testIsLoading && testJob?.['running'] == false}
tag={testJob?.tag}
/>
</Pane>
@@ -50,7 +50,9 @@
}
function plugIt(argName: string) {
args[argName] = evalValue(argName, mod, testStepStore, pickableProperties, true)
args[argName] = structuredClone(
evalValue(argName, mod, testStepStore, pickableProperties, true)
)
try {
editor?.[argName]?.setCode(JSON.stringify(args[argName], null, 4))
} catch {
@@ -1,6 +1,8 @@
<script lang="ts">
import JsonEditor from './apps/editor/settingsPanel/inputEditor/JsonEditor.svelte'
import ResourcePicker from './ResourcePicker.svelte'
import S3ObjectPicker from './S3ObjectPicker.svelte'
import SimpleEditor from './SimpleEditor.svelte'
export let format: string
export let value: any
@@ -8,6 +10,7 @@
export let showSchemaExplorer = false
export let selectFirst = false
export let defaultValue: any
export let editor: SimpleEditor | undefined = undefined
function isString(value: any) {
return typeof value === 'string' || value instanceof String
@@ -39,7 +42,7 @@
<div class="flex flex-row w-full flex-wrap gap-x-2 gap-y-0.5">
{#if format === 'resource-s3_object'}
<S3ObjectPicker bind:value />
{:else}
{:else if value == undefined || typeof value === 'string'}
<ResourcePicker
{selectFirst}
{disablePortal}
@@ -52,5 +55,7 @@
resourceType={format.split('-').length > 1 ? format.substring('resource-'.length) : undefined}
{showSchemaExplorer}
/>
{:else}
<JsonEditor bind:editor code={JSON.stringify(value, null, 2)} bind:value />
{/if}
</div>
@@ -148,7 +148,7 @@
<span class="text-tertiary text-2xs"
>If left empty, will be derived automatically from $AWS_SECRET_KEY, pod or ec2 profile</span
>
<input type="password" bind:value={bucket_config.secret_key} />
<input type="password" autocomplete="new-password" bind:value={bucket_config.secret_key} />
</label>
<label class="block pb-2">
<span class="text-primary font-semibold text-sm">Endpoint</span>
@@ -174,7 +174,7 @@
</label>
<label class="block pb-2">
<span class="text-primary font-semibold text-sm">Access Key</span>
<input type="password" bind:value={bucket_config.accessKey} />
<input type="password" autocomplete="new-password" bind:value={bucket_config.accessKey} />
</label>
<label class="block pb-2">
<span class="text-primary font-semibold text-sm"
+1 -1
View File
@@ -48,7 +48,7 @@
type="password"
bind:value={password}
on:keydown
autocomplete="off"
autocomplete="new-password"
{placeholder}
{disabled}
/>
@@ -2,6 +2,7 @@
import { createPopperActions, type PopperOptions } from 'svelte-popperjs'
import type { PopoverPlacement } from './Popover.model'
import Portal from '$lib/components/Portal.svelte'
import { ExternalLink } from 'lucide-svelte'
export let placement: PopoverPlacement = 'bottom-end'
+2 -2
View File
@@ -29,11 +29,11 @@
}. Allowed types: string (CSS selector) or HTMLElement.`
)
}
targetEl.appendChild(el)
el.hidden = false
if (!el.classList.contains('windmill-app')) {
el.classList.add('windmill-app')
}
targetEl.appendChild(el)
el.hidden = false
}
function destroy() {
@@ -10,6 +10,7 @@
import DarkModeObserver from './DarkModeObserver.svelte'
import { Pen, Plus, RotateCw } from 'lucide-svelte'
import ResourceEditorDrawer from './ResourceEditorDrawer.svelte'
import { sendUserToast } from '$lib/toast'
const dispatch = createEventDispatcher()
@@ -42,30 +43,38 @@
appConnect?.open?.(resourceType, expressOAuthSetup)
}
let loading = true
async function loadResources(resourceType: string | undefined) {
const nc = (
await ResourceService.listResource({
workspace: $workspaceStore!,
resourceType
})
)
.filter((x) => x.resource_type != 'state' && x.resource_type != 'cache')
.map((x) => ({
value: x.path,
label: x.path,
type: x.resource_type
}))
loading = true
try {
const nc = (
await ResourceService.listResource({
workspace: $workspaceStore!,
resourceType
})
)
.filter((x) => x.resource_type != 'state' && x.resource_type != 'cache')
.map((x) => ({
value: x.path,
label: x.path,
type: x.resource_type
}))
// TODO check if this is needed
if (!nc.find((x) => x.value == value) && (initialValue || value)) {
nc.push({ value: value ?? initialValue!, label: value ?? initialValue!, type: '' })
}
collection = nc
if (collection.length == 1 && selectFirst && valueSelect == undefined) {
value = collection[0].value
valueType = collection[0].type
valueSelect = collection[0]
// TODO check if this is needed
if (!nc.find((x) => x.value == value) && (initialValue || value)) {
nc.push({ value: value ?? initialValue!, label: value ?? initialValue!, type: '' })
}
collection = nc
if (collection.length == 1 && selectFirst && valueSelect == undefined) {
value = collection[0].value
valueType = collection[0].type
valueSelect = collection[0]
}
} catch (e) {
sendUserToast('Failed to load resource types', true)
console.error(e)
}
loading = false
}
$: $workspaceStore && loadResources(resourceType)
@@ -131,46 +140,48 @@
? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
: SELECT_INPUT_DEFAULT_STYLE.containerStyles}
/>
{:else}
{:else if !loading}
<div class="text-2xs text-tertiary mr-2">0 found</div>
{/if}
{#if value && value != ''}
<Button
{disabled}
color="light"
variant="border"
size="xs"
on:click={() => resourceEditor?.initEdit?.(value ?? '')}
startIcon={{ icon: Pen }}
iconOnly
/>
{/if}
{#if resourceType?.includes(',')}
{#each resourceType.split(',') as rt}
{#if !loading}
{#if value && value != ''}
<Button
{disabled}
color="light"
variant="border"
size="xs"
on:click={() => appConnect?.open?.(rt)}
startIcon={{ icon: Plus }}>{rt}</Button
on:click={() => resourceEditor?.initEdit?.(value ?? '')}
startIcon={{ icon: Pen }}
iconOnly
/>
{/if}
{#if resourceType?.includes(',')}
{#each resourceType.split(',') as rt}
<Button
{disabled}
color="light"
variant="border"
size="xs"
on:click={() => appConnect?.open?.(rt)}
startIcon={{ icon: Plus }}>{rt}</Button
>
{/each}
{:else}
<Button
{disabled}
color="light"
variant="border"
size="xs"
on:click={() => appConnect?.open?.(resourceType, expressOAuthSetup)}
startIcon={{ icon: Plus }}
iconOnly={collection?.length > 0}
>{#if collection?.length == 0}
Add a {resourceType} resource
{/if}</Button
>
{/each}
{:else}
<Button
{disabled}
color="light"
variant="border"
size="xs"
on:click={() => appConnect?.open?.(resourceType, expressOAuthSetup)}
startIcon={{ icon: Plus }}
iconOnly={collection?.length > 0}
>{#if collection?.length == 0}
Add a {resourceType} resource
{/if}</Button
>
{/if}
{/if}
<Button
@@ -3,9 +3,10 @@
import type { Writable } from 'svelte/store'
import type { GroupContext } from '../types'
export let groupContext: Writable<Record<string, any>>
export let context: Writable<Record<string, any>>
export let id: string
setContext<GroupContext>('GroupContext', groupContext)
setContext<GroupContext>('GroupContext', { id, context })
</script>
<slot />
@@ -126,20 +126,23 @@
<div
class={twMerge('p-2 overflow-auto h-full', css?.container?.class, 'wm-schema-form')}
style={css?.container?.style}
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
>
<LightweightSchemaForm
defaultValues={resolvedConfig.defaultValues}
dynamicEnums={resolvedConfig.dynamicEnums}
schema={result}
bind:isValid={valid}
bind:args
bind:this={schemaForm}
displayType={Boolean(resolvedConfig.displayType)}
largeGap={Boolean(resolvedConfig.largeGap)}
{css}
/>
<div
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
>
<LightweightSchemaForm
defaultValues={resolvedConfig.defaultValues}
dynamicEnums={resolvedConfig.dynamicEnums}
schema={result}
bind:isValid={valid}
bind:args
bind:this={schemaForm}
displayType={Boolean(resolvedConfig.displayType)}
largeGap={Boolean(resolvedConfig.largeGap)}
{css}
/>
</div>
</div>
{:else}
<p class="m-2 italic">Empty form (no property)</p>
@@ -98,7 +98,7 @@
duration={testJob?.['duration_ms']}
mem={testJob?.['mem_peak']}
content={testJob?.logs}
isLoading={testIsLoading}
isLoading={testIsLoading && testJob?.['running'] == false}
tag={testJob?.tag}
/>
</div>
@@ -21,7 +21,7 @@
export let render: boolean
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
const { app, worldStore, policy } = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['recomputeallcomponent'].initialData.configuration,
@@ -59,7 +59,7 @@
<InitializeComponent {id} />
<AlignWrapper {horizontalAlignment}>
{#if render}
{#if render && policy}
<RecomputeAllWrapper
containerClass={css?.container?.class}
containerStyle={css?.container?.style}
@@ -26,6 +26,7 @@
import { Drawer, DrawerContent } from '$lib/components/common'
import InsertRow from './InsertRow.svelte'
import Portal from '$lib/components/Portal.svelte'
import { sendUserToast } from '$lib/toast'
import type { AppInput, StaticInput } from '$lib/components/apps/inputType'
import DbExplorerCount from './DbExplorerCount.svelte'
@@ -126,20 +126,21 @@
['ContextPanel', contextPanel]
])
new AppAggridTableActions({
const ta = new AppAggridTableActions({
target: c.eGui,
props: {
p,
id: id,
actions,
rowIndex,
row,
render: true,
wrapActions: resolvedConfig.wrapActions,
selectRow: () => {
selectRow: (p) => {
toggleRow(p)
p.node.setSelected(true)
},
onSet: (id, value) => {
onSet: (id, value, rowIndex) => {
if (!inputs[id]) {
inputs[id] = { [rowIndex]: value }
} else {
@@ -148,7 +149,7 @@
outputs?.inputs.set(inputs, true)
},
onRemove: (id) => {
onRemove: (id, rowIndex) => {
if (inputs?.[id] == undefined) {
return
}
@@ -163,6 +164,14 @@
},
context: componentContext
})
return {
destroy: () => {
ta.$destroy()
},
refresh(params) {
ta.$set({ rowIndex: params.node.rowIndex ?? 0, row: params.data, p: params })
}
}
})
function transformColumnDefs(columnDefs: any[] | undefined) {
@@ -184,7 +193,7 @@
field: 'delete',
headerName: 'Delete',
cellRenderer: cellRendererFactory((c, p) => {
new Button({
let ta = new Button({
target: c.eGui,
props: {
btnClasses: 'w-12',
@@ -197,6 +206,14 @@
nonCaptureEvent: true
}
})
return {
destroy: () => {
ta.$destroy()
},
refresh(params) {
//
}
}
}),
cellRendererParams: {
onClick: (e) => {
@@ -123,7 +123,7 @@
}
if (!deepEqual(outputs?.selectedRow?.peak(), data)) {
outputs?.selectedRow.set(data)
outputs?.selectedRow?.set(data)
}
if (iterContext && listInputs) {
@@ -202,20 +202,21 @@
['ContextPanel', contextPanel]
])
new AppAggridTableActions({
let ta = new AppAggridTableActions({
target: c.eGui,
props: {
p,
id: id,
actions,
rowIndex,
row,
render,
wrapActions: resolvedConfig.wrapActions,
selectRow: () => {
selectRow: (p) => {
toggleRow(p)
p.node.setSelected(true)
},
onSet: (id, value) => {
onSet: (id, value, rowIndex) => {
if (!inputs[id]) {
inputs[id] = { [rowIndex]: value }
} else {
@@ -224,7 +225,7 @@
outputs?.inputs.set(inputs, true)
},
onRemove: (id) => {
onRemove: (id, rowIndex) => {
if (inputs?.[id] == undefined) {
return
}
@@ -239,6 +240,15 @@
},
context: componentContext
})
return {
destroy: () => {
ta.$destroy()
},
refresh(params) {
ta.$set({ rowIndex: params.node.rowIndex ?? 0, row: params.data, p: params })
}
}
})
function mountGrid() {
@@ -16,16 +16,18 @@
import ComponentOutputViewer from '$lib/components/apps/editor/contextPanel/ComponentOutputViewer.svelte'
import { connectOutput } from '$lib/components/apps/editor/appUtils'
import RowWrapper from '../../layout/RowWrapper.svelte'
import type { ICellRendererParams } from 'ag-grid-community'
export let p: ICellRendererParams<any>
export let id: string
export let render: boolean
export let actions: TableAction[] = []
export let rowIndex: number
export let row: { original: Record<string, any> }
export let onSet: (id: string, value: any) => void
export let onRemove: (id: string) => void
export let onSet: (id: string, value: any, rowIndex: number) => void
export let onRemove: (id: string, rowIndex: number) => void
export let wrapActions: boolean | undefined = undefined
export let selectRow: () => void
export let selectRow: (params: ICellRendererParams<any>) => void
const dispatch = createEventDispatcher()
const { selectedComponent, hoverStore, mode, connectingInput } =
@@ -39,12 +41,25 @@
const parent = rowDiv.parentElement?.parentElement?.parentElement
if (parent) {
parent.classList.add('w-full')
} else {
//sometimes the parent is not available immediately
setTimeout(() => {
const parent = rowDiv?.parentElement?.parentElement?.parentElement
if (parent) {
parent.classList.add('w-full')
}
}, 10)
}
}
})
</script>
<RowWrapper value={row} index={rowIndex} {onSet} {onRemove}>
<RowWrapper
value={row}
index={rowIndex}
onSet={(id, value) => onSet(id, value, rowIndex)}
onRemove={(id) => onRemove(id, rowIndex)}
>
<div
class={twMerge(
'flex flex-row justify-center items-center gap-4 h-full px-4 py-1 w-full',
@@ -68,7 +83,7 @@
}
}}
on:pointerdown|stopPropagation={(e) => {
selectRow()
selectRow(p)
if (!$connectingInput.opened) {
$selectedComponent = [action.id]
@@ -170,7 +185,7 @@
noWFull
preclickAction={async () => {
dispatch('toggleRow')
selectRow()
selectRow(p)
}}
id={action.id}
customCss={action.customCss}
@@ -196,7 +211,7 @@
onToggle={action.onToggle}
preclickAction={async () => {
dispatch('toggleRow')
selectRow()
selectRow(p)
}}
verticalAlignment="center"
{controls}
@@ -215,7 +230,7 @@
onSelect={action.onSelect}
preclickAction={async () => {
dispatch('toggleRow')
selectRow()
selectRow(p)
}}
{controls}
/>
@@ -227,7 +242,7 @@
{render}
preclickAction={async () => {
dispatch('toggleRow')
selectRow()
selectRow(p)
}}
noWFull
id={action.id}
@@ -252,7 +267,7 @@
onToggle={action.onToggle}
preclickAction={async () => {
dispatch('toggleRow')
selectRow()
selectRow(p)
}}
/>
{:else if action.type == 'selectcomponent'}
@@ -269,7 +284,7 @@
onSelect={action.onSelect}
preclickAction={async () => {
dispatch('toggleRow')
selectRow()
selectRow(p)
}}
/>
{/if}
@@ -80,7 +80,11 @@
{:else}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:dblclick={toggleEdit}>
{value}
{#if typeof value == 'object'}
{JSON.stringify(value)}
{:else}
{value}
{/if}
</div>
{/if}
</td>
@@ -28,8 +28,11 @@
for (let i = 0; i < objArray.length; i++) {
let line = ''
for (let j = 0; j < headers.length; j++) {
const value = objArray[i][headers[j]]
let value = objArray[i][headers[j]]
line += j ? ',' : ''
if (typeof value != 'string') {
value = JSON.stringify(value)
}
line += /[\",\n]/.test(value) ? '"' + value.replace(/"/g, '""') + '"' : value
}
str += line + '\r\n'
@@ -11,18 +11,21 @@ import type { ICellRendererComp, ICellRendererParams } from 'ag-grid-community'
*/
export abstract class AbstractCellRenderer implements ICellRendererComp {
eGui: any
protected value: any
protected params: any
protected component:
| {
refresh: (params: ICellRendererParams) => void
destroy: () => void
}
| undefined
constructor(parentElement = 'span') {
// create empty span (or other element) to place svelte component in
this.component = undefined
this.eGui = document.createElement(parentElement)
}
init(params: ICellRendererParams & { onClick?: (data: any) => void }) {
this.value = params.value
this.createComponent(params)
this.component = this.createComponent(params)
this.eGui.addEventListener('click', () => params.onClick?.(params.data))
this.params = params
}
getGui() {
@@ -30,11 +33,13 @@ export abstract class AbstractCellRenderer implements ICellRendererComp {
}
refresh(params: ICellRendererParams) {
this.value = params.value
this.component?.refresh?.(params)
return true
}
destroy(): void {
this.component?.destroy?.()
}
/**
* Define and create the svelte component to use in the cell
* @example
@@ -48,7 +53,10 @@ export abstract class AbstractCellRenderer implements ICellRendererComp {
* }
* @param params params for rendering the call, including the value for the cell
*/
abstract createComponent(params: ICellRendererParams): void
abstract createComponent(params: ICellRendererParams): {
refresh: (params: ICellRendererParams) => void
destroy: () => void
}
}
/**
@@ -58,11 +66,20 @@ export abstract class AbstractCellRenderer implements ICellRendererComp {
* @returns
*/
export function cellRendererFactory(
svelteComponent: (cell: AbstractCellRenderer, params: ICellRendererParams) => void
svelteComponent: (
cell: AbstractCellRenderer,
params: ICellRendererParams
) => {
refresh: (params: ICellRendererParams) => void
destroy: () => void
}
) {
class Renderer extends AbstractCellRenderer {
createComponent(params: ICellRendererParams<any, any>): void {
svelteComponent(this, params)
createComponent(params: ICellRendererParams<any, any>): {
refresh: (params: ICellRendererParams) => void
destroy: () => void
} {
return svelteComponent(this, params)
}
}
return Renderer
@@ -43,10 +43,12 @@
let previousConnectedValues: Record<string, any> = {}
let groupStore = groupContext?.context
$: fullContext = {
iter: iterContext ? $iterContext : undefined,
row: rowContext ? $rowContext : undefined,
group: groupContext ? $groupContext : undefined
group: groupStore ? $groupStore : undefined
}
$: lastInput?.type == 'evalv2' &&
@@ -272,7 +274,8 @@
$componentControl,
$worldStore,
$runnableComponents,
false
false,
groupContext?.id
)
error = ''
return r
@@ -297,7 +300,8 @@
$componentControl,
$worldStore,
$runnableComponents,
false
false,
groupContext?.id
)
error = ''
return r
@@ -279,14 +279,15 @@
computeGlobalContext($worldStore, {
iter: iterContext ? $iterContext : undefined,
row: rowContext ? $rowContext : undefined,
group: groupContext ? $groupContext : undefined
group: groupContext ? get(groupContext.context) : undefined
}),
$state,
isEditor,
$componentControl,
$worldStore,
$runnableComponents,
true
true,
groupContext?.id
)
await setResult(r, job)
@@ -504,6 +505,7 @@
computeGlobalContext($worldStore, {
iter: iterContext ? $iterContext : undefined,
row: rowContext ? $rowContext : undefined,
group: groupContext ? get(groupContext.context) : undefined,
result: res
}),
$state,
@@ -511,7 +513,8 @@
$componentControl,
$worldStore,
$runnableComponents,
true
true,
groupContext?.id
)
return transformerResult
} catch (err) {
@@ -656,6 +659,18 @@
bgRuns.update((runs) => runs.filter((r) => r !== id))
}
}
function getError(obj: any) {
try {
if (obj?.error) {
return obj.error
}
return undefined
} catch (e) {
console.error('Error accessing error from result', e)
return undefined
}
}
</script>
{#each Object.entries(fields ?? {}) as [key, v] (key)}
@@ -750,7 +765,7 @@
<Alert type="warning" size="xs" class="mt-2 px-1" title="Missing runnable">
Please select a runnable
</Alert>
{:else if result?.error && $mode === 'preview' && !errorHandledByComponent}
{:else if getError(result) && $mode === 'preview' && !errorHandledByComponent}
<div
title="Error"
class={classNames(
@@ -25,11 +25,11 @@ function create_context_function_template(
) {
let hasReturnAsLastLine = noReturn || eval_string.split('\n').some((x) => x.startsWith('return '))
return `
return async function (context, state, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal, open, close, validate, invalidate, validateAll, clearFiles, showToast, waitJob, askNewResource) {
return async function (context, state, createProxy, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal, open, close, validate, invalidate, validateAll, clearFiles, showToast, waitJob, askNewResource) {
"use strict";
${
contextKeys && contextKeys.length > 0
? `let ${contextKeys.map((key) => ` ${key} = context['${key}']`)};`
? `let ${contextKeys.map((key) => ` ${key} = createProxy('${key}', context['${key}'])`)};`
: ``
}
${
@@ -46,6 +46,7 @@ return ${eval_string.startsWith('return ') ? eval_string.substring(7) : eval_str
type WmFunctor = (
context,
state,
createProxy,
goto,
setTab,
recompute,
@@ -113,30 +114,72 @@ export async function eval_like(
showToast?: (message: string, error?: boolean) => void
waitJob?: (jobId: string) => void
askNewResource?: () => void
setGroupValue?: (key: string, value: any) => void
}
>,
worldStore: World | undefined,
runnableComponents: Record<string, { cb?: (() => void)[] }>,
noReturn: boolean
noReturn: boolean,
groupContextId: string | undefined
) {
const proxiedState = new Proxy(state, {
set(target, key, value) {
if (typeof key !== 'string') {
throw new Error('Invalid key')
const createProxy = (name: string, obj: any) => {
// console.log('Creating proxy', name, obj)
if (obj != null && obj != undefined && typeof obj == 'object') {
if (name == 'group' && groupContextId) {
return createGroupProxy(groupContextId, obj)
}
target[key] = value
let o = worldStore?.newOutput('state', key, value)
o?.set(value, true)
return new Proxy(obj, {
set(target, key, value) {
if (name != 'state') {
throw new Error(
'Cannot set value on objects that are neither the global state or a container group field'
)
}
if (typeof key !== 'string') {
throw new Error('Invalid key')
}
target[key] = value
let o = worldStore?.newOutput(name, key, value)
o?.set(value, true)
return true
return true
},
get(obj, prop) {
if (name != 'state' && prop == 'group') {
return createGroupProxy(name, obj[prop])
} else {
return obj[prop]
}
}
})
} else {
return obj
}
})
}
const createGroupProxy = (name: string, obj: any) => {
return new Proxy(obj, {
set(target, key, value) {
target[key] = value
let o = worldStore?.newOutput(name, 'group', target)
o?.set(target, true)
if (typeof key !== 'string') {
throw new Error('Invalid key')
}
controlComponents[name]?.setGroupValue?.(key, value)
return true
}
})
}
const proxiedState = createProxy('state', state)
let evaluator = make_context_evaluator(text, Object.keys(context ?? {}), noReturn)
// console.log(i, j)
return await evaluator(
context,
proxiedState,
createProxy,
async (x, newTab) => {
if (newTab || editor) {
if (!newTab) {
@@ -10,6 +10,7 @@
// @ts-ignore
import MultiSelect from 'svelte-multiselect'
import Portal from '$lib/components/Portal.svelte'
import { createFloatingActions } from 'svelte-floating-ui'
import { extractCustomProperties } from '$lib/utils'
import { tick } from 'svelte'
@@ -9,6 +9,7 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
// @ts-ignore
import Portal from '$lib/components/Portal.svelte'
import { createFloatingActions } from 'svelte-floating-ui'
import { extractCustomProperties } from '$lib/utils'
import { tick } from 'svelte'
@@ -173,6 +173,7 @@
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
on:keydown|stopPropagation
type="password"
autocomplete="new-password"
bind:value
placeholder={resolvedConfig.placeholder}
disabled={resolvedConfig.disabled}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext } from 'svelte'
import { getContext, onMount } from 'svelte'
import { initOutput } from '../../editor/appUtils'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
@@ -18,7 +18,7 @@
export let render: boolean
export let groupFields: RichConfigurations | undefined = undefined
const { app, focusedGrid, selectedComponent, worldStore, connectingInput } =
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
let groupContext = writable({})
@@ -34,6 +34,17 @@
}
}
onMount(() => {
$componentControl[id] = {
setGroupValue: (field: string, value: any) => {
groupContext.update((group) => {
group[field] = value
return group
})
}
}
})
let css = initCss($app.css?.containercomponent, customCss)
</script>
@@ -57,7 +68,7 @@
<div class="w-full h-full">
{#if $app.subgrids?.[`${id}-0`]}
<GroupWrapper {groupContext}>
<GroupWrapper {id} context={groupContext}>
<SubGridEditor
visible={render}
{id}
@@ -3,6 +3,7 @@
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
import Portal from '$lib/components/Portal.svelte'
import { initCss } from '../../utils'
import { Button, Drawer, DrawerContent } from '$lib/components/common'
import { twMerge } from 'tailwind-merge'
@@ -8,6 +8,7 @@
import { initConfig, initOutput, maxHeight, ROW_GAP_Y, ROW_HEIGHT } from '../../editor/appUtils'
import InitializeComponent from '../helpers/InitializeComponent.svelte'
import Portal from '$lib/components/Portal.svelte'
import { clickOutside } from '$lib/utils'
import { X } from 'lucide-svelte'
import { components } from '../../editor/component'
@@ -119,8 +119,8 @@
email: $userStore?.email,
groups: $userStore?.groups,
username: $userStore?.username,
query: Object.fromEntries(new URLSearchParams(window.location.search).entries()),
hash: window.location.hash,
query: Object.fromEntries($page.url.searchParams.entries()),
hash: $page.url.hash,
workspace: $workspaceStore,
mode: 'editor',
summary: $summaryStore,
@@ -168,6 +168,7 @@
previewTheme,
debuggingComponents: writable({}),
replaceStateFn: (path) => replaceState(path, $page.state),
policy: policy,
recomputeAllContext: writable({
loading: false,
componentNumber: 0,
@@ -217,10 +218,6 @@
function hashchange(e: HashChangeEvent) {
context.hash = e.newURL.split('#')[1]
context = context
worldStore.update((x) => {
x.outputsById?.['ctx']?.['hash'].set(context.hash, true)
return x
})
}
$: context.mode = $mode == 'dnd' ? 'editor' : 'viewer'
@@ -573,7 +570,7 @@
app={$appStore}
appPath={path}
{breakpoint}
author={policy.on_behalf_of_email ?? ''}
{policy}
isEditor
{context}
noBackend={false}
@@ -938,17 +938,19 @@
<h2>Public URL</h2>
<div class="mt-4" />
<Toggle
options={{
left: `Require login and read-access`,
right: `No login required`
}}
checked={policy.execution_mode == 'anonymous'}
on:change={(e) => {
policy.execution_mode = e.detail ? 'anonymous' : 'publisher'
setPublishState()
}}
/>
<div class="flex gap-2 items-center">
<Toggle
options={{
left: `Require login and read-access`,
right: `No login required`
}}
checked={policy.execution_mode == 'anonymous'}
on:change={(e) => {
policy.execution_mode = e.detail ? 'anonymous' : 'publisher'
setPublishState()
}}
/>
</div>
<div class="my-6 box">
Public url:
@@ -975,10 +977,14 @@
of embedding app must be the same as the one of Windmill)</div
>
</div>
<Alert type="info" title="Only latest deployed app is publicly available">
You will still need to deploy the app to make visible the latest changes
</Alert>
<a
href="https://www.windmill.dev/docs/advanced/external_auth_with_jwt#embed-public-apps-using-your-own-authentification"
class="mt-4 text-2xs">Embed this app in your own product to be used by your own users</a
>
{/if}
</DrawerContent>
</Drawer>
@@ -1147,14 +1153,14 @@
duration={job?.['duration_ms']}
jobId={job?.id}
content={job?.logs}
isLoading={testIsLoading}
isLoading={testIsLoading && job?.['running'] == false}
tag={job?.tag}
/>
</Pane>
<Pane size={50} minSize={10} class="text-sm text-secondary">
{#if job != undefined && 'result' in job && job.result != undefined}
<div class="relative h-full px-2">
<DisplayResult
{#if job != undefined && 'result' in job && job.result != undefined}<div
class="relative h-full px-2"
><DisplayResult
workspaceId={$workspaceStore}
jobId={selectedJobId}
result={job.result}
@@ -9,6 +9,7 @@
EditorBreakpoint,
EditorMode
} from '../types'
import type { Policy } from '$lib/gen'
import Button from '../../common/button/Button.svelte'
import { Unlock } from 'lucide-svelte'
import GridViewer from './GridViewer.svelte'
@@ -18,7 +19,7 @@
import { deepEqual } from 'fast-equals'
import { dfs, maxHeight } from './appUtils'
import { BG_PREFIX, migrateApp } from '../utils'
import { workspaceStore, enterpriseLicense, userStore } from '$lib/stores'
import { workspaceStore, enterpriseLicense } from '$lib/stores'
import DarkModeObserver from '$lib/components/DarkModeObserver.svelte'
import { getTheme } from './componentsPanel/themeUtils'
import HiddenComponent from '../components/helpers/HiddenComponent.svelte'
@@ -27,11 +28,11 @@
export let app: App
export let appPath: string = ''
export let breakpoint: Writable<EditorBreakpoint> = writable('lg')
export let author: string
export let policy: Policy = {}
export let summary: string = ''
export let workspace: string = $workspaceStore!
export let isEditor: boolean = false
export let context: Record<string, any>
export let noBackend: boolean = false
export let isLocked = false
export let hideRefreshBar = false
@@ -45,10 +46,6 @@
migrateApp(app)
let email: string = $userStore?.email ?? 'anonymous'
let groups: string[] = $userStore?.groups ?? []
let username: string = $userStore?.username ?? 'anonymous'
const appStore = writable<App>(app)
const selectedComponent = writable<string[] | undefined>(undefined)
const mode = writable<EditorMode>('preview')
@@ -62,15 +59,11 @@
const allIdsInPath = writable<string[]>([])
let ncontext: any = {
email,
groups,
username,
query: Object.fromEntries(new URLSearchParams(window.location.search).entries()),
hash: window.location.hash,
...context,
workspace,
mode: 'viewer',
summary: summary,
author
author: policy.on_behalf_of_email
}
function resizeWindow() {
@@ -87,15 +80,7 @@
let parentContext = getContext<AppViewerContext>('AppViewerContext')
let worldStore = buildWorld(ncontext)
$: onContextChange({
email,
groups,
username,
workspace,
summary: summary,
author
})
$: onContextChange(context)
function onContextChange(context: any) {
Object.assign(ncontext, context)
@@ -151,6 +136,7 @@
debuggingComponents: writable({}),
replaceStateFn,
gotoFn,
policy,
recomputeAllContext: writable({
loading: false,
componentNumber: 0,
@@ -253,7 +239,7 @@
<h2 class="truncate">{summary}</h2>
<RecomputeAllComponents />
<div class="text-2xs text-secondary">
{author != '' ? `on behalf of ${author}` : ''}
{policy.on_behalf_of ? `on behalf of ${policy.on_behalf_of_email}` : ''}
</div>
</div>
</div>
@@ -24,6 +24,7 @@
import { RotateCw, Save } from 'lucide-svelte'
import { CUSTOM_TAGS_SETTING, WORKSPACE_SLACK_BOT_TOKEN_PATH } from '$lib/consts'
import { loadSchemaFromPath } from '$lib/infer'
import { hubPaths } from '$lib/hub'
export let appPath: string
export let open = false
@@ -187,7 +188,7 @@ export async function main(app_path: string, startup_duration = 5, kind: 'pdf' |
const notificationScripts = {
discord: {
path: 'hub/7838/discord',
path: hubPaths.discordReport,
schema: {
type: 'object',
properties: {
@@ -203,7 +204,7 @@ export async function main(app_path: string, startup_duration = 5, kind: 'pdf' |
}
},
slack: {
path: 'hub/7836/slack', // if to be updated, also update it in in backend/windmill-queue/src/jobs.rs
path: hubPaths.slackReport, // if to be updated, also update it in in backend/windmill-queue/src/jobs.rs
schema: {
type: 'object',
properties: {
@@ -216,7 +217,7 @@ export async function main(app_path: string, startup_duration = 5, kind: 'pdf' |
}
},
email: {
path: 'hub/7837/smtp',
path: hubPaths.smtpReport,
schema: {
type: 'object',
properties: {
@@ -179,7 +179,7 @@
</Button>
</div>
</div>
<AppPreview author="" noBackend app={selected.value} />
<AppPreview noBackend app={selected.value} context={{}} />
{:else}
<Skeleton layout={[[40]]} />
{/if}
@@ -31,6 +31,7 @@
import { secondaryMenuLeft } from './settingsPanel/secondaryMenu'
import { clickOutside } from '$lib/utils'
import Portal from '$lib/components/Portal.svelte'
import { twMerge } from 'tailwind-merge'
let contextMenuVisible = false

Some files were not shown because too many files have changed in this diff Show More