@open-wxa/wxa-runner-core 0.2.3 → 0.2.4
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/dist/{chunk-22T3D5KS.mjs → chunk-NGXYLLYS.mjs} +2 -2
- package/dist/chunk-NGXYLLYS.mjs.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +156 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -21
- package/dist/index.mjs.map +1 -1
- package/dist/setup.mjs +1 -1
- package/package.json +3 -3
- package/dist/chunk-22T3D5KS.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__dirname
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NGXYLLYS.mjs";
|
|
4
4
|
|
|
5
5
|
// src/server/DevServer.ts
|
|
6
|
-
import { randomUUID } from "crypto";
|
|
6
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
7
7
|
import fs4 from "fs";
|
|
8
8
|
import http from "http";
|
|
9
9
|
import path4 from "path";
|
|
10
10
|
import express2 from "express";
|
|
11
|
-
import { WebSocket, WebSocketServer } from "ws";
|
|
12
|
-
import {
|
|
11
|
+
import { WebSocket as WebSocket2, WebSocketServer } from "ws";
|
|
12
|
+
import { readRegionData, resolveBaseLib, usesIsolatedContext } from "@open-wxa/wxa-runner-extractor";
|
|
13
13
|
|
|
14
14
|
// src/project/AppBuilder.ts
|
|
15
15
|
import { compileWxml, compileWxss } from "@open-wxa/wxa-runner-compiler";
|
|
@@ -653,28 +653,164 @@ function planHotUpdate(files, units) {
|
|
|
653
653
|
return plan;
|
|
654
654
|
}
|
|
655
655
|
|
|
656
|
+
// src/server/developerTicket.ts
|
|
657
|
+
import { execFile } from "child_process";
|
|
658
|
+
import { randomUUID } from "crypto";
|
|
659
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
660
|
+
import { WebSocket } from "ws";
|
|
661
|
+
import { findDeveloperTicket, findElectronDevtoolsCli, TICKET_ENV } from "@open-wxa/wxa-runner-extractor";
|
|
662
|
+
var AUTO_PORT = 9420;
|
|
663
|
+
var CLI_TIMEOUT = 6e4;
|
|
664
|
+
var AUTOMATOR_TIMEOUT = 5e3;
|
|
665
|
+
var WINDOW_TIMEOUT = 1e4;
|
|
666
|
+
var WINDOW_RETRY_INTERVAL = 500;
|
|
667
|
+
function developerTicketSource(projectPath, logger) {
|
|
668
|
+
const cli = findElectronDevtoolsCli();
|
|
669
|
+
if (!cli) return async () => findDeveloperTicket();
|
|
670
|
+
let automatorPort = null;
|
|
671
|
+
const startOnce = () => {
|
|
672
|
+
if (!automatorPort) {
|
|
673
|
+
const starting = startAutomation(cli, projectPath).then((port) => {
|
|
674
|
+
logger.log(`[wxa-runner] \u771F\u5B9E\u767B\u5F55\uFF1A\u5DF2\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u4E3A\u9879\u76EE\u5F00\u542F\u81EA\u52A8\u5316\u63A5\u53E3\uFF08\u7AEF\u53E3 ${port}\uFF09\uFF0Cticket \u4ECE\u5F00\u53D1\u8005\u5DE5\u5177\u5B9E\u65F6\u83B7\u53D6`);
|
|
675
|
+
return port;
|
|
676
|
+
});
|
|
677
|
+
starting.catch(() => {
|
|
678
|
+
if (automatorPort === starting) automatorPort = null;
|
|
679
|
+
});
|
|
680
|
+
automatorPort = starting;
|
|
681
|
+
}
|
|
682
|
+
return automatorPort;
|
|
683
|
+
};
|
|
684
|
+
return async () => {
|
|
685
|
+
if (process.env[TICKET_ENV]) return { newticket: process.env[TICKET_ENV] };
|
|
686
|
+
const port = startOnce();
|
|
687
|
+
try {
|
|
688
|
+
return await requestTicketOnceWindowReady(await port, projectPath);
|
|
689
|
+
} catch (error) {
|
|
690
|
+
if (!(error instanceof AutomatorGone)) throw error;
|
|
691
|
+
if (automatorPort === port) automatorPort = null;
|
|
692
|
+
return requestTicketOnceWindowReady(await startOnce(), projectPath);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
var AutomatorGone = class extends Error {
|
|
697
|
+
};
|
|
698
|
+
var WindowNotFound = class extends Error {
|
|
699
|
+
};
|
|
700
|
+
async function requestTicketOnceWindowReady(port, projectPath) {
|
|
701
|
+
const deadline = Date.now() + WINDOW_TIMEOUT;
|
|
702
|
+
for (; ; ) {
|
|
703
|
+
try {
|
|
704
|
+
return await requestTicket(port);
|
|
705
|
+
} catch (error) {
|
|
706
|
+
if (!(error instanceof WindowNotFound)) throw error;
|
|
707
|
+
if (Date.now() >= deadline) throw new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u627E\u4E0D\u5230\u9879\u76EE\u7A97\u53E3\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u6253\u5F00\u9879\u76EE ${projectPath}`);
|
|
708
|
+
await sleep(WINDOW_RETRY_INTERVAL);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
async function startAutomation(cli, projectPath) {
|
|
713
|
+
const stdout = await runCli(cli, ["agent", "start", "--project", projectPath, "--auto-port", String(AUTO_PORT), "--lang", "zh"]);
|
|
714
|
+
let autoPort = NaN;
|
|
715
|
+
try {
|
|
716
|
+
autoPort = Number(JSON.parse(stdout.match(/^\{$[\s\S]*?^\}$/m)?.[0] ?? "").autoPort);
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
if (Number.isInteger(autoPort) && autoPort > 0) return autoPort;
|
|
720
|
+
if (stdout.includes("\u670D\u52A1\u7AEF\u53E3\u5DF2\u5173\u95ED")) {
|
|
721
|
+
throw new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u670D\u52A1\u7AEF\u53E3\u5DF2\u5173\u95ED\uFF0C\u8BF7\u5728\u5F00\u53D1\u8005\u5DE5\u5177\u300C\u8BBE\u7F6E \u2192 \u5B89\u5168\u8BBE\u7F6E\u300D\u91CC\u5F00\u542F\u670D\u52A1\u7AEF\u53E3");
|
|
722
|
+
}
|
|
723
|
+
throw new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u6CA1\u80FD\u4E3A\u9879\u76EE\u5F00\u542F\u81EA\u52A8\u5316\u63A5\u53E3\uFF1A${cliErrorMessage(stdout)}`);
|
|
724
|
+
}
|
|
725
|
+
function runCli(cli, args) {
|
|
726
|
+
const bat = cli.endsWith(".bat");
|
|
727
|
+
const [file, fileArgs] = bat ? [`"${cli}"`, args.map((arg) => `"${arg}"`)] : [cli, args];
|
|
728
|
+
return new Promise((resolve, reject) => {
|
|
729
|
+
const child = execFile(file, fileArgs, { shell: bat, timeout: CLI_TIMEOUT, windowsHide: true }, (error, stdout) => {
|
|
730
|
+
if (error?.killed) {
|
|
731
|
+
reject(new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u547D\u4EE4\u884C ${CLI_TIMEOUT / 1e3}s \u5185\u6CA1\u6709\u5B8C\u6210\uFF0C\u8BF7\u770B\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u662F\u5426\u6709\u7B49\u5F85\u786E\u8BA4\u7684\u5F39\u7A97\uFF08\u5982\u4FE1\u4EFB\u9879\u76EE\u3001\u626B\u7801\u767B\u5F55\uFF09`));
|
|
732
|
+
} else if (error && !stdout) {
|
|
733
|
+
reject(new Error(`\u8FD0\u884C\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u547D\u4EE4\u884C\u5931\u8D25\uFF1A${error.message}`));
|
|
734
|
+
} else {
|
|
735
|
+
resolve(stdout);
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
child.stdin?.end();
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
function cliErrorMessage(stdout) {
|
|
742
|
+
const output = stdout.replace(/\x1b\[[0-9;]*m/g, "");
|
|
743
|
+
const start = output.indexOf("[error]");
|
|
744
|
+
const error = (start < 0 ? output : output.slice(start + "[error]".length)).trim();
|
|
745
|
+
const message = error.match(/message: (['"`])((?:\\.|(?!\1)[^\\])*?)(?:\\n|\1)/)?.[2];
|
|
746
|
+
return message ?? (error.replace(/\s+/g, " ").slice(0, 200) || "\u547D\u4EE4\u884C\u6CA1\u6709\u8F93\u51FA");
|
|
747
|
+
}
|
|
748
|
+
function requestTicket(port) {
|
|
749
|
+
return new Promise((resolve, reject) => {
|
|
750
|
+
const id = `wxa-runner-${randomUUID()}`;
|
|
751
|
+
const socket = new WebSocket(`ws://127.0.0.1:${port}`);
|
|
752
|
+
const settle = (finish) => {
|
|
753
|
+
clearTimeout(timer);
|
|
754
|
+
socket.removeAllListeners().on("error", () => void 0).terminate();
|
|
755
|
+
finish();
|
|
756
|
+
};
|
|
757
|
+
const timer = setTimeout(
|
|
758
|
+
() => settle(
|
|
759
|
+
() => reject(new AutomatorGone(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u81EA\u52A8\u5316\u63A5\u53E3 ${AUTOMATOR_TIMEOUT / 1e3}s \u5185\u6CA1\u6709\u54CD\u5E94\uFF0C\u8BF7\u786E\u8BA4\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u6253\u5F00\u4E86\u9879\u76EE\u7A97\u53E3`))
|
|
760
|
+
),
|
|
761
|
+
AUTOMATOR_TIMEOUT
|
|
762
|
+
);
|
|
763
|
+
const gone = () => settle(() => reject(new AutomatorGone(`\u8FDE\u4E0D\u4E0A\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u81EA\u52A8\u5316\u63A5\u53E3\uFF08\u7AEF\u53E3 ${port}\uFF09\uFF0C\u8BF7\u786E\u8BA4\u5F00\u53D1\u8005\u5DE5\u5177\u5728\u8FD0\u884C`)));
|
|
764
|
+
socket.on("error", gone);
|
|
765
|
+
socket.on("close", gone);
|
|
766
|
+
socket.on("open", () => socket.send(JSON.stringify({ id, method: "Tool.getTicket", params: {} })));
|
|
767
|
+
socket.on("message", (data) => {
|
|
768
|
+
let message;
|
|
769
|
+
try {
|
|
770
|
+
message = JSON.parse(String(data));
|
|
771
|
+
} catch {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (message?.id !== id) return;
|
|
775
|
+
if (message.error) settle(() => reject(explainAutomatorError(String(message.error.message))));
|
|
776
|
+
else if (typeof message.result?.ticket !== "string") settle(() => reject(new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u6CA1\u6709\u8FD4\u56DE\u767B\u5F55\u7968\u636E")));
|
|
777
|
+
else settle(() => resolve({ newticket: message.result.ticket, expiredAt: message.result.expiredTime }));
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
function explainAutomatorError(message) {
|
|
782
|
+
if (message.includes("GUI project window is not found")) return new WindowNotFound(message);
|
|
783
|
+
if (message.includes("not allow to get")) {
|
|
784
|
+
return new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u4E0D\u5141\u8BB8\u83B7\u53D6\u767B\u5F55\u7968\u636E\uFF0C\u8BF7\u5728\u5F00\u53D1\u8005\u5DE5\u5177\u300C\u8BBE\u7F6E \u2192 \u5B89\u5168\u8BBE\u7F6E\u300D\u91CC\u6253\u5F00\u300C\u5141\u8BB8\u83B7\u53D6\u5DE5\u5177\u767B\u5F55\u7968\u636E\u300D");
|
|
785
|
+
}
|
|
786
|
+
if (message.includes("need to login")) return new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u5728\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u767B\u5F55");
|
|
787
|
+
return new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u8FD4\u56DE\u9519\u8BEF\uFF1A${message}`);
|
|
788
|
+
}
|
|
789
|
+
|
|
656
790
|
// src/server/realLogin.ts
|
|
657
791
|
import express from "express";
|
|
658
|
-
import { findDeveloperTicket, TICKET_ENV } from "@open-wxa/wxa-runner-extractor";
|
|
792
|
+
import { findDeveloperTicket as findDeveloperTicket2, TICKET_ENV as TICKET_ENV2 } from "@open-wxa/wxa-runner-extractor";
|
|
659
793
|
var DEV_LOGIC = "https://servicewechat.com/wxa-dev-logic";
|
|
660
794
|
var CLIENT_VERSION = "1021810240";
|
|
661
795
|
var UPSTREAM_TIMEOUT = 1e4;
|
|
662
796
|
var LOOPBACK_ADDRESSES = ["127.0.0.1", "::1", "::ffff:127.0.0.1"];
|
|
663
797
|
var LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
|
|
664
798
|
var ERROR_HINTS = {
|
|
799
|
+
// 40001:开发者工具刷新过 ticket,旧的随即作废
|
|
800
|
+
40001: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
|
|
665
801
|
40029: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
|
|
666
802
|
85023: "\uFF08\u5F53\u524D\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u662F\u8BE5\u5C0F\u7A0B\u5E8F\u7684\u5F00\u53D1\u8005\uFF09"
|
|
667
803
|
};
|
|
668
804
|
function requireUsableTicket(ticket) {
|
|
669
|
-
if (!ticket) throw new Error(`\u672A\u627E\u5230\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\uFF0C\u8BF7\u767B\u5F55\u5F00\u53D1\u8005\u5DE5\u5177\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${
|
|
805
|
+
if (!ticket) throw new Error(`\u672A\u627E\u5230\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\uFF0C\u8BF7\u767B\u5F55\u5F00\u53D1\u8005\u5DE5\u5177\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${TICKET_ENV2}`);
|
|
670
806
|
if (ticket.expiredAt !== void 0 && ticket.expiredAt <= Date.now()) {
|
|
671
807
|
throw new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u6253\u5F00\u5F00\u53D1\u8005\u5DE5\u5177\u5237\u65B0\u767B\u5F55\u6001");
|
|
672
808
|
}
|
|
673
809
|
return ticket.newticket;
|
|
674
810
|
}
|
|
675
|
-
function realLoginRouter(appid, findTicket =
|
|
811
|
+
function realLoginRouter(appid, findTicket = findDeveloperTicket2) {
|
|
676
812
|
const callDevLogic = async (endpoint, body) => {
|
|
677
|
-
const query = new URLSearchParams({ appid, newticket: requireUsableTicket(findTicket()), clientversion: CLIENT_VERSION });
|
|
813
|
+
const query = new URLSearchParams({ appid, newticket: requireUsableTicket(await findTicket()), clientversion: CLIENT_VERSION });
|
|
678
814
|
const response = await fetch(`${DEV_LOGIC}/${endpoint}?${query}`, {
|
|
679
815
|
method: "POST",
|
|
680
816
|
headers: { "Content-Type": "application/json" },
|
|
@@ -853,13 +989,7 @@ var DevServer = class {
|
|
|
853
989
|
if (!this.baseLib.compatible) {
|
|
854
990
|
this.logger.warn(`[wxa-runner] \u57FA\u7840\u5E93 ${this.baseLib.version} \u9AD8\u4E8E\u5DF2\u9A8C\u8BC1\u7684\u7248\u672C\u8303\u56F4\uFF0C\u53EF\u80FD\u65E0\u6CD5\u6B63\u5E38\u8FD0\u884C`);
|
|
855
991
|
}
|
|
856
|
-
|
|
857
|
-
try {
|
|
858
|
-
requireUsableTicket(findDeveloperTicket2());
|
|
859
|
-
} catch (error) {
|
|
860
|
-
this.logger.warn(`[wxa-runner] \u771F\u5B9E\u767B\u5F55\u6682\u4E0D\u53EF\u7528\uFF1A${error.message}`);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
992
|
+
this.ticketSource = options.realLogin ? developerTicketSource(this.project.projectPath, this.logger) : null;
|
|
863
993
|
this.device = { ...DEFAULT_DEVICE, ...options.device };
|
|
864
994
|
this.builder = new AppBuilder(this.project);
|
|
865
995
|
this.setupFile = resolveSetupFile(options.setup, this.project.projectPath);
|
|
@@ -895,6 +1025,7 @@ ${error.message}`);
|
|
|
895
1025
|
const { port } = server.address();
|
|
896
1026
|
this.address = `http://${host}:${port}`;
|
|
897
1027
|
if (this.options.watch !== false) this.startWatching();
|
|
1028
|
+
this.ticketSource?.().then(requireUsableTicket).catch((error) => this.logger.warn(`[wxa-runner] \u771F\u5B9E\u767B\u5F55\u6682\u4E0D\u53EF\u7528\uFF1A${error.message}`));
|
|
898
1029
|
return this.address;
|
|
899
1030
|
}
|
|
900
1031
|
async close() {
|
|
@@ -1005,18 +1136,18 @@ ${error.message}`);
|
|
|
1005
1136
|
res.status(400).type("text").send(`\u975E\u6CD5\u7684\u6587\u4EF6\u7C7B\u578B ${type}`);
|
|
1006
1137
|
return;
|
|
1007
1138
|
}
|
|
1008
|
-
const name = `${
|
|
1139
|
+
const name = `${randomUUID2()}.${type}`;
|
|
1009
1140
|
this.tempFiles.set(name, Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0));
|
|
1010
1141
|
res.json({ url: `${req.protocol}://${req.get("host")}/__wxa__/tmp/${name}` });
|
|
1011
1142
|
});
|
|
1012
1143
|
app.get("/__wxa__/region/:kind", (req, res) => {
|
|
1013
1144
|
const kind = req.params.kind;
|
|
1014
|
-
const
|
|
1015
|
-
if (!
|
|
1145
|
+
const data = kind === "districts" || kind === "streets" ? readRegionData(kind) : null;
|
|
1146
|
+
if (!data) {
|
|
1016
1147
|
res.status(404).end();
|
|
1017
1148
|
return;
|
|
1018
1149
|
}
|
|
1019
|
-
res.type("text/plain; charset=utf-8").
|
|
1150
|
+
res.type("text/plain; charset=utf-8").set("Cache-Control", "public, max-age=86400").send(data);
|
|
1020
1151
|
});
|
|
1021
1152
|
app.get("/__wxa__/tmp/:name", (req, res) => {
|
|
1022
1153
|
const data = this.tempFiles.get(req.params.name);
|
|
@@ -1029,7 +1160,7 @@ ${error.message}`);
|
|
|
1029
1160
|
app.delete("/__wxa__/tmp/:name", (req, res) => {
|
|
1030
1161
|
res.status(this.tempFiles.delete(req.params.name) ? 204 : 404).end();
|
|
1031
1162
|
});
|
|
1032
|
-
if (this.
|
|
1163
|
+
if (this.ticketSource) app.use("/__wxa__/api", realLoginRouter(this.project.appid, this.ticketSource));
|
|
1033
1164
|
app.use("/__wxa__/webview", express2.static(root));
|
|
1034
1165
|
app.use(express2.static(root));
|
|
1035
1166
|
app.use((error, _req, res, _next) => {
|
|
@@ -1108,7 +1239,7 @@ ${message}`);
|
|
|
1108
1239
|
broadcast(message) {
|
|
1109
1240
|
const data = JSON.stringify(message);
|
|
1110
1241
|
this.sockets?.clients.forEach((client) => {
|
|
1111
|
-
if (client.readyState ===
|
|
1242
|
+
if (client.readyState === WebSocket2.OPEN) client.send(data);
|
|
1112
1243
|
});
|
|
1113
1244
|
}
|
|
1114
1245
|
};
|