qualty 0.1.32 → 0.1.34
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/local-runner.js +47 -9
- package/bin/qualty.js +2 -2
- package/package.json +1 -1
package/bin/local-runner.js
CHANGED
|
@@ -109,17 +109,38 @@ function startCloudflaredForPort(token, port) {
|
|
|
109
109
|
return cmd;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
function preferIpv4Addresses(addresses) {
|
|
113
|
+
const ipv4 = addresses.filter((entry) => entry.family === 4);
|
|
114
|
+
return ipv4.length > 0 ? ipv4 : addresses;
|
|
115
|
+
}
|
|
116
|
+
|
|
112
117
|
async function resolvePublicHostname(hostname) {
|
|
113
118
|
const [ipv4, ipv6] = await Promise.allSettled([resolve4(hostname), resolve6(hostname)]);
|
|
114
|
-
|
|
119
|
+
let addresses = [
|
|
115
120
|
...(ipv4.status === "fulfilled" ? ipv4.value.map((address) => ({ address, family: 4 })) : []),
|
|
116
121
|
...(ipv6.status === "fulfilled" ? ipv6.value.map((address) => ({ address, family: 6 })) : []),
|
|
117
122
|
].sort((a, b) => a.family - b.family);
|
|
118
|
-
|
|
123
|
+
|
|
124
|
+
// System resolvers sometimes return only AAAA before A records propagate; prefer DoH A records.
|
|
125
|
+
if (!addresses.some((entry) => entry.family === 4)) {
|
|
126
|
+
try {
|
|
127
|
+
const dohAddresses = await resolvePublicHostnameViaDoh(hostname);
|
|
128
|
+
const dohIpv4 = dohAddresses.filter((entry) => entry.family === 4);
|
|
129
|
+
if (dohIpv4.length > 0) {
|
|
130
|
+
addresses = [...dohIpv4, ...addresses.filter((entry) => entry.family === 6)];
|
|
131
|
+
} else if (addresses.length === 0 && dohAddresses.length > 0) {
|
|
132
|
+
addresses = dohAddresses;
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
/* Fall through to errors below when nothing resolved. */
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (addresses.length > 0) return preferIpv4Addresses(addresses);
|
|
119
140
|
|
|
120
141
|
try {
|
|
121
142
|
const dohAddresses = await resolvePublicHostnameViaDoh(hostname);
|
|
122
|
-
if (dohAddresses.length > 0) return dohAddresses;
|
|
143
|
+
if (dohAddresses.length > 0) return preferIpv4Addresses(dohAddresses);
|
|
123
144
|
} catch {
|
|
124
145
|
/* Surface the original resolver error below; it is usually more familiar. */
|
|
125
146
|
}
|
|
@@ -154,11 +175,21 @@ async function resolvePublicHostnameViaDoh(hostname) {
|
|
|
154
175
|
}
|
|
155
176
|
|
|
156
177
|
function probeWebSocketUpgrade(connectUrl, addresses, timeoutMs = 10000) {
|
|
178
|
+
const routable = preferIpv4Addresses(addresses);
|
|
157
179
|
return new Promise((resolve, reject) => {
|
|
180
|
+
let settled = false;
|
|
181
|
+
const finish = (err) => {
|
|
182
|
+
if (settled) return;
|
|
183
|
+
settled = true;
|
|
184
|
+
if (err) reject(err);
|
|
185
|
+
else resolve();
|
|
186
|
+
};
|
|
187
|
+
|
|
158
188
|
const url = new URL(connectUrl);
|
|
159
189
|
const req = httpsRequest({
|
|
160
190
|
protocol: url.protocol === "wss:" ? "https:" : url.protocol,
|
|
161
191
|
hostname: url.hostname,
|
|
192
|
+
servername: url.hostname,
|
|
162
193
|
port: url.port || 443,
|
|
163
194
|
path: `${url.pathname || "/"}${url.search || ""}`,
|
|
164
195
|
method: "GET",
|
|
@@ -167,18 +198,22 @@ function probeWebSocketUpgrade(connectUrl, addresses, timeoutMs = 10000) {
|
|
|
167
198
|
Upgrade: "websocket",
|
|
168
199
|
"Sec-WebSocket-Key": randomBytes(16).toString("base64"),
|
|
169
200
|
"Sec-WebSocket-Version": "13",
|
|
201
|
+
Host: url.host,
|
|
170
202
|
},
|
|
171
203
|
timeout: timeoutMs,
|
|
172
204
|
lookup: (_hostname, options, callback) => {
|
|
173
205
|
const cb = typeof options === "function" ? options : callback;
|
|
174
206
|
const lookupOptions = typeof options === "object" && options ? options : {};
|
|
175
|
-
const picked =
|
|
207
|
+
const picked = routable[0];
|
|
176
208
|
if (!picked?.address || !picked?.family) {
|
|
177
209
|
cb(new Error("No resolved tunnel addresses available"));
|
|
178
210
|
return;
|
|
179
211
|
}
|
|
180
212
|
if (lookupOptions.all) {
|
|
181
|
-
cb(
|
|
213
|
+
cb(
|
|
214
|
+
null,
|
|
215
|
+
routable.map((entry) => ({ address: entry.address, family: entry.family }))
|
|
216
|
+
);
|
|
182
217
|
return;
|
|
183
218
|
}
|
|
184
219
|
cb(null, picked.address, picked.family);
|
|
@@ -187,14 +222,17 @@ function probeWebSocketUpgrade(connectUrl, addresses, timeoutMs = 10000) {
|
|
|
187
222
|
|
|
188
223
|
req.on("upgrade", (_res, socket) => {
|
|
189
224
|
socket.destroy();
|
|
190
|
-
|
|
225
|
+
finish();
|
|
191
226
|
});
|
|
192
227
|
req.on("response", (res) => {
|
|
193
228
|
res.resume();
|
|
194
|
-
|
|
229
|
+
finish(new Error(`WebSocket upgrade returned HTTP ${res.statusCode}`));
|
|
195
230
|
});
|
|
196
231
|
req.on("timeout", () => req.destroy(new Error(`WebSocket upgrade timed out after ${timeoutMs}ms`)));
|
|
197
|
-
req.on("error",
|
|
232
|
+
req.on("error", finish);
|
|
233
|
+
req.on("socket", (socket) => {
|
|
234
|
+
socket.on("error", finish);
|
|
235
|
+
});
|
|
198
236
|
req.end();
|
|
199
237
|
});
|
|
200
238
|
}
|
|
@@ -207,7 +245,7 @@ async function waitForPublicBrowserTunnel({ connectUrl, hostname, timeoutMs = 60
|
|
|
207
245
|
while (Date.now() < deadline) {
|
|
208
246
|
attempts += 1;
|
|
209
247
|
try {
|
|
210
|
-
const addresses = await resolvePublicHostname(hostname);
|
|
248
|
+
const addresses = preferIpv4Addresses(await resolvePublicHostname(hostname));
|
|
211
249
|
// eslint-disable-next-line no-console
|
|
212
250
|
console.log(
|
|
213
251
|
`[qualty][tunnel] DNS ready for ${hostname}: ${addresses.map((a) => a.address).join(", ")}`
|
package/bin/qualty.js
CHANGED
|
@@ -27,9 +27,9 @@ import {
|
|
|
27
27
|
} from "./auth-ci-bundle.js";
|
|
28
28
|
|
|
29
29
|
/** CLI backend URL is fixed by Qualty distribution. */
|
|
30
|
-
|
|
30
|
+
const DEFAULT_QUALTY_API_URL = "https://qualty-api-production.up.railway.app";
|
|
31
|
+
// const DEFAULT_QUALTY_API_URL = "https://qualty-api-development.up.railway.app";
|
|
31
32
|
// const DEFAULT_QUALTY_API_URL = "http://localhost:8000";
|
|
32
|
-
const DEFAULT_QUALTY_API_URL = "https://qualty-api-development.up.railway.app";
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
const PROJECT_CONFIG_FILENAME = ".qualty.json";
|