@liiift-studio/deploy-vercel-from-sanity 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/index.js +22 -10
- package/dist/index.mjs +24 -12
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -366,6 +366,21 @@ See [CHANGELOG.md](./CHANGELOG.md).
|
|
|
366
366
|
|
|
367
367
|
---
|
|
368
368
|
|
|
369
|
+
## Tests and CI
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
npm test # unit tests
|
|
373
|
+
npm run lint # includes the compat-seam import rule
|
|
374
|
+
npm run typecheck
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Unit tests cover the proxy's authorization boundary (fail-closed status key,
|
|
378
|
+
per-project deployment scoping, role gating, SSRF guard on hook URLs) and the URL
|
|
379
|
+
validators that guard every anchor the plugin renders. `prepublishOnly` runs
|
|
380
|
+
typecheck and tests before building, so a release cannot ship past them.
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
369
384
|
## Contributing
|
|
370
385
|
|
|
371
386
|
Issues and pull requests welcome at [github.com/Liiift-Studio/Deploy-Vercel-from-Sanity](https://github.com/Liiift-Studio/Deploy-Vercel-from-Sanity).
|
package/dist/index.js
CHANGED
|
@@ -816,8 +816,14 @@ function DeployHistory({ target, token, onClose }) {
|
|
|
816
816
|
const [loading, setLoading] = (0, import_react7.useState)(true);
|
|
817
817
|
const [error, setError] = (0, import_react7.useState)(null);
|
|
818
818
|
const { projectId, hookId } = parseHookUrl(target.url);
|
|
819
|
-
const transport =
|
|
820
|
-
|
|
819
|
+
const transport = (0, import_react7.useMemo)(
|
|
820
|
+
() => pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token },
|
|
821
|
+
[pluginConfig.mode, pluginConfig.proxyUrl, pluginConfig.statusKey, token]
|
|
822
|
+
);
|
|
823
|
+
const targetRef = (0, import_react7.useMemo)(
|
|
824
|
+
() => ({ projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId }),
|
|
825
|
+
[projectId, hookId, target.proxyKey, target.teamId]
|
|
826
|
+
);
|
|
821
827
|
const load = (0, import_react7.useCallback)(async () => {
|
|
822
828
|
setLoading(true);
|
|
823
829
|
setError(null);
|
|
@@ -829,7 +835,7 @@ function DeployHistory({ target, token, onClose }) {
|
|
|
829
835
|
} finally {
|
|
830
836
|
setLoading(false);
|
|
831
837
|
}
|
|
832
|
-
}, [
|
|
838
|
+
}, [transport, targetRef]);
|
|
833
839
|
(0, import_react7.useEffect)(() => {
|
|
834
840
|
load();
|
|
835
841
|
}, [load]);
|
|
@@ -910,8 +916,14 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
910
916
|
const toast = useToast();
|
|
911
917
|
const pluginConfig = usePluginConfig();
|
|
912
918
|
const client = (0, import_sanity.useClient)({ apiVersion: "2025-01-01" });
|
|
913
|
-
const transport =
|
|
914
|
-
|
|
919
|
+
const transport = (0, import_react8.useMemo)(
|
|
920
|
+
() => pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token },
|
|
921
|
+
[pluginConfig.mode, pluginConfig.proxyUrl, pluginConfig.statusKey, token]
|
|
922
|
+
);
|
|
923
|
+
const targetRef = (0, import_react8.useMemo)(
|
|
924
|
+
() => ({ projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId }),
|
|
925
|
+
[projectId, hookId, target.proxyKey, target.teamId]
|
|
926
|
+
);
|
|
915
927
|
const [deployments, setDeployments] = (0, import_react8.useState)([]);
|
|
916
928
|
const [loadingInitial, setLoadingInitial] = (0, import_react8.useState)(true);
|
|
917
929
|
const [pendingSince, setPendingSince] = (0, import_react8.useState)(null);
|
|
@@ -939,7 +951,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
939
951
|
const isPending = pendingSince !== null;
|
|
940
952
|
const isActive = isPending || isActiveState(latest?.state);
|
|
941
953
|
const fetchDeployments2 = (0, import_react8.useCallback)(async () => {
|
|
942
|
-
const ready = transport.mode === "proxy" ? Boolean(
|
|
954
|
+
const ready = transport.mode === "proxy" ? Boolean(targetRef.proxyKey && transport.proxyUrl) : Boolean(targetRef.projectId && targetRef.hookId && transport.token);
|
|
943
955
|
if (!ready) return;
|
|
944
956
|
const seq = ++requestSeqRef.current;
|
|
945
957
|
try {
|
|
@@ -952,7 +964,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
952
964
|
setPollError(err instanceof Error ? err.message : "Could not reach the Vercel API");
|
|
953
965
|
console.error("Deploy-vercel-from-sanity: fetch error", err);
|
|
954
966
|
}
|
|
955
|
-
}, [
|
|
967
|
+
}, [transport, targetRef]);
|
|
956
968
|
(0, import_react8.useEffect)(() => {
|
|
957
969
|
fetchDeployments2().finally(() => setLoadingInitial(false));
|
|
958
970
|
}, [fetchDeployments2]);
|
|
@@ -1043,7 +1055,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
1043
1055
|
} finally {
|
|
1044
1056
|
setCanceling(false);
|
|
1045
1057
|
}
|
|
1046
|
-
}, [latest?.uid,
|
|
1058
|
+
}, [latest?.uid, transport, targetRef, fetchDeployments2]);
|
|
1047
1059
|
const copyUrl = (0, import_react8.useCallback)(() => {
|
|
1048
1060
|
if (!latest?.url) return;
|
|
1049
1061
|
const fullUrl = deploymentHref(latest.url) ?? "";
|
|
@@ -1067,7 +1079,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
1067
1079
|
} finally {
|
|
1068
1080
|
setLoadingLogs(false);
|
|
1069
1081
|
}
|
|
1070
|
-
}, [latest?.uid,
|
|
1082
|
+
}, [latest?.uid, transport, targetRef]);
|
|
1071
1083
|
const toggleErrorLogs = (0, import_react8.useCallback)(() => {
|
|
1072
1084
|
if (!showErrorLogs && errorLines.length === 0 && !logError) fetchErrorLogs();
|
|
1073
1085
|
setShowErrorLogs((v) => !v);
|
|
@@ -1632,7 +1644,7 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
|
|
|
1632
1644
|
}
|
|
1633
1645
|
|
|
1634
1646
|
// src/version.ts
|
|
1635
|
-
var VERSION = "1.3.
|
|
1647
|
+
var VERSION = "1.3.2";
|
|
1636
1648
|
|
|
1637
1649
|
// src/components/DeployTool.tsx
|
|
1638
1650
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
package/dist/index.mjs
CHANGED
|
@@ -84,7 +84,7 @@ function usePluginConfig() {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// src/components/DeployItem.tsx
|
|
87
|
-
import { useState as useState5, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef2 } from "react";
|
|
87
|
+
import { useState as useState5, useEffect as useEffect4, useCallback as useCallback4, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
88
88
|
import { flushSync } from "react-dom";
|
|
89
89
|
|
|
90
90
|
// src/compat/primitives.tsx
|
|
@@ -772,7 +772,7 @@ function StatusBadge({ state, showSpinner }) {
|
|
|
772
772
|
}
|
|
773
773
|
|
|
774
774
|
// src/components/DeployHistory.tsx
|
|
775
|
-
import { useId as useId3, useEffect as useEffect3, useState as useState4, useCallback as useCallback3 } from "react";
|
|
775
|
+
import { useId as useId3, useEffect as useEffect3, useMemo, useState as useState4, useCallback as useCallback3 } from "react";
|
|
776
776
|
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
777
777
|
function DeployHistory({ target, token, onClose }) {
|
|
778
778
|
const dialogId = useId3();
|
|
@@ -781,8 +781,14 @@ function DeployHistory({ target, token, onClose }) {
|
|
|
781
781
|
const [loading, setLoading] = useState4(true);
|
|
782
782
|
const [error, setError] = useState4(null);
|
|
783
783
|
const { projectId, hookId } = parseHookUrl(target.url);
|
|
784
|
-
const transport =
|
|
785
|
-
|
|
784
|
+
const transport = useMemo(
|
|
785
|
+
() => pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token },
|
|
786
|
+
[pluginConfig.mode, pluginConfig.proxyUrl, pluginConfig.statusKey, token]
|
|
787
|
+
);
|
|
788
|
+
const targetRef = useMemo(
|
|
789
|
+
() => ({ projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId }),
|
|
790
|
+
[projectId, hookId, target.proxyKey, target.teamId]
|
|
791
|
+
);
|
|
786
792
|
const load = useCallback3(async () => {
|
|
787
793
|
setLoading(true);
|
|
788
794
|
setError(null);
|
|
@@ -794,7 +800,7 @@ function DeployHistory({ target, token, onClose }) {
|
|
|
794
800
|
} finally {
|
|
795
801
|
setLoading(false);
|
|
796
802
|
}
|
|
797
|
-
}, [
|
|
803
|
+
}, [transport, targetRef]);
|
|
798
804
|
useEffect3(() => {
|
|
799
805
|
load();
|
|
800
806
|
}, [load]);
|
|
@@ -875,8 +881,14 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
875
881
|
const toast = useToast();
|
|
876
882
|
const pluginConfig = usePluginConfig();
|
|
877
883
|
const client = useClient({ apiVersion: "2025-01-01" });
|
|
878
|
-
const transport =
|
|
879
|
-
|
|
884
|
+
const transport = useMemo2(
|
|
885
|
+
() => pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token },
|
|
886
|
+
[pluginConfig.mode, pluginConfig.proxyUrl, pluginConfig.statusKey, token]
|
|
887
|
+
);
|
|
888
|
+
const targetRef = useMemo2(
|
|
889
|
+
() => ({ projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId }),
|
|
890
|
+
[projectId, hookId, target.proxyKey, target.teamId]
|
|
891
|
+
);
|
|
880
892
|
const [deployments, setDeployments] = useState5([]);
|
|
881
893
|
const [loadingInitial, setLoadingInitial] = useState5(true);
|
|
882
894
|
const [pendingSince, setPendingSince] = useState5(null);
|
|
@@ -904,7 +916,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
904
916
|
const isPending = pendingSince !== null;
|
|
905
917
|
const isActive = isPending || isActiveState(latest?.state);
|
|
906
918
|
const fetchDeployments2 = useCallback4(async () => {
|
|
907
|
-
const ready = transport.mode === "proxy" ? Boolean(
|
|
919
|
+
const ready = transport.mode === "proxy" ? Boolean(targetRef.proxyKey && transport.proxyUrl) : Boolean(targetRef.projectId && targetRef.hookId && transport.token);
|
|
908
920
|
if (!ready) return;
|
|
909
921
|
const seq = ++requestSeqRef.current;
|
|
910
922
|
try {
|
|
@@ -917,7 +929,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
917
929
|
setPollError(err instanceof Error ? err.message : "Could not reach the Vercel API");
|
|
918
930
|
console.error("Deploy-vercel-from-sanity: fetch error", err);
|
|
919
931
|
}
|
|
920
|
-
}, [
|
|
932
|
+
}, [transport, targetRef]);
|
|
921
933
|
useEffect4(() => {
|
|
922
934
|
fetchDeployments2().finally(() => setLoadingInitial(false));
|
|
923
935
|
}, [fetchDeployments2]);
|
|
@@ -1008,7 +1020,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
1008
1020
|
} finally {
|
|
1009
1021
|
setCanceling(false);
|
|
1010
1022
|
}
|
|
1011
|
-
}, [latest?.uid,
|
|
1023
|
+
}, [latest?.uid, transport, targetRef, fetchDeployments2]);
|
|
1012
1024
|
const copyUrl = useCallback4(() => {
|
|
1013
1025
|
if (!latest?.url) return;
|
|
1014
1026
|
const fullUrl = deploymentHref(latest.url) ?? "";
|
|
@@ -1032,7 +1044,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
|
|
|
1032
1044
|
} finally {
|
|
1033
1045
|
setLoadingLogs(false);
|
|
1034
1046
|
}
|
|
1035
|
-
}, [latest?.uid,
|
|
1047
|
+
}, [latest?.uid, transport, targetRef]);
|
|
1036
1048
|
const toggleErrorLogs = useCallback4(() => {
|
|
1037
1049
|
if (!showErrorLogs && errorLines.length === 0 && !logError) fetchErrorLogs();
|
|
1038
1050
|
setShowErrorLogs((v) => !v);
|
|
@@ -1597,7 +1609,7 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
|
|
|
1597
1609
|
}
|
|
1598
1610
|
|
|
1599
1611
|
// src/version.ts
|
|
1600
|
-
var VERSION = "1.3.
|
|
1612
|
+
var VERSION = "1.3.2";
|
|
1601
1613
|
|
|
1602
1614
|
// src/components/DeployTool.tsx
|
|
1603
1615
|
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liiift-studio/deploy-vercel-from-sanity",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Sanity Studio plugin — trigger and monitor Vercel deployments with full status, history, and build logs. Supports Studio v3.30 through v6.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Liiift Studio",
|
|
@@ -41,7 +41,11 @@
|
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsup",
|
|
43
43
|
"dev": "tsup --watch",
|
|
44
|
-
"prepublishOnly": "npm run build"
|
|
44
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm test && npm run build",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest",
|
|
47
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
48
|
+
"lint": "eslint ."
|
|
45
49
|
},
|
|
46
50
|
"peerDependencies": {
|
|
47
51
|
"@sanity/icons": ">=2 <6",
|
|
@@ -55,11 +59,16 @@
|
|
|
55
59
|
"@sanity/ui": "^4",
|
|
56
60
|
"@types/react": "^19",
|
|
57
61
|
"@types/react-dom": "^19.2.4",
|
|
62
|
+
"eslint": "^9.39.5",
|
|
63
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
58
64
|
"react": "^19",
|
|
59
65
|
"react-dom": "^19",
|
|
60
66
|
"sanity": "^6",
|
|
67
|
+
"styled-components": "^6.5.3",
|
|
61
68
|
"tsup": "^8",
|
|
62
|
-
"typescript": "^5"
|
|
69
|
+
"typescript": "^5",
|
|
70
|
+
"typescript-eslint": "^8.67.0",
|
|
71
|
+
"vitest": "^3.2.7"
|
|
63
72
|
},
|
|
64
73
|
"repository": {
|
|
65
74
|
"type": "git",
|