@vercel/go 4.0.0 → 6.0.0
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/bin/proxy-linux-amd64 +0 -0
- package/bin/proxy-linux-arm64 +0 -0
- package/dist/index.js +259 -186
- package/package.json +4 -5
- package/bootstrap/utils.go +0 -140
- package/bootstrap/vc-init-dev.go +0 -147
package/bin/proxy-linux-amd64
CHANGED
|
Binary file
|
package/bin/proxy-linux-arm64
CHANGED
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -2199,14 +2199,14 @@ var require_signal_exit = __commonJS({
|
|
|
2199
2199
|
if (opts && opts.alwaysLast) {
|
|
2200
2200
|
ev = "afterexit";
|
|
2201
2201
|
}
|
|
2202
|
-
var
|
|
2202
|
+
var remove4 = function() {
|
|
2203
2203
|
emitter.removeListener(ev, cb);
|
|
2204
2204
|
if (emitter.listeners("exit").length === 0 && emitter.listeners("afterexit").length === 0) {
|
|
2205
2205
|
unload();
|
|
2206
2206
|
}
|
|
2207
2207
|
};
|
|
2208
2208
|
emitter.on(ev, cb);
|
|
2209
|
-
return
|
|
2209
|
+
return remove4;
|
|
2210
2210
|
};
|
|
2211
2211
|
unload = function unload2() {
|
|
2212
2212
|
if (!loaded || !processOk(global.process)) {
|
|
@@ -4372,7 +4372,7 @@ var require_copy = __commonJS({
|
|
|
4372
4372
|
"use strict";
|
|
4373
4373
|
var fs3 = require_graceful_fs();
|
|
4374
4374
|
var path = require("path");
|
|
4375
|
-
var
|
|
4375
|
+
var mkdirp4 = require_mkdirs2().mkdirs;
|
|
4376
4376
|
var pathExists5 = require_path_exists().pathExists;
|
|
4377
4377
|
var utimes = require_utimes().utimesMillis;
|
|
4378
4378
|
var notExist = Symbol("notExist");
|
|
@@ -4408,7 +4408,7 @@ var require_copy = __commonJS({
|
|
|
4408
4408
|
return cb(err);
|
|
4409
4409
|
if (dirExists)
|
|
4410
4410
|
return startCopy(destStat, src, dest, opts, cb);
|
|
4411
|
-
|
|
4411
|
+
mkdirp4(destParent, (err2) => {
|
|
4412
4412
|
if (err2)
|
|
4413
4413
|
return cb(err2);
|
|
4414
4414
|
return startCopy(destStat, src, dest, opts, cb);
|
|
@@ -4889,7 +4889,7 @@ var require_empty = __commonJS({
|
|
|
4889
4889
|
var fs3 = require("fs");
|
|
4890
4890
|
var path = require("path");
|
|
4891
4891
|
var mkdir = require_mkdirs2();
|
|
4892
|
-
var
|
|
4892
|
+
var remove4 = require_remove();
|
|
4893
4893
|
var emptyDir = u2(function emptyDir2(dir, callback) {
|
|
4894
4894
|
callback = callback || function() {
|
|
4895
4895
|
};
|
|
@@ -4902,7 +4902,7 @@ var require_empty = __commonJS({
|
|
|
4902
4902
|
const item = items.pop();
|
|
4903
4903
|
if (!item)
|
|
4904
4904
|
return callback();
|
|
4905
|
-
|
|
4905
|
+
remove4.remove(item, (err2) => {
|
|
4906
4906
|
if (err2)
|
|
4907
4907
|
return callback(err2);
|
|
4908
4908
|
deleteItem();
|
|
@@ -4919,7 +4919,7 @@ var require_empty = __commonJS({
|
|
|
4919
4919
|
}
|
|
4920
4920
|
items.forEach((item) => {
|
|
4921
4921
|
item = path.join(dir, item);
|
|
4922
|
-
|
|
4922
|
+
remove4.removeSync(item);
|
|
4923
4923
|
});
|
|
4924
4924
|
}
|
|
4925
4925
|
module2.exports = {
|
|
@@ -5574,8 +5574,8 @@ var require_move = __commonJS({
|
|
|
5574
5574
|
var fs3 = require_graceful_fs();
|
|
5575
5575
|
var path = require("path");
|
|
5576
5576
|
var copy3 = require_copy2().copy;
|
|
5577
|
-
var
|
|
5578
|
-
var
|
|
5577
|
+
var remove4 = require_remove().remove;
|
|
5578
|
+
var mkdirp4 = require_mkdirs2().mkdirp;
|
|
5579
5579
|
var pathExists5 = require_path_exists().pathExists;
|
|
5580
5580
|
function move2(src, dest, opts, cb) {
|
|
5581
5581
|
if (typeof opts === "function") {
|
|
@@ -5593,7 +5593,7 @@ var require_move = __commonJS({
|
|
|
5593
5593
|
if (st2.isDirectory() && isSrcSubdir(src, dest)) {
|
|
5594
5594
|
return cb(new Error(`Cannot move '${src}' to a subdirectory of itself, '${dest}'.`));
|
|
5595
5595
|
}
|
|
5596
|
-
|
|
5596
|
+
mkdirp4(path.dirname(dest), (err2) => {
|
|
5597
5597
|
if (err2)
|
|
5598
5598
|
return cb(err2);
|
|
5599
5599
|
return doRename(src, dest, overwrite, cb);
|
|
@@ -5602,7 +5602,7 @@ var require_move = __commonJS({
|
|
|
5602
5602
|
}
|
|
5603
5603
|
function doRename(src, dest, overwrite, cb) {
|
|
5604
5604
|
if (overwrite) {
|
|
5605
|
-
return
|
|
5605
|
+
return remove4(dest, (err) => {
|
|
5606
5606
|
if (err)
|
|
5607
5607
|
return cb(err);
|
|
5608
5608
|
return rename(src, dest, overwrite, cb);
|
|
@@ -5633,7 +5633,7 @@ var require_move = __commonJS({
|
|
|
5633
5633
|
copy3(src, dest, opts, (err) => {
|
|
5634
5634
|
if (err)
|
|
5635
5635
|
return cb(err);
|
|
5636
|
-
return
|
|
5636
|
+
return remove4(src, cb);
|
|
5637
5637
|
});
|
|
5638
5638
|
}
|
|
5639
5639
|
function isSrcSubdir(src, dest) {
|
|
@@ -10967,9 +10967,9 @@ var require_dev_proxy = __commonJS({
|
|
|
10967
10967
|
resolveServiceRoutePrefix: () => resolveServiceRoutePrefix,
|
|
10968
10968
|
rewriteRequestUrl: () => rewriteRequestUrl,
|
|
10969
10969
|
sanitizeHeaders: () => sanitizeHeaders,
|
|
10970
|
-
startDevProxy: () =>
|
|
10970
|
+
startDevProxy: () => startDevProxy2,
|
|
10971
10971
|
stripServiceRoutePrefix: () => stripServiceRoutePrefix,
|
|
10972
|
-
waitForPort: () =>
|
|
10972
|
+
waitForPort: () => waitForPort
|
|
10973
10973
|
});
|
|
10974
10974
|
module2.exports = __toCommonJS2(dev_proxy_exports);
|
|
10975
10975
|
var import_node_http = require("http");
|
|
@@ -11060,7 +11060,7 @@ var require_dev_proxy = __commonJS({
|
|
|
11060
11060
|
});
|
|
11061
11061
|
});
|
|
11062
11062
|
}
|
|
11063
|
-
function
|
|
11063
|
+
function isPortReachable(port) {
|
|
11064
11064
|
return new Promise((resolve) => {
|
|
11065
11065
|
const socket = (0, import_node_net.createConnection)({ port, host: LOCALHOST });
|
|
11066
11066
|
const done = (reachable) => {
|
|
@@ -11074,10 +11074,10 @@ var require_dev_proxy = __commonJS({
|
|
|
11074
11074
|
socket.once("error", () => done(false));
|
|
11075
11075
|
});
|
|
11076
11076
|
}
|
|
11077
|
-
function
|
|
11077
|
+
function sleep(ms2) {
|
|
11078
11078
|
return new Promise((resolve) => setTimeout(resolve, ms2));
|
|
11079
11079
|
}
|
|
11080
|
-
async function
|
|
11080
|
+
async function waitForPort(port, child, timeout, label = "Dev server") {
|
|
11081
11081
|
let exited;
|
|
11082
11082
|
let spawnError;
|
|
11083
11083
|
const onExit = (code, signal) => {
|
|
@@ -11098,9 +11098,9 @@ var require_dev_proxy = __commonJS({
|
|
|
11098
11098
|
`${label} exited before it started listening (code: ${exited.code}, signal: ${exited.signal})`
|
|
11099
11099
|
);
|
|
11100
11100
|
}
|
|
11101
|
-
if (await
|
|
11101
|
+
if (await isPortReachable(port))
|
|
11102
11102
|
return;
|
|
11103
|
-
await
|
|
11103
|
+
await sleep(READINESS_POLL_INTERVAL);
|
|
11104
11104
|
}
|
|
11105
11105
|
throw new Error(`${label} did not start listening within ${timeout}ms`);
|
|
11106
11106
|
} finally {
|
|
@@ -11176,7 +11176,7 @@ var require_dev_proxy = __commonJS({
|
|
|
11176
11176
|
});
|
|
11177
11177
|
return server;
|
|
11178
11178
|
}
|
|
11179
|
-
async function
|
|
11179
|
+
async function startDevProxy2(options) {
|
|
11180
11180
|
const {
|
|
11181
11181
|
spawnServer,
|
|
11182
11182
|
env = process.env,
|
|
@@ -11198,7 +11198,7 @@ var require_dev_proxy = __commonJS({
|
|
|
11198
11198
|
}
|
|
11199
11199
|
};
|
|
11200
11200
|
try {
|
|
11201
|
-
await
|
|
11201
|
+
await waitForPort(internalPort, child, readinessTimeout, label);
|
|
11202
11202
|
server = createDevProxyServer({
|
|
11203
11203
|
targetPort: internalPort,
|
|
11204
11204
|
routePrefix: resolveServiceRoutePrefix(env)
|
|
@@ -14579,6 +14579,7 @@ var localCacheDir = (0, import_path4.join)(".vercel", "cache", "golang");
|
|
|
14579
14579
|
var GO_FLAGS = process.platform === "win32" ? [] : ["-ldflags", "-s -w"];
|
|
14580
14580
|
var GO_MIN_MAJOR_VERSION = 1;
|
|
14581
14581
|
var GO_MIN_MINOR_VERSION = 13;
|
|
14582
|
+
var GO_INSTALL_MARKER = ".vercel-go-install-complete";
|
|
14582
14583
|
function getGoUrl(version2) {
|
|
14583
14584
|
const { arch, platform } = process;
|
|
14584
14585
|
const ext = platform === "win32" ? "zip" : "tar.gz";
|
|
@@ -14753,27 +14754,75 @@ var GoWrapper = class {
|
|
|
14753
14754
|
getEnv() {
|
|
14754
14755
|
return this.env;
|
|
14755
14756
|
}
|
|
14756
|
-
build(src, dest, { vendorMode = false } = {}) {
|
|
14757
|
-
(0, import_build_utils.debug)(
|
|
14757
|
+
build(src, dest, { vendorMode = false, release = true } = {}) {
|
|
14758
|
+
(0, import_build_utils.debug)(
|
|
14759
|
+
`Building ${release ? "optimized" : "debug"} 'go' binary ${src} -> ${dest}`
|
|
14760
|
+
);
|
|
14758
14761
|
const sources = Array.isArray(src) ? src : [src];
|
|
14759
14762
|
const envGoBuildFlags = (this.env || this.opts.env).GO_BUILD_FLAGS;
|
|
14760
|
-
const
|
|
14763
|
+
const defaultFlags = release ? GO_FLAGS : [];
|
|
14764
|
+
const flags = envGoBuildFlags ? (0, import_string_argv.default)(envGoBuildFlags) : [...defaultFlags];
|
|
14761
14765
|
if (vendorMode && !envGoBuildFlags) {
|
|
14762
14766
|
flags.push("-mod=vendor");
|
|
14763
14767
|
}
|
|
14764
14768
|
return this.execute("build", ...flags, "-o", dest, ...sources);
|
|
14765
14769
|
}
|
|
14766
14770
|
};
|
|
14771
|
+
function newestSupportedGoVersion() {
|
|
14772
|
+
return Array.from(versionMap.values())[0];
|
|
14773
|
+
}
|
|
14774
|
+
function compareGoVersions(left, right) {
|
|
14775
|
+
const leftParts = left.split(".").map((part) => parseInt(part, 10));
|
|
14776
|
+
const rightParts = right.split(".").map((part) => parseInt(part, 10));
|
|
14777
|
+
for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index++) {
|
|
14778
|
+
const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
|
|
14779
|
+
if (difference !== 0) {
|
|
14780
|
+
return difference;
|
|
14781
|
+
}
|
|
14782
|
+
}
|
|
14783
|
+
return 0;
|
|
14784
|
+
}
|
|
14785
|
+
function selectGoVersion(preferred, { preferNewestToolchain = false } = {}) {
|
|
14786
|
+
if (preferred?.toolchain) {
|
|
14787
|
+
return preferred.toolchain;
|
|
14788
|
+
}
|
|
14789
|
+
if (preferred && !preferNewestToolchain) {
|
|
14790
|
+
return preferred.go;
|
|
14791
|
+
}
|
|
14792
|
+
const newestSupported = newestSupportedGoVersion();
|
|
14793
|
+
if (preferred && compareGoVersions(preferred.go, newestSupported) > 0) {
|
|
14794
|
+
return preferred.go;
|
|
14795
|
+
}
|
|
14796
|
+
return newestSupported;
|
|
14797
|
+
}
|
|
14798
|
+
var MIN_DARWIN_RUNNABLE_GO = { major: 1, minor: 23 };
|
|
14799
|
+
function isBelowMinDarwinRunnable(version2) {
|
|
14800
|
+
const [major, minor] = version2.split(".").map((part) => parseInt(part, 10));
|
|
14801
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor))
|
|
14802
|
+
return false;
|
|
14803
|
+
if (major !== MIN_DARWIN_RUNNABLE_GO.major) {
|
|
14804
|
+
return major < MIN_DARWIN_RUNNABLE_GO.major;
|
|
14805
|
+
}
|
|
14806
|
+
return minor < MIN_DARWIN_RUNNABLE_GO.minor;
|
|
14807
|
+
}
|
|
14767
14808
|
async function createGo({
|
|
14768
14809
|
modulePath,
|
|
14769
14810
|
opts = {},
|
|
14770
|
-
workPath
|
|
14811
|
+
workPath,
|
|
14812
|
+
preferNewestToolchain = false
|
|
14771
14813
|
}) {
|
|
14772
14814
|
let goPreferredVersion;
|
|
14773
14815
|
if (modulePath) {
|
|
14774
14816
|
goPreferredVersion = await parseGoModVersionFromModule(modulePath);
|
|
14775
14817
|
}
|
|
14776
|
-
const goSelectedVersion = goPreferredVersion
|
|
14818
|
+
const goSelectedVersion = selectGoVersion(goPreferredVersion, {
|
|
14819
|
+
preferNewestToolchain
|
|
14820
|
+
});
|
|
14821
|
+
if (preferNewestToolchain && process.platform === "darwin" && isBelowMinDarwinRunnable(goSelectedVersion)) {
|
|
14822
|
+
console.warn(
|
|
14823
|
+
`Warning: your \`go.mod\` pins Go ${goSelectedVersion} via \`toolchain\`. Binaries built with Go older than ${MIN_DARWIN_RUNNABLE_GO.major}.${MIN_DARWIN_RUNNABLE_GO.minor} do not start on recent macOS. Remove the directive to use Go ${newestSupportedGoVersion()} locally.`
|
|
14824
|
+
);
|
|
14825
|
+
}
|
|
14777
14826
|
const env = opts.env ? (0, import_build_utils.cloneEnv)(opts.env) : (0, import_build_utils.cloneEnv)(process.env);
|
|
14778
14827
|
const { PATH } = env;
|
|
14779
14828
|
const { platform } = process;
|
|
@@ -14837,9 +14886,17 @@ async function createGo({
|
|
|
14837
14886
|
for (const [label, goDir] of Object.entries(goDirs)) {
|
|
14838
14887
|
try {
|
|
14839
14888
|
const goBinDir = goDir && (0, import_path4.join)(goDir, "bin");
|
|
14840
|
-
if (goBinDir
|
|
14841
|
-
(0,
|
|
14842
|
-
|
|
14889
|
+
if (goBinDir) {
|
|
14890
|
+
if (!await (0, import_fs_extra.pathExists)(goBinDir)) {
|
|
14891
|
+
(0, import_build_utils.debug)(`Go not found in ${label}`);
|
|
14892
|
+
continue;
|
|
14893
|
+
}
|
|
14894
|
+
if (!await (0, import_fs_extra.pathExists)((0, import_path4.join)(goDir, GO_INSTALL_MARKER))) {
|
|
14895
|
+
(0, import_build_utils.debug)(
|
|
14896
|
+
`Go install in ${label} is incomplete, ignoring this installation`
|
|
14897
|
+
);
|
|
14898
|
+
continue;
|
|
14899
|
+
}
|
|
14843
14900
|
}
|
|
14844
14901
|
env.GOROOT = goDir || void 0;
|
|
14845
14902
|
env.PATH = goBinDir || PATH;
|
|
@@ -14876,40 +14933,56 @@ async function download({ dest, version: version2 }) {
|
|
|
14876
14933
|
if (!res.ok) {
|
|
14877
14934
|
throw new Error(`Failed to download: ${url} (${res.status})`);
|
|
14878
14935
|
}
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
14891
|
-
const
|
|
14892
|
-
if (
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
|
|
14900
|
-
|
|
14936
|
+
const tmpSuffix = `${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
14937
|
+
const tmpDest = `${dest}.tmp-${tmpSuffix}`;
|
|
14938
|
+
(0, import_build_utils.debug)(`Installing go ${version2} to ${dest} (via ${tmpDest})`);
|
|
14939
|
+
await (0, import_fs_extra.mkdirp)(tmpDest);
|
|
14940
|
+
try {
|
|
14941
|
+
if (/\.zip$/.test(filename)) {
|
|
14942
|
+
const zipFile = (0, import_path4.join)((0, import_os.tmpdir)(), `${tmpSuffix}-${filename}`);
|
|
14943
|
+
try {
|
|
14944
|
+
await streamPipeline(res.body, (0, import_fs_extra.createWriteStream)(zipFile));
|
|
14945
|
+
const zip = await import_yauzl_promise.default.open(zipFile);
|
|
14946
|
+
let entry = await zip.readEntry();
|
|
14947
|
+
while (entry) {
|
|
14948
|
+
const fileName = entry.fileName.split("/").slice(1).join("/");
|
|
14949
|
+
if (fileName) {
|
|
14950
|
+
const destPath = (0, import_path4.join)(tmpDest, fileName);
|
|
14951
|
+
if (/\/$/.test(fileName)) {
|
|
14952
|
+
await (0, import_fs_extra.mkdirp)(destPath);
|
|
14953
|
+
} else {
|
|
14954
|
+
const [entryStream] = await Promise.all([
|
|
14955
|
+
entry.openReadStream(),
|
|
14956
|
+
(0, import_fs_extra.mkdirp)((0, import_path4.dirname)(destPath))
|
|
14957
|
+
]);
|
|
14958
|
+
const out = (0, import_fs_extra.createWriteStream)(destPath);
|
|
14959
|
+
await streamPipeline(entryStream, out);
|
|
14960
|
+
}
|
|
14901
14961
|
}
|
|
14962
|
+
entry = await zip.readEntry();
|
|
14902
14963
|
}
|
|
14903
|
-
|
|
14964
|
+
} finally {
|
|
14965
|
+
await (0, import_fs_extra.remove)(zipFile);
|
|
14904
14966
|
}
|
|
14905
|
-
}
|
|
14906
|
-
await (
|
|
14967
|
+
} else {
|
|
14968
|
+
await new Promise((resolve, reject) => {
|
|
14969
|
+
res.body.on("error", reject).pipe(lo({ cwd: tmpDest, strip: 1 })).on("error", reject).on("finish", resolve);
|
|
14970
|
+
});
|
|
14907
14971
|
}
|
|
14908
|
-
|
|
14972
|
+
await import_fs5.default.promises.writeFile((0, import_path4.join)(tmpDest, GO_INSTALL_MARKER), version2);
|
|
14973
|
+
if (!await (0, import_fs_extra.pathExists)((0, import_path4.join)(dest, GO_INSTALL_MARKER))) {
|
|
14974
|
+
await (0, import_fs_extra.remove)(dest);
|
|
14975
|
+
}
|
|
14976
|
+
try {
|
|
14977
|
+
await import_fs5.default.promises.rename(tmpDest, dest);
|
|
14978
|
+
} catch (err) {
|
|
14979
|
+
if (!await (0, import_fs_extra.pathExists)((0, import_path4.join)(dest, GO_INSTALL_MARKER))) {
|
|
14980
|
+
throw err;
|
|
14981
|
+
}
|
|
14982
|
+
}
|
|
14983
|
+
} finally {
|
|
14984
|
+
await (0, import_fs_extra.remove)(tmpDest);
|
|
14909
14985
|
}
|
|
14910
|
-
await new Promise((resolve, reject) => {
|
|
14911
|
-
res.body.on("error", reject).pipe(lo({ cwd: dest, strip: 1 })).on("error", reject).on("finish", resolve);
|
|
14912
|
-
});
|
|
14913
14986
|
}
|
|
14914
14987
|
var goVersionRegExp = /(\d+)\.(\d+)(?:\.(\d+))?/;
|
|
14915
14988
|
function parseGoVersionString(goVersionOutput) {
|
|
@@ -15088,7 +15161,6 @@ var detectEntrypoint = async ({ workPath }) => {
|
|
|
15088
15161
|
|
|
15089
15162
|
// src/standalone-server.ts
|
|
15090
15163
|
var import_child_process = require("child_process");
|
|
15091
|
-
var import_net = require("net");
|
|
15092
15164
|
var import_path6 = require("path");
|
|
15093
15165
|
var import_fs_extra3 = __toESM(require_lib2());
|
|
15094
15166
|
var import_build_utils4 = require("@vercel/build-utils");
|
|
@@ -15333,19 +15405,29 @@ async function createStandaloneServerLambda({
|
|
|
15333
15405
|
});
|
|
15334
15406
|
return lambda;
|
|
15335
15407
|
}
|
|
15336
|
-
var DEV_SERVER_STARTUP_TIMEOUT = 5 * 6e4;
|
|
15337
15408
|
var PERSISTENT_SERVERS = /* @__PURE__ */ new Map();
|
|
15338
15409
|
var PENDING_STARTS = /* @__PURE__ */ new Map();
|
|
15339
15410
|
var cleanupHandlersInstalled = false;
|
|
15340
|
-
function
|
|
15411
|
+
function killStandaloneDevServerProcessTree(child) {
|
|
15412
|
+
if (!child.pid)
|
|
15413
|
+
return;
|
|
15341
15414
|
try {
|
|
15342
15415
|
if (process.platform === "win32") {
|
|
15343
|
-
|
|
15416
|
+
child.kill("SIGTERM");
|
|
15344
15417
|
} else {
|
|
15345
|
-
process.kill(-pid, "SIGTERM");
|
|
15418
|
+
process.kill(-child.pid, "SIGTERM");
|
|
15346
15419
|
}
|
|
15347
15420
|
} catch (err) {
|
|
15348
|
-
(
|
|
15421
|
+
if (err.code !== "ESRCH") {
|
|
15422
|
+
(0, import_build_utils4.debug)(`Error killing standalone Go dev server ${child.pid}: ${err}`);
|
|
15423
|
+
}
|
|
15424
|
+
}
|
|
15425
|
+
}
|
|
15426
|
+
async function closeStandaloneDevServer(handle) {
|
|
15427
|
+
try {
|
|
15428
|
+
await handle.close();
|
|
15429
|
+
} finally {
|
|
15430
|
+
killStandaloneDevServerProcessTree(handle.child);
|
|
15349
15431
|
}
|
|
15350
15432
|
}
|
|
15351
15433
|
function installGlobalCleanupHandlers() {
|
|
@@ -15353,162 +15435,151 @@ function installGlobalCleanupHandlers() {
|
|
|
15353
15435
|
return;
|
|
15354
15436
|
cleanupHandlersInstalled = true;
|
|
15355
15437
|
const killAll = () => {
|
|
15356
|
-
for (const [key,
|
|
15357
|
-
killStandaloneDevServer(info.pid);
|
|
15438
|
+
for (const [key, server] of PERSISTENT_SERVERS.entries()) {
|
|
15358
15439
|
PERSISTENT_SERVERS.delete(key);
|
|
15440
|
+
killStandaloneDevServerProcessTree(server.handle.child);
|
|
15359
15441
|
}
|
|
15360
15442
|
};
|
|
15361
|
-
process.on("SIGINT",
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
process.on("SIGTERM", () => {
|
|
15365
|
-
killAll();
|
|
15366
|
-
});
|
|
15367
|
-
process.on("exit", () => {
|
|
15368
|
-
killAll();
|
|
15369
|
-
});
|
|
15443
|
+
process.on("SIGINT", killAll);
|
|
15444
|
+
process.on("SIGTERM", killAll);
|
|
15445
|
+
process.on("exit", killAll);
|
|
15370
15446
|
}
|
|
15371
|
-
function
|
|
15372
|
-
return
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
|
|
15377
|
-
|
|
15378
|
-
|
|
15379
|
-
socket.setTimeout(1e3);
|
|
15380
|
-
socket.once("connect", () => done(true));
|
|
15381
|
-
socket.once("timeout", () => done(false));
|
|
15382
|
-
socket.once("error", () => done(false));
|
|
15383
|
-
});
|
|
15384
|
-
}
|
|
15385
|
-
function sleep(ms2) {
|
|
15386
|
-
return new Promise((resolve) => setTimeout(resolve, ms2));
|
|
15387
|
-
}
|
|
15388
|
-
async function waitForPort(port, child, timeout) {
|
|
15389
|
-
let exited = null;
|
|
15390
|
-
let spawnError = null;
|
|
15391
|
-
const onExit = (code, signal) => {
|
|
15392
|
-
exited = { code, signal };
|
|
15393
|
-
};
|
|
15394
|
-
const onError = (err) => {
|
|
15395
|
-
spawnError = err;
|
|
15396
|
-
};
|
|
15397
|
-
child.once("exit", onExit);
|
|
15398
|
-
child.once("error", onError);
|
|
15399
|
-
try {
|
|
15400
|
-
const start = Date.now();
|
|
15401
|
-
while (Date.now() - start < timeout) {
|
|
15402
|
-
if (spawnError) {
|
|
15403
|
-
throw spawnError;
|
|
15404
|
-
}
|
|
15405
|
-
if (exited) {
|
|
15406
|
-
const { code, signal } = exited;
|
|
15407
|
-
throw new Error(
|
|
15408
|
-
`Standalone Go dev server exited before startup completed (code: ${code}, signal: ${signal})`
|
|
15409
|
-
);
|
|
15410
|
-
}
|
|
15411
|
-
if (await isPortReachable(port)) {
|
|
15412
|
-
return;
|
|
15447
|
+
function toDevServerResult(serverKey, handle) {
|
|
15448
|
+
return {
|
|
15449
|
+
port: handle.port,
|
|
15450
|
+
pid: handle.pid,
|
|
15451
|
+
persistent: true,
|
|
15452
|
+
shutdown: async () => {
|
|
15453
|
+
if (PERSISTENT_SERVERS.get(serverKey)?.handle === handle) {
|
|
15454
|
+
PERSISTENT_SERVERS.delete(serverKey);
|
|
15413
15455
|
}
|
|
15414
|
-
await
|
|
15456
|
+
await closeStandaloneDevServer(handle);
|
|
15415
15457
|
}
|
|
15416
|
-
|
|
15417
|
-
`Standalone Go dev server did not start within ${timeout}ms`
|
|
15418
|
-
);
|
|
15419
|
-
} finally {
|
|
15420
|
-
child.removeListener("exit", onExit);
|
|
15421
|
-
child.removeListener("error", onError);
|
|
15422
|
-
}
|
|
15458
|
+
};
|
|
15423
15459
|
}
|
|
15424
15460
|
async function startStandaloneDevServer(opts, resolvedEntrypoint) {
|
|
15425
15461
|
const { workPath, meta = {} } = opts;
|
|
15462
|
+
const { devCacheDir = (0, import_path6.join)(workPath, ".vercel", "cache") } = meta;
|
|
15426
15463
|
const serverKey = `${workPath}::${resolvedEntrypoint}`;
|
|
15427
15464
|
const existing = PERSISTENT_SERVERS.get(serverKey);
|
|
15428
15465
|
if (existing) {
|
|
15429
|
-
return
|
|
15430
|
-
port: existing.port,
|
|
15431
|
-
pid: existing.pid,
|
|
15432
|
-
shutdown: async () => {
|
|
15433
|
-
}
|
|
15434
|
-
};
|
|
15466
|
+
return toDevServerResult(serverKey, existing.handle);
|
|
15435
15467
|
}
|
|
15436
15468
|
const pending = PENDING_STARTS.get(serverKey);
|
|
15437
15469
|
if (pending) {
|
|
15438
|
-
|
|
15439
|
-
return
|
|
15440
|
-
} };
|
|
15470
|
+
await pending;
|
|
15471
|
+
return startStandaloneDevServer(opts, resolvedEntrypoint);
|
|
15441
15472
|
}
|
|
15442
15473
|
const startPromise = (async () => {
|
|
15443
|
-
const
|
|
15444
|
-
const env = (0, import_build_utils4.cloneEnv)(process.env, meta.env, {
|
|
15445
|
-
PORT: String(port)
|
|
15446
|
-
});
|
|
15474
|
+
const env = (0, import_build_utils4.cloneEnv)(process.env, meta.env);
|
|
15447
15475
|
const entrypointDir = (0, import_path6.dirname)((0, import_path6.join)(workPath, resolvedEntrypoint));
|
|
15448
15476
|
const { goModPath } = await findGoModPath(entrypointDir, workPath);
|
|
15449
15477
|
const modulePath = goModPath ? (0, import_path6.dirname)(goModPath) : workPath;
|
|
15450
15478
|
const go = await createGo({
|
|
15451
15479
|
modulePath,
|
|
15452
15480
|
opts: { cwd: workPath, env },
|
|
15453
|
-
workPath
|
|
15481
|
+
workPath,
|
|
15482
|
+
// The binary has to run on this machine, so don't downgrade.
|
|
15483
|
+
preferNewestToolchain: true
|
|
15454
15484
|
});
|
|
15455
|
-
const
|
|
15456
|
-
const
|
|
15457
|
-
|
|
15458
|
-
(0, import_build_utils4.debug)(
|
|
15459
|
-
`Starting standalone Go dev server wrapper: go run ${devWrapper} (target ${runTarget}, port ${port})`
|
|
15485
|
+
const buildTarget = resolvedEntrypoint === "main.go" ? "." : "./" + (0, import_path6.dirname)(resolvedEntrypoint);
|
|
15486
|
+
const isVendored = await (0, import_fs_extra3.pathExists)(
|
|
15487
|
+
(0, import_path6.join)(workPath, "vendor", "modules.txt")
|
|
15460
15488
|
);
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
process.stdout.write(chunk.toString());
|
|
15476
|
-
}
|
|
15477
|
-
});
|
|
15478
|
-
child.stderr?.on("data", (data) => {
|
|
15479
|
-
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
15480
|
-
if (opts.onStderr) {
|
|
15481
|
-
opts.onStderr(chunk);
|
|
15482
|
-
} else {
|
|
15483
|
-
process.stderr.write(chunk.toString());
|
|
15489
|
+
if (isVendored) {
|
|
15490
|
+
(0, import_build_utils4.debug)("Detected vendor directory, using -mod=vendor for build");
|
|
15491
|
+
}
|
|
15492
|
+
const outDir = (0, import_path6.join)(
|
|
15493
|
+
devCacheDir,
|
|
15494
|
+
"go-standalone",
|
|
15495
|
+
Math.random().toString(32).substring(2)
|
|
15496
|
+
);
|
|
15497
|
+
await (0, import_fs_extra3.mkdirp)(outDir);
|
|
15498
|
+
const cleanupOutDir = async () => {
|
|
15499
|
+
try {
|
|
15500
|
+
await (0, import_fs_extra3.remove)(outDir);
|
|
15501
|
+
} catch (err) {
|
|
15502
|
+
(0, import_build_utils4.debug)(`Could not delete dev server directory ${outDir}: ${err}`);
|
|
15484
15503
|
}
|
|
15485
|
-
}
|
|
15486
|
-
const
|
|
15504
|
+
};
|
|
15505
|
+
const executablePath = (0, import_path6.join)(
|
|
15506
|
+
outDir,
|
|
15507
|
+
`dev-server${process.platform === "win32" ? ".exe" : ""}`
|
|
15508
|
+
);
|
|
15509
|
+
(0, import_build_utils4.debug)(
|
|
15510
|
+
`Building standalone Go dev server: go build ${buildTarget} -> ${executablePath}`
|
|
15511
|
+
);
|
|
15487
15512
|
try {
|
|
15488
|
-
await
|
|
15513
|
+
await go.build(buildTarget, executablePath, {
|
|
15514
|
+
vendorMode: isVendored,
|
|
15515
|
+
release: false
|
|
15516
|
+
});
|
|
15489
15517
|
} catch (err) {
|
|
15490
|
-
|
|
15491
|
-
|
|
15492
|
-
}
|
|
15518
|
+
console.error(`Failed to build standalone Go server: ${buildTarget}`);
|
|
15519
|
+
await cleanupOutDir();
|
|
15493
15520
|
throw err;
|
|
15494
15521
|
}
|
|
15495
|
-
|
|
15496
|
-
|
|
15497
|
-
|
|
15498
|
-
|
|
15499
|
-
|
|
15500
|
-
|
|
15522
|
+
(0, import_build_utils4.debug)(`Starting standalone Go dev server: ${executablePath}`);
|
|
15523
|
+
let spawnedChild;
|
|
15524
|
+
const handle = await (0, import_ipc_proxy.startDevProxy)({
|
|
15525
|
+
port: typeof meta.port === "number" ? meta.port : void 0,
|
|
15526
|
+
env,
|
|
15527
|
+
label: "Standalone Go dev server",
|
|
15528
|
+
// Spawning the binary avoids a `go run` wrapper. On Unix, detaching also
|
|
15529
|
+
// makes it a process-group leader so shutdown can include its helpers.
|
|
15530
|
+
spawnServer: (internalPort) => {
|
|
15531
|
+
const child = (0, import_child_process.spawn)(executablePath, [], {
|
|
15532
|
+
cwd: workPath,
|
|
15533
|
+
env: (0, import_build_utils4.cloneEnv)(go.getEnv(), { PORT: String(internalPort) }),
|
|
15534
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
15535
|
+
detached: process.platform !== "win32"
|
|
15536
|
+
});
|
|
15537
|
+
spawnedChild = child;
|
|
15538
|
+
const forward = (stream, onData, fallback) => {
|
|
15539
|
+
stream?.on("data", (data) => {
|
|
15540
|
+
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
15541
|
+
if (onData) {
|
|
15542
|
+
onData(chunk);
|
|
15543
|
+
} else {
|
|
15544
|
+
fallback.write(
|
|
15545
|
+
new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)
|
|
15546
|
+
);
|
|
15547
|
+
}
|
|
15548
|
+
});
|
|
15549
|
+
};
|
|
15550
|
+
forward(child.stdout, opts.onStdout, process.stdout);
|
|
15551
|
+
forward(child.stderr, opts.onStderr, process.stderr);
|
|
15552
|
+
return child;
|
|
15553
|
+
}
|
|
15554
|
+
}).catch(async (err) => {
|
|
15555
|
+
if (spawnedChild) {
|
|
15556
|
+
killStandaloneDevServerProcessTree(spawnedChild);
|
|
15557
|
+
}
|
|
15558
|
+
await cleanupOutDir();
|
|
15559
|
+
throw err;
|
|
15560
|
+
});
|
|
15561
|
+
const server = { handle };
|
|
15562
|
+
PERSISTENT_SERVERS.set(serverKey, server);
|
|
15563
|
+
handle.child.once("exit", () => {
|
|
15564
|
+
if (PERSISTENT_SERVERS.get(serverKey)?.handle === handle) {
|
|
15565
|
+
PERSISTENT_SERVERS.delete(serverKey);
|
|
15566
|
+
}
|
|
15567
|
+
closeStandaloneDevServer(handle).catch((err) => {
|
|
15568
|
+
(0, import_build_utils4.debug)(`Error closing standalone Go dev proxy: ${err}`);
|
|
15569
|
+
});
|
|
15570
|
+
void cleanupOutDir();
|
|
15501
15571
|
});
|
|
15502
15572
|
installGlobalCleanupHandlers();
|
|
15503
|
-
return
|
|
15573
|
+
return server;
|
|
15504
15574
|
})();
|
|
15505
15575
|
PENDING_STARTS.set(serverKey, startPromise);
|
|
15506
15576
|
try {
|
|
15507
|
-
const
|
|
15508
|
-
return
|
|
15509
|
-
} };
|
|
15577
|
+
const server = await startPromise;
|
|
15578
|
+
return toDevServerResult(serverKey, server.handle);
|
|
15510
15579
|
} finally {
|
|
15511
|
-
PENDING_STARTS.
|
|
15580
|
+
if (PENDING_STARTS.get(serverKey) === startPromise) {
|
|
15581
|
+
PENDING_STARTS.delete(serverKey);
|
|
15582
|
+
}
|
|
15512
15583
|
}
|
|
15513
15584
|
}
|
|
15514
15585
|
|
|
@@ -16167,9 +16238,11 @@ async function startDevServer(opts) {
|
|
|
16167
16238
|
cwd: tmp,
|
|
16168
16239
|
env
|
|
16169
16240
|
},
|
|
16170
|
-
workPath
|
|
16241
|
+
workPath,
|
|
16242
|
+
// The binary has to run on this machine, so don't downgrade.
|
|
16243
|
+
preferNewestToolchain: true
|
|
16171
16244
|
});
|
|
16172
|
-
await go.build("./...", executable);
|
|
16245
|
+
await go.build("./...", executable, { release: false });
|
|
16173
16246
|
(0, import_build_utils5.debug)(`SPAWNING ${executable} CWD=${tmp}`);
|
|
16174
16247
|
const child = (0, import_child_process2.spawn)(executable, [], {
|
|
16175
16248
|
cwd: tmp,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/go",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/index",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
|
@@ -12,12 +12,11 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
14
|
"*.go",
|
|
15
|
-
"bin"
|
|
16
|
-
"bootstrap"
|
|
15
|
+
"bin"
|
|
17
16
|
],
|
|
18
17
|
"dependencies": {},
|
|
19
18
|
"peerDependencies": {
|
|
20
|
-
"@vercel/build-utils": "14.
|
|
19
|
+
"@vercel/build-utils": "14.5.0"
|
|
21
20
|
},
|
|
22
21
|
"devDependencies": {
|
|
23
22
|
"@tootallnate/once": "1.1.2",
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
"vitest": "4.1.10",
|
|
37
36
|
"xdg-app-paths": "5.1.0",
|
|
38
37
|
"yauzl-promise": "2.1.3",
|
|
39
|
-
"@vercel/build-utils": "14.
|
|
38
|
+
"@vercel/build-utils": "14.5.0",
|
|
40
39
|
"@vercel-internals/ipc-proxy": "1.0.0"
|
|
41
40
|
},
|
|
42
41
|
"scripts": {
|
package/bootstrap/utils.go
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"net"
|
|
6
|
-
"os"
|
|
7
|
-
"strings"
|
|
8
|
-
"time"
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
func findFreePort() (int, error) {
|
|
12
|
-
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
13
|
-
if err != nil {
|
|
14
|
-
return 0, err
|
|
15
|
-
}
|
|
16
|
-
port := listener.Addr().(*net.TCPAddr).Port
|
|
17
|
-
listener.Close()
|
|
18
|
-
return port, nil
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
func waitForServer(port int, timeout time.Duration) error {
|
|
22
|
-
const (
|
|
23
|
-
dialTimeout = 200 * time.Millisecond
|
|
24
|
-
pollInterval = 50 * time.Millisecond
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
|
28
|
-
deadline := time.Now().Add(timeout)
|
|
29
|
-
var lastErr error
|
|
30
|
-
|
|
31
|
-
for time.Now().Before(deadline) {
|
|
32
|
-
remaining := time.Until(deadline)
|
|
33
|
-
if remaining <= 0 {
|
|
34
|
-
break
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
attemptTimeout := dialTimeout
|
|
38
|
-
if remaining < attemptTimeout {
|
|
39
|
-
attemptTimeout = remaining
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
conn, err := net.DialTimeout("tcp", addr, attemptTimeout)
|
|
43
|
-
if err == nil {
|
|
44
|
-
conn.Close()
|
|
45
|
-
return nil
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
lastErr = err
|
|
49
|
-
time.Sleep(pollInterval)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if lastErr != nil {
|
|
53
|
-
return fmt.Errorf(
|
|
54
|
-
"server did not start listening on %s within %v: %w",
|
|
55
|
-
addr,
|
|
56
|
-
timeout,
|
|
57
|
-
lastErr,
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return fmt.Errorf("server did not start listening on %s within %v", addr, timeout)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
func normalizeServiceRoutePrefix(rawPrefix string) string {
|
|
65
|
-
if rawPrefix == "" {
|
|
66
|
-
return ""
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
prefix := strings.TrimSpace(rawPrefix)
|
|
70
|
-
if prefix == "" {
|
|
71
|
-
return ""
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if !strings.HasPrefix(prefix, "/") {
|
|
75
|
-
prefix = "/" + prefix
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if prefix != "/" {
|
|
79
|
-
prefix = strings.TrimRight(prefix, "/")
|
|
80
|
-
if prefix == "" {
|
|
81
|
-
prefix = "/"
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if prefix == "/" {
|
|
86
|
-
return ""
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return prefix
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
func serviceRoutePrefixStripEnabled() bool {
|
|
93
|
-
raw := strings.TrimSpace(os.Getenv("VERCEL_SERVICE_ROUTE_PREFIX_STRIP"))
|
|
94
|
-
if raw == "" {
|
|
95
|
-
return false
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
normalized := strings.ToLower(raw)
|
|
99
|
-
return normalized == "1" || normalized == "true"
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
func resolveServiceRoutePrefix() string {
|
|
103
|
-
if !serviceRoutePrefixStripEnabled() {
|
|
104
|
-
return ""
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return normalizeServiceRoutePrefix(os.Getenv("VERCEL_SERVICE_ROUTE_PREFIX"))
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
func stripServiceRoutePrefix(pathValue string, prefix string) string {
|
|
111
|
-
if pathValue == "*" {
|
|
112
|
-
return pathValue
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
normalizedPath := pathValue
|
|
116
|
-
if normalizedPath == "" {
|
|
117
|
-
normalizedPath = "/"
|
|
118
|
-
} else if !strings.HasPrefix(normalizedPath, "/") {
|
|
119
|
-
normalizedPath = "/" + normalizedPath
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if prefix == "" {
|
|
123
|
-
return normalizedPath
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if normalizedPath == prefix {
|
|
127
|
-
return "/"
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
prefixWithSlash := prefix + "/"
|
|
131
|
-
if strings.HasPrefix(normalizedPath, prefixWithSlash) {
|
|
132
|
-
stripped := normalizedPath[len(prefix):]
|
|
133
|
-
if stripped == "" {
|
|
134
|
-
return "/"
|
|
135
|
-
}
|
|
136
|
-
return stripped
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return normalizedPath
|
|
140
|
-
}
|
package/bootstrap/vc-init-dev.go
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
//go:build vcdev
|
|
2
|
-
// +build vcdev
|
|
3
|
-
|
|
4
|
-
// vc-init-dev.go - Dev wrapper for standalone Go servers on Vercel
|
|
5
|
-
//
|
|
6
|
-
// This wrapper is used by `vercel dev` for standalone Go mode. It:
|
|
7
|
-
// 1. Starts the user's server (`go run <target>`) on an internal port
|
|
8
|
-
// 2. Proxies requests from the external dev port to the internal server
|
|
9
|
-
// 3. Optionally strips generated services route prefixes before forwarding
|
|
10
|
-
|
|
11
|
-
package main
|
|
12
|
-
|
|
13
|
-
import (
|
|
14
|
-
"context"
|
|
15
|
-
"fmt"
|
|
16
|
-
"net/http"
|
|
17
|
-
"net/http/httputil"
|
|
18
|
-
"net/url"
|
|
19
|
-
"os"
|
|
20
|
-
"os/exec"
|
|
21
|
-
"os/signal"
|
|
22
|
-
"strconv"
|
|
23
|
-
"strings"
|
|
24
|
-
"sync"
|
|
25
|
-
"syscall"
|
|
26
|
-
"time"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
func mustEnv(name string) string {
|
|
30
|
-
value := strings.TrimSpace(os.Getenv(name))
|
|
31
|
-
if value == "" {
|
|
32
|
-
fmt.Fprintf(os.Stderr, "%s is not set\n", name)
|
|
33
|
-
os.Exit(1)
|
|
34
|
-
}
|
|
35
|
-
return value
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
func withEnvOverride(env []string, key, value string) []string {
|
|
39
|
-
prefix := key + "="
|
|
40
|
-
filtered := make([]string, 0, len(env)+1)
|
|
41
|
-
for _, item := range env {
|
|
42
|
-
if !strings.HasPrefix(item, prefix) {
|
|
43
|
-
filtered = append(filtered, item)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
filtered = append(filtered, prefix+value)
|
|
47
|
-
return filtered
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
func main() {
|
|
51
|
-
listenPortRaw := mustEnv("PORT")
|
|
52
|
-
listenPort, err := strconv.Atoi(listenPortRaw)
|
|
53
|
-
if err != nil || listenPort <= 0 {
|
|
54
|
-
fmt.Fprintf(os.Stderr, "Invalid PORT value: %q\n", listenPortRaw)
|
|
55
|
-
os.Exit(1)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
runTarget := mustEnv("__VC_GO_DEV_RUN_TARGET")
|
|
59
|
-
serviceRoutePrefix := resolveServiceRoutePrefix()
|
|
60
|
-
|
|
61
|
-
userPort, err := findFreePort()
|
|
62
|
-
if err != nil {
|
|
63
|
-
fmt.Fprintf(os.Stderr, "Failed to find free port: %v\n", err)
|
|
64
|
-
os.Exit(1)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
ctx, cancel := context.WithCancel(context.Background())
|
|
68
|
-
defer cancel()
|
|
69
|
-
|
|
70
|
-
cmd := exec.CommandContext(ctx, "go", "run", runTarget)
|
|
71
|
-
cmd.Env = withEnvOverride(os.Environ(), "PORT", strconv.Itoa(userPort))
|
|
72
|
-
cmd.Stdout = os.Stdout
|
|
73
|
-
cmd.Stderr = os.Stderr
|
|
74
|
-
|
|
75
|
-
if err := cmd.Start(); err != nil {
|
|
76
|
-
fmt.Fprintf(os.Stderr, "Failed to start Go dev server: %v\n", err)
|
|
77
|
-
os.Exit(1)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if err := waitForServer(userPort, 30*time.Second); err != nil {
|
|
81
|
-
fmt.Fprintf(os.Stderr, "Go dev server failed to start: %v\n", err)
|
|
82
|
-
_ = cmd.Process.Kill()
|
|
83
|
-
os.Exit(1)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
targetURL, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", userPort))
|
|
87
|
-
proxy := httputil.NewSingleHostReverseProxy(targetURL)
|
|
88
|
-
originalDirector := proxy.Director
|
|
89
|
-
proxy.Director = func(req *http.Request) {
|
|
90
|
-
originalDirector(req)
|
|
91
|
-
if host := req.Header.Get("X-Forwarded-Host"); host != "" {
|
|
92
|
-
req.Host = host
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
server := &http.Server{
|
|
97
|
-
Addr: fmt.Sprintf("127.0.0.1:%d", listenPort),
|
|
98
|
-
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
99
|
-
if r.URL.Path == "/_vercel/ping" {
|
|
100
|
-
w.WriteHeader(http.StatusOK)
|
|
101
|
-
w.Write([]byte("OK"))
|
|
102
|
-
return
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if r.URL != nil {
|
|
106
|
-
originalPath := r.URL.Path
|
|
107
|
-
r.URL.Path = stripServiceRoutePrefix(r.URL.Path, serviceRoutePrefix)
|
|
108
|
-
if r.URL.Path != originalPath {
|
|
109
|
-
r.URL.RawPath = ""
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
proxy.ServeHTTP(w, r)
|
|
114
|
-
}),
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
var closeOnce sync.Once
|
|
118
|
-
stop := func() {
|
|
119
|
-
closeOnce.Do(func() {
|
|
120
|
-
cancel()
|
|
121
|
-
_ = server.Close()
|
|
122
|
-
if cmd.Process != nil {
|
|
123
|
-
_ = cmd.Process.Kill()
|
|
124
|
-
}
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
go func() {
|
|
129
|
-
_ = cmd.Wait()
|
|
130
|
-
stop()
|
|
131
|
-
}()
|
|
132
|
-
|
|
133
|
-
sigCh := make(chan os.Signal, 1)
|
|
134
|
-
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
|
135
|
-
go func() {
|
|
136
|
-
<-sigCh
|
|
137
|
-
stop()
|
|
138
|
-
}()
|
|
139
|
-
|
|
140
|
-
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
141
|
-
fmt.Fprintf(os.Stderr, "Dev proxy server error: %v\n", err)
|
|
142
|
-
stop()
|
|
143
|
-
os.Exit(1)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
stop()
|
|
147
|
-
}
|