create-windy 0.1.1 → 0.2.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/README.md +31 -7
- package/dist/cli.js +2788 -94
- package/package.json +7 -4
- package/template/.husky/pre-commit +15 -0
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +28 -75
- package/template/README.md +20 -6
- package/template/_gitignore +2 -0
- package/template/package.json +5 -0
- /package/{assets/LICENSE-Apache-2.0.txt → LICENSE} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,197 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
13
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
21
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
31
|
+
return to;
|
|
32
|
+
};
|
|
33
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
35
|
+
|
|
36
|
+
// ../../node_modules/.bun/cli-width@4.1.0/node_modules/cli-width/index.js
|
|
37
|
+
var require_cli_width = __commonJS((exports, module3) => {
|
|
38
|
+
module3.exports = cliWidth;
|
|
39
|
+
function normalizeOpts(options) {
|
|
40
|
+
const defaultOpts = {
|
|
41
|
+
defaultWidth: 0,
|
|
42
|
+
output: process.stdout,
|
|
43
|
+
tty: __require("tty")
|
|
44
|
+
};
|
|
45
|
+
if (!options) {
|
|
46
|
+
return defaultOpts;
|
|
47
|
+
}
|
|
48
|
+
Object.keys(defaultOpts).forEach(function(key) {
|
|
49
|
+
if (!options[key]) {
|
|
50
|
+
options[key] = defaultOpts[key];
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return options;
|
|
54
|
+
}
|
|
55
|
+
function cliWidth(options) {
|
|
56
|
+
const opts = normalizeOpts(options);
|
|
57
|
+
if (opts.output.getWindowSize) {
|
|
58
|
+
return opts.output.getWindowSize()[0] || opts.defaultWidth;
|
|
59
|
+
}
|
|
60
|
+
if (opts.tty.getWindowSize) {
|
|
61
|
+
return opts.tty.getWindowSize()[1] || opts.defaultWidth;
|
|
62
|
+
}
|
|
63
|
+
if (opts.output.columns) {
|
|
64
|
+
return opts.output.columns;
|
|
65
|
+
}
|
|
66
|
+
if (process.env.CLI_WIDTH) {
|
|
67
|
+
const width = parseInt(process.env.CLI_WIDTH, 10);
|
|
68
|
+
if (!isNaN(width) && width !== 0) {
|
|
69
|
+
return width;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return opts.defaultWidth;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ../../node_modules/.bun/mute-stream@3.0.0/node_modules/mute-stream/lib/index.js
|
|
77
|
+
var require_lib = __commonJS((exports, module3) => {
|
|
78
|
+
var Stream = __require("stream");
|
|
79
|
+
|
|
80
|
+
class MuteStream extends Stream {
|
|
81
|
+
#isTTY = null;
|
|
82
|
+
constructor(opts = {}) {
|
|
83
|
+
super(opts);
|
|
84
|
+
this.writable = this.readable = true;
|
|
85
|
+
this.muted = false;
|
|
86
|
+
this.on("pipe", this._onpipe);
|
|
87
|
+
this.replace = opts.replace;
|
|
88
|
+
this._prompt = opts.prompt || null;
|
|
89
|
+
this._hadControl = false;
|
|
90
|
+
}
|
|
91
|
+
#destSrc(key, def) {
|
|
92
|
+
if (this._dest) {
|
|
93
|
+
return this._dest[key];
|
|
94
|
+
}
|
|
95
|
+
if (this._src) {
|
|
96
|
+
return this._src[key];
|
|
97
|
+
}
|
|
98
|
+
return def;
|
|
99
|
+
}
|
|
100
|
+
#proxy(method, ...args) {
|
|
101
|
+
if (typeof this._dest?.[method] === "function") {
|
|
102
|
+
this._dest[method](...args);
|
|
103
|
+
}
|
|
104
|
+
if (typeof this._src?.[method] === "function") {
|
|
105
|
+
this._src[method](...args);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
get isTTY() {
|
|
109
|
+
if (this.#isTTY !== null) {
|
|
110
|
+
return this.#isTTY;
|
|
111
|
+
}
|
|
112
|
+
return this.#destSrc("isTTY", false);
|
|
113
|
+
}
|
|
114
|
+
set isTTY(val) {
|
|
115
|
+
this.#isTTY = val;
|
|
116
|
+
}
|
|
117
|
+
get rows() {
|
|
118
|
+
return this.#destSrc("rows");
|
|
119
|
+
}
|
|
120
|
+
get columns() {
|
|
121
|
+
return this.#destSrc("columns");
|
|
122
|
+
}
|
|
123
|
+
mute() {
|
|
124
|
+
this.muted = true;
|
|
125
|
+
}
|
|
126
|
+
unmute() {
|
|
127
|
+
this.muted = false;
|
|
128
|
+
}
|
|
129
|
+
_onpipe(src) {
|
|
130
|
+
this._src = src;
|
|
131
|
+
}
|
|
132
|
+
pipe(dest, options) {
|
|
133
|
+
this._dest = dest;
|
|
134
|
+
return super.pipe(dest, options);
|
|
135
|
+
}
|
|
136
|
+
pause() {
|
|
137
|
+
if (this._src) {
|
|
138
|
+
return this._src.pause();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
resume() {
|
|
142
|
+
if (this._src) {
|
|
143
|
+
return this._src.resume();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
write(c) {
|
|
147
|
+
if (this.muted) {
|
|
148
|
+
if (!this.replace) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
if (c.match(/^\u001b/)) {
|
|
152
|
+
if (c.indexOf(this._prompt) === 0) {
|
|
153
|
+
c = c.slice(this._prompt.length);
|
|
154
|
+
c = c.replace(/./g, this.replace);
|
|
155
|
+
c = this._prompt + c;
|
|
156
|
+
}
|
|
157
|
+
this._hadControl = true;
|
|
158
|
+
return this.emit("data", c);
|
|
159
|
+
} else {
|
|
160
|
+
if (this._prompt && this._hadControl && c.indexOf(this._prompt) === 0) {
|
|
161
|
+
this._hadControl = false;
|
|
162
|
+
this.emit("data", this._prompt);
|
|
163
|
+
c = c.slice(this._prompt.length);
|
|
164
|
+
}
|
|
165
|
+
c = c.toString().replace(/./g, this.replace);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
this.emit("data", c);
|
|
169
|
+
}
|
|
170
|
+
end(c) {
|
|
171
|
+
if (this.muted) {
|
|
172
|
+
if (c && this.replace) {
|
|
173
|
+
c = c.toString().replace(/./g, this.replace);
|
|
174
|
+
} else {
|
|
175
|
+
c = null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (c) {
|
|
179
|
+
this.emit("data", c);
|
|
180
|
+
}
|
|
181
|
+
this.emit("end");
|
|
182
|
+
}
|
|
183
|
+
destroy(...args) {
|
|
184
|
+
return this.#proxy("destroy", ...args);
|
|
185
|
+
}
|
|
186
|
+
destroySoon(...args) {
|
|
187
|
+
return this.#proxy("destroySoon", ...args);
|
|
188
|
+
}
|
|
189
|
+
close(...args) {
|
|
190
|
+
return this.#proxy("close", ...args);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
module3.exports = MuteStream;
|
|
194
|
+
});
|
|
2
195
|
|
|
3
196
|
// src/arguments.ts
|
|
4
197
|
import { resolve } from "node:path";
|
|
@@ -495,6 +688,12 @@ function readProjectLicense(args) {
|
|
|
495
688
|
}
|
|
496
689
|
var helpText = `create-windy <项目名> [选项]
|
|
497
690
|
|
|
691
|
+
生命周期命令:
|
|
692
|
+
create-windy doctor [--cwd <目录>] [--repair]
|
|
693
|
+
create-windy update --check [--cwd <目录>]
|
|
694
|
+
create-windy update --dry-run [--to <版本>] [--cwd <目录>]
|
|
695
|
+
create-windy update [--to <版本>] [--cwd <目录>]
|
|
696
|
+
|
|
498
697
|
选项:
|
|
499
698
|
--profile private-enterprise 单组织、单租户、私有部署 profile
|
|
500
699
|
--skip-install 只生成文件,不执行 bun install
|
|
@@ -606,7 +805,7 @@ function isMissingCommand(error) {
|
|
|
606
805
|
}
|
|
607
806
|
|
|
608
807
|
// src/git.ts
|
|
609
|
-
async function initializeGitRepository(targetDirectory, execute = runCommand) {
|
|
808
|
+
async function initializeGitRepository(targetDirectory, execute = runCommand, activateHooks = false) {
|
|
610
809
|
try {
|
|
611
810
|
await execute("git", ["init", "-b", "main"], targetDirectory);
|
|
612
811
|
} catch (error) {
|
|
@@ -615,6 +814,16 @@ async function initializeGitRepository(targetDirectory, execute = runCommand) {
|
|
|
615
814
|
warning: `未能初始化 Git 仓库,项目文件已保留。${recoveryHint(error)}`
|
|
616
815
|
};
|
|
617
816
|
}
|
|
817
|
+
if (activateHooks) {
|
|
818
|
+
try {
|
|
819
|
+
await execute("bun", ["run", "prepare"], targetDirectory);
|
|
820
|
+
} catch (error) {
|
|
821
|
+
return {
|
|
822
|
+
status: "skipped",
|
|
823
|
+
warning: "Git 仓库已初始化,但提交钩子未能启用。请安装依赖后运行 bun run prepare。" + compactReason(error)
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
}
|
|
618
827
|
try {
|
|
619
828
|
await execute("git", ["add", "--all"], targetDirectory);
|
|
620
829
|
} catch (error) {
|
|
@@ -681,8 +890,9 @@ function rewriteCustomerDockerfile(source) {
|
|
|
681
890
|
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
682
891
|
import { join as join2 } from "node:path";
|
|
683
892
|
var oxcStarterPolicy = [
|
|
684
|
-
"-
|
|
685
|
-
"-
|
|
893
|
+
"- 生成时默认预装 Oxlint、Oxfmt、lint-staged 与 Husky。",
|
|
894
|
+
"- 提交前自动格式化暂存文件;lint、typecheck 和 test 保留为显式命令。",
|
|
895
|
+
"- 这是 Starter 的初始配置;项目所有者可按团队需要保留、替换或扩展。"
|
|
686
896
|
].join(`
|
|
687
897
|
`);
|
|
688
898
|
var noCodeQualityPolicy = "- 本项目生成时未引入 lint 或 format 工具;项目所有者可按团队需要自行选择。";
|
|
@@ -702,7 +912,13 @@ import { mkdir, readFile as readFile3, rm, writeFile as writeFile3 } from "node:
|
|
|
702
912
|
import { dirname, join as join3, resolve as resolve2 } from "node:path";
|
|
703
913
|
import { fileURLToPath } from "node:url";
|
|
704
914
|
var packageRoot = resolve2(dirname(fileURLToPath(import.meta.url)), "..");
|
|
705
|
-
var apacheLicensePath = resolve2(packageRoot, "
|
|
915
|
+
var apacheLicensePath = resolve2(packageRoot, "LICENSE");
|
|
916
|
+
var windyLicensePath = ".windy/licenses/WINDY-APACHE-2.0.txt";
|
|
917
|
+
async function applyWindyBaseLicense(targetDirectory) {
|
|
918
|
+
const target = join3(targetDirectory, windyLicensePath);
|
|
919
|
+
await mkdir(dirname(target), { recursive: true });
|
|
920
|
+
await writeFile3(target, await readFile3(apacheLicensePath));
|
|
921
|
+
}
|
|
706
922
|
async function applyProjectLicense(targetDirectory, license, holder, year = new Date().getFullYear()) {
|
|
707
923
|
const target = join3(targetDirectory, "LICENSE");
|
|
708
924
|
if (license === "UNLICENSED") {
|
|
@@ -751,7 +967,11 @@ import { lstat, readdir, readFile as readFile4, writeFile as writeFile4 } from "
|
|
|
751
967
|
import { join as join4, relative, sep } from "node:path";
|
|
752
968
|
var managedFilesManifestPath = ".windy/files.json";
|
|
753
969
|
var ignoredDirectoryNames = new Set([".git", "node_modules", "dist"]);
|
|
754
|
-
var ignoredFiles = new Set([
|
|
970
|
+
var ignoredFiles = new Set([
|
|
971
|
+
"bun.lock",
|
|
972
|
+
managedFilesManifestPath,
|
|
973
|
+
".windy/update-state.json"
|
|
974
|
+
]);
|
|
755
975
|
var projectOwnedRoots = [
|
|
756
976
|
"apps/web/src/app",
|
|
757
977
|
"apps/server/src/work-order",
|
|
@@ -784,6 +1004,16 @@ async function writeManagedFilesManifest(targetDirectory, generatorVersion) {
|
|
|
784
1004
|
await writeFile4(join4(targetDirectory, managedFilesManifestPath), `${JSON.stringify(manifest, null, 2)}
|
|
785
1005
|
`);
|
|
786
1006
|
}
|
|
1007
|
+
async function readManagedFilesManifest(targetDirectory) {
|
|
1008
|
+
const parsed = JSON.parse(await readFile4(join4(targetDirectory, managedFilesManifestPath), "utf8"));
|
|
1009
|
+
if (!isManagedFilesManifest(parsed)) {
|
|
1010
|
+
throw new Error("受管文件清单无效,无法安全更新");
|
|
1011
|
+
}
|
|
1012
|
+
return parsed;
|
|
1013
|
+
}
|
|
1014
|
+
function hashContent(content) {
|
|
1015
|
+
return createHash("sha256").update(content).digest("hex");
|
|
1016
|
+
}
|
|
787
1017
|
function classifyOwnership(path) {
|
|
788
1018
|
if (matchesRoot(path, projectOwnedRoots))
|
|
789
1019
|
return "project-owned";
|
|
@@ -804,6 +1034,10 @@ async function collectFiles(root) {
|
|
|
804
1034
|
if (metadata.isSymbolicLink())
|
|
805
1035
|
continue;
|
|
806
1036
|
if (metadata.isDirectory()) {
|
|
1037
|
+
const relativeDirectory = relative(root, path).split(sep).join("/");
|
|
1038
|
+
if (relativeDirectory === ".windy/backups" || relativeDirectory === ".windy/reports") {
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
807
1041
|
await visit(path, output);
|
|
808
1042
|
continue;
|
|
809
1043
|
}
|
|
@@ -816,6 +1050,17 @@ async function collectFiles(root) {
|
|
|
816
1050
|
function matchesRoot(path, roots) {
|
|
817
1051
|
return roots.some((root) => path === root || path.startsWith(`${root}/`));
|
|
818
1052
|
}
|
|
1053
|
+
function isManagedFilesManifest(value) {
|
|
1054
|
+
if (!value || typeof value !== "object")
|
|
1055
|
+
return false;
|
|
1056
|
+
const manifest = value;
|
|
1057
|
+
return manifest.schemaVersion === 1 && manifest.algorithm === "sha256" && typeof manifest.generatorVersion === "string" && Array.isArray(manifest.files) && manifest.files.every((entry) => {
|
|
1058
|
+
if (!entry || typeof entry !== "object")
|
|
1059
|
+
return false;
|
|
1060
|
+
const file = entry;
|
|
1061
|
+
return typeof file.path === "string" && (file.ownership === "windy-managed" || file.ownership === "shared-scaffold" || file.ownership === "project-owned") && typeof file.hash === "string";
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
819
1064
|
|
|
820
1065
|
// src/module-materializer.ts
|
|
821
1066
|
import { lstat as lstat2, readFile as readFile5, readdir as readdir2, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
@@ -1101,8 +1346,10 @@ ${projectName} 是由 create-windy 生成的单组织、单租户企业应用项
|
|
|
1101
1346
|
\`/\`,平台管理控制面位于 \`/admin/**\`;业务功能可以按自己的产品习惯开发,不需要
|
|
1102
1347
|
复用 Admin 页面结构。
|
|
1103
1348
|
|
|
1104
|
-
本项目固定使用 Bun 1.3.14
|
|
1105
|
-
|
|
1349
|
+
本项目固定使用 Bun 1.3.14 或更高版本,客户业务代码的当前法律许可为
|
|
1350
|
+
\`${projectLicense}\`。Windy Starter 基座使用 Apache-2.0,正文保留在
|
|
1351
|
+
\`.windy/licenses/WINDY-APACHE-2.0.txt\`。项目不包含软件供应方的 License Authority、
|
|
1352
|
+
内部签名服务、发布密钥或私有源仓库信息。
|
|
1106
1353
|
${includeExample ? "创建时包含了 `work-order` 示例模块,用于演示 Module Manifest 接入;它不是生产工单产品,可以在正式开发前替换。" : "创建时未选择 `work-order` 示例模块,当前项目只包含平台模块。"}
|
|
1107
1354
|
|
|
1108
1355
|
## 环境要求
|
|
@@ -1169,8 +1416,9 @@ bun run dev:web
|
|
|
1169
1416
|
| ---------- | -------- | ------------ | ---- |
|
|
1170
1417
|
${moduleRows}
|
|
1171
1418
|
|
|
1172
|
-
精确生成来源和选择记录在 \`.windy/generation.json
|
|
1173
|
-
\`module add/remove\`
|
|
1419
|
+
精确生成来源和选择记录在 \`.windy/generation.json\`。当前版本已提供可回滚的
|
|
1420
|
+
\`windy update\`;通用 \`module add/remove\` 尚未交付,不要把 Feature Flag 关闭当成
|
|
1421
|
+
模块已经卸载。
|
|
1174
1422
|
|
|
1175
1423
|
## 常用命令
|
|
1176
1424
|
|
|
@@ -1180,10 +1428,19 @@ bun run dev:server
|
|
|
1180
1428
|
bun run typecheck
|
|
1181
1429
|
bun run test
|
|
1182
1430
|
bun run build
|
|
1431
|
+
bun run windy doctor
|
|
1432
|
+
bun run windy update --check
|
|
1433
|
+
bun run windy update --dry-run
|
|
1183
1434
|
${includeOxc ? `bun run lint
|
|
1184
1435
|
bun run format` : "# 本项目未预装 lint/format 工具,可按团队需要自行选择。"}
|
|
1185
1436
|
\`\`\`
|
|
1186
1437
|
|
|
1438
|
+
${includeOxc ? "提交时 Husky 会通过 lint-staged 与 Oxfmt 格式化暂存文件,不会把未暂存内容带入提交。lint、typecheck 和 test 保留为需要时显式运行的命令。" : "当前项目没有生成 Husky 代码质量钩子;如后续自行引入工具,请同步配置提交工作流。"}
|
|
1439
|
+
|
|
1440
|
+
更新前必须保持 Git 工作区干净。\`--dry-run\` 只生成计划,不写项目;正式更新先备份,
|
|
1441
|
+
再做 package 字段级合并、共享脚手架三方合并与完整验证。任何冲突都会在写入前中止,
|
|
1442
|
+
验证失败会自动回滚;中断恢复使用 \`bun run windy doctor --repair\`。
|
|
1443
|
+
|
|
1187
1444
|
## 开发约束
|
|
1188
1445
|
|
|
1189
1446
|
- 新增路由默认需要登录;只有显式 public 的页面允许匿名访问。
|
|
@@ -1206,27 +1463,40 @@ create-windy 默认建立 \`main\` 分支并创建 \`chore: initialize project\`
|
|
|
1206
1463
|
|
|
1207
1464
|
## License
|
|
1208
1465
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1466
|
+
客户业务代码当前法律许可证为 \`${projectLicense}\`。${projectLicense === "UNLICENSED" ? "项目没有附带开源许可证。" : "完整许可证正文位于根目录 `LICENSE`。"}Windy Starter 基座继续适用
|
|
1467
|
+
\`.windy/licenses/WINDY-APACHE-2.0.txt\` 中的 Apache-2.0。项目法律许可证与
|
|
1468
|
+
\`system.license\` 离线运行时授权模块是两个不同概念;更换其中一个不会自动更换
|
|
1469
|
+
另一个。
|
|
1211
1470
|
`;
|
|
1212
1471
|
}
|
|
1213
1472
|
|
|
1214
1473
|
// src/project-package.ts
|
|
1215
|
-
|
|
1474
|
+
var defaultLintStaged = {
|
|
1475
|
+
"*.{cjs,css,cts,html,js,json,jsonc,jsx,less,md,mdx,mjs,mts,scss,ts,tsx,vue,yaml,yml}": "oxfmt --write"
|
|
1476
|
+
};
|
|
1477
|
+
function createStarterPackage(source, projectName, includeOxc = true, projectLicense = "UNLICENSED", selectedModules = [], generatorVersion) {
|
|
1478
|
+
const { "lint-staged": sourceLintStaged, ...sourceWithoutLintStaged } = source;
|
|
1216
1479
|
const devDependencies = { ...source.devDependencies };
|
|
1217
1480
|
if (!includeOxc) {
|
|
1481
|
+
delete devDependencies.husky;
|
|
1482
|
+
delete devDependencies["lint-staged"];
|
|
1218
1483
|
delete devDependencies.oxfmt;
|
|
1219
1484
|
delete devDependencies.oxlint;
|
|
1220
1485
|
}
|
|
1221
1486
|
return {
|
|
1222
|
-
...
|
|
1487
|
+
...sourceWithoutLintStaged,
|
|
1223
1488
|
name: projectName,
|
|
1224
1489
|
private: true,
|
|
1225
1490
|
license: projectLicense,
|
|
1226
1491
|
packageManager: "bun@1.3.14",
|
|
1227
1492
|
engines: { ...source.engines, bun: ">=1.3.14" },
|
|
1493
|
+
...includeOxc ? { "lint-staged": sourceLintStaged ?? defaultLintStaged } : {},
|
|
1228
1494
|
scripts: {
|
|
1495
|
+
...includeOxc ? {
|
|
1496
|
+
prepare: "git rev-parse --git-dir >/dev/null 2>&1 || exit 0; husky"
|
|
1497
|
+
} : {},
|
|
1229
1498
|
dev: "bun run --cwd apps/web dev",
|
|
1499
|
+
...generatorVersion ? { windy: "create-windy" } : {},
|
|
1230
1500
|
"dev:web": "bun run --cwd apps/web dev",
|
|
1231
1501
|
"dev:server": "bun run --cwd apps/server dev",
|
|
1232
1502
|
build: "bun run --cwd apps/web build",
|
|
@@ -1241,10 +1511,17 @@ function createStarterPackage(source, projectName, includeOxc = true, projectLic
|
|
|
1241
1511
|
"bun run --cwd packages/database typecheck",
|
|
1242
1512
|
"bun run --cwd packages/crud-generator typecheck"
|
|
1243
1513
|
].join(" && "),
|
|
1244
|
-
...includeOxc ? {
|
|
1514
|
+
...includeOxc ? {
|
|
1515
|
+
lint: "oxlint .",
|
|
1516
|
+
format: "oxfmt",
|
|
1517
|
+
"format:staged": "lint-staged"
|
|
1518
|
+
} : {},
|
|
1245
1519
|
test: "bun test && bun run --cwd apps/web test"
|
|
1246
1520
|
},
|
|
1247
|
-
devDependencies
|
|
1521
|
+
devDependencies: {
|
|
1522
|
+
...devDependencies,
|
|
1523
|
+
...generatorVersion ? { "create-windy": generatorVersion } : {}
|
|
1524
|
+
}
|
|
1248
1525
|
};
|
|
1249
1526
|
}
|
|
1250
1527
|
|
|
@@ -1252,6 +1529,7 @@ function createStarterPackage(source, projectName, includeOxc = true, projectLic
|
|
|
1252
1529
|
var templateRootFiles = [
|
|
1253
1530
|
".dockerignore",
|
|
1254
1531
|
".gitignore",
|
|
1532
|
+
".husky/pre-commit",
|
|
1255
1533
|
".oxfmtrc.json",
|
|
1256
1534
|
".oxlintrc.json",
|
|
1257
1535
|
"AGENTS.md",
|
|
@@ -1312,8 +1590,13 @@ var npmTemplateAliases = {
|
|
|
1312
1590
|
"bunfig.toml": "_bunfig.toml"
|
|
1313
1591
|
};
|
|
1314
1592
|
function generationTemplatePaths(includeExample, includeOxc = true) {
|
|
1315
|
-
return templatePaths.filter((path) => (includeExample || path !== "packages/example-work-order") && (includeOxc ||
|
|
1593
|
+
return templatePaths.filter((path) => (includeExample || path !== "packages/example-work-order") && (includeOxc || !oxcTemplatePaths.has(path)));
|
|
1316
1594
|
}
|
|
1595
|
+
var oxcTemplatePaths = new Set([
|
|
1596
|
+
".husky/pre-commit",
|
|
1597
|
+
".oxfmtrc.json",
|
|
1598
|
+
".oxlintrc.json"
|
|
1599
|
+
]);
|
|
1317
1600
|
var excludedNames = new Set([
|
|
1318
1601
|
".DS_Store",
|
|
1319
1602
|
".env",
|
|
@@ -1341,6 +1624,14 @@ async function readTemplateMetadata(sourceRoot) {
|
|
|
1341
1624
|
}
|
|
1342
1625
|
return parsed;
|
|
1343
1626
|
}
|
|
1627
|
+
async function readGenerationProvenance(targetDirectory) {
|
|
1628
|
+
const path = join6(targetDirectory, generationMetadataFile);
|
|
1629
|
+
const parsed = JSON.parse(await readFile6(path, "utf8"));
|
|
1630
|
+
if (!isGenerationProvenance(parsed)) {
|
|
1631
|
+
throw new Error("当前目录不是受支持的 Windy 项目:Project Lock 无效");
|
|
1632
|
+
}
|
|
1633
|
+
return parsed;
|
|
1634
|
+
}
|
|
1344
1635
|
async function writeGenerationProvenance(targetDirectory, metadata, profile, selectedModules, includeOxc, legalLicense) {
|
|
1345
1636
|
const requested = selectedModuleManifests(selectedModules);
|
|
1346
1637
|
const versions = new Map(metadata.modules.map((item) => [item.name, item]));
|
|
@@ -1378,6 +1669,14 @@ function isTemplateMetadata(value) {
|
|
|
1378
1669
|
const metadata = value;
|
|
1379
1670
|
return metadata.schemaVersion === 1 && typeof metadata.templateCommit === "string" && /^[0-9a-f]{40}$/.test(metadata.templateCommit) && typeof metadata.generatorVersion === "string" && metadata.generatorVersion.length > 0 && Array.isArray(metadata.modules) && metadata.modules.every(isModuleVersion);
|
|
1380
1671
|
}
|
|
1672
|
+
function isGenerationProvenance(value) {
|
|
1673
|
+
if (!isTemplateMetadata(value))
|
|
1674
|
+
return false;
|
|
1675
|
+
const metadata = value;
|
|
1676
|
+
const toolchain = metadata.toolchain;
|
|
1677
|
+
const choices = metadata.choices;
|
|
1678
|
+
return typeof metadata.windyVersion === "string" && metadata.profile === "private-enterprise" && toolchain?.runtime === "bun" && (toolchain?.codeQuality === "oxc" || toolchain?.codeQuality === "none") && (metadata.legalLicense === "UNLICENSED" || metadata.legalLicense === "MIT" || metadata.legalLicense === "Apache-2.0") && Array.isArray(metadata.appliedRecipes) && metadata.appliedRecipes.every((item) => typeof item === "string") && metadata.managedFilesManifest === ".windy/files.json" && typeof choices?.includeExample === "boolean" && typeof choices.includeOxc === "boolean";
|
|
1679
|
+
}
|
|
1381
1680
|
function isModuleVersion(value) {
|
|
1382
1681
|
if (!value || typeof value !== "object")
|
|
1383
1682
|
return false;
|
|
@@ -1401,13 +1700,14 @@ async function generateProject(input) {
|
|
|
1401
1700
|
await materializeSelectedModules(input.targetDirectory, selectedModules);
|
|
1402
1701
|
const packagePath = join7(input.targetDirectory, "package.json");
|
|
1403
1702
|
const packageJson = JSON.parse(await readFile7(packagePath, "utf8"));
|
|
1404
|
-
await writeFile7(packagePath, `${JSON.stringify(createStarterPackage(packageJson, input.projectName, includeOxc, projectLicense, selectedModules), null, 2)}
|
|
1703
|
+
await writeFile7(packagePath, `${JSON.stringify(createStarterPackage(packageJson, input.projectName, includeOxc, projectLicense, selectedModules, templateMetadata.generatorVersion), null, 2)}
|
|
1405
1704
|
`);
|
|
1406
1705
|
await writeFile7(join7(input.targetDirectory, "docker-compose.yml"), starterCompose);
|
|
1407
1706
|
await writeFile7(join7(input.targetDirectory, ".env.example"), environmentExample(includeExample));
|
|
1408
1707
|
await writeFile7(join7(input.targetDirectory, "README.md"), starterReadme(input.projectName, includeExample, includeOxc, projectLicense, selectedModules));
|
|
1409
1708
|
await writeFile7(join7(input.targetDirectory, "docs/README.md"), starterDocsReadme(selectedModules.includes("system.license")));
|
|
1410
1709
|
await reflectCodeQualityChoice(input.targetDirectory, includeOxc);
|
|
1710
|
+
await applyWindyBaseLicense(input.targetDirectory);
|
|
1411
1711
|
await applyProjectLicense(input.targetDirectory, projectLicense, input.licenseHolder);
|
|
1412
1712
|
await prepareCustomerDockerfiles(input.targetDirectory);
|
|
1413
1713
|
await writeGenerationProvenance(input.targetDirectory, templateMetadata, input.profile ?? starterProfile.key, selectedModules, includeOxc, projectLicense);
|
|
@@ -1536,7 +1836,7 @@ async function createProject(options, dependencies = {}) {
|
|
|
1536
1836
|
const log = dependencies.log ?? console.log;
|
|
1537
1837
|
let gitInitialized = false;
|
|
1538
1838
|
if (options.initializeGit) {
|
|
1539
|
-
const result = await (dependencies.initializeGit ?? initializeGitRepository)(options.targetDirectory, dependencies.commandExecutor);
|
|
1839
|
+
const result = await (dependencies.initializeGit ?? initializeGitRepository)(options.targetDirectory, dependencies.commandExecutor, options.includeOxc && (options.install || options.verify));
|
|
1540
1840
|
gitInitialized = result.status === "committed";
|
|
1541
1841
|
if (result.status === "skipped")
|
|
1542
1842
|
log(`
|
|
@@ -1579,95 +1879,2489 @@ function isMissing3(error) {
|
|
|
1579
1879
|
}
|
|
1580
1880
|
|
|
1581
1881
|
// src/interactive-selection.ts
|
|
1582
|
-
import { createInterface } from "node:readline/promises";
|
|
1882
|
+
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
1583
1883
|
import { stdin, stdout } from "node:process";
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1884
|
+
|
|
1885
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/key.js
|
|
1886
|
+
var keybindings = ["emacs", "vim"];
|
|
1887
|
+
var keybindingLookup = new Set(keybindings);
|
|
1888
|
+
function isKeybinding(value) {
|
|
1889
|
+
return keybindingLookup.has(value);
|
|
1890
|
+
}
|
|
1891
|
+
function getDefaultKeybindings() {
|
|
1892
|
+
const env = process.env["INQUIRER_KEYBINDINGS"];
|
|
1893
|
+
if (!env)
|
|
1894
|
+
return [];
|
|
1895
|
+
return Array.from(new Set(env.toLowerCase().split(/[\s,]+/).filter(isKeybinding)));
|
|
1896
|
+
}
|
|
1897
|
+
var isUpKey = (key, keybindings2 = []) => key.name === "up" || keybindings2.includes("vim") && key.name === "k" || keybindings2.includes("emacs") && key.ctrl && key.name === "p";
|
|
1898
|
+
var isDownKey = (key, keybindings2 = []) => key.name === "down" || keybindings2.includes("vim") && key.name === "j" || keybindings2.includes("emacs") && key.ctrl && key.name === "n";
|
|
1899
|
+
var isSpaceKey = (key) => key.name === "space";
|
|
1900
|
+
var isNumberKey = (key) => "1234567890".includes(key.name);
|
|
1901
|
+
var isEnterKey = (key) => key.name === "enter" || key.name === "return";
|
|
1902
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/errors.js
|
|
1903
|
+
class AbortPromptError extends Error {
|
|
1904
|
+
name = "AbortPromptError";
|
|
1905
|
+
message = "Prompt was aborted";
|
|
1906
|
+
constructor(options) {
|
|
1907
|
+
super();
|
|
1908
|
+
this.cause = options?.cause;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
class CancelPromptError extends Error {
|
|
1913
|
+
name = "CancelPromptError";
|
|
1914
|
+
message = "Prompt was canceled";
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
class ExitPromptError extends Error {
|
|
1918
|
+
name = "ExitPromptError";
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
class HookError extends Error {
|
|
1922
|
+
name = "HookError";
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
class ValidationError extends Error {
|
|
1926
|
+
name = "ValidationError";
|
|
1927
|
+
}
|
|
1928
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/use-state.js
|
|
1929
|
+
import { AsyncResource as AsyncResource2 } from "node:async_hooks";
|
|
1930
|
+
|
|
1931
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/hook-engine.js
|
|
1932
|
+
import { AsyncLocalStorage, AsyncResource } from "node:async_hooks";
|
|
1933
|
+
var hookStorage = new AsyncLocalStorage;
|
|
1934
|
+
function createStore(rl) {
|
|
1935
|
+
const store = {
|
|
1936
|
+
rl,
|
|
1937
|
+
hooks: [],
|
|
1938
|
+
hooksCleanup: [],
|
|
1939
|
+
hooksEffect: [],
|
|
1940
|
+
index: 0,
|
|
1941
|
+
handleChange() {}
|
|
1942
|
+
};
|
|
1943
|
+
return store;
|
|
1944
|
+
}
|
|
1945
|
+
function withHooks(rl, cb) {
|
|
1946
|
+
const store = createStore(rl);
|
|
1947
|
+
return hookStorage.run(store, () => {
|
|
1948
|
+
function cycle(render) {
|
|
1949
|
+
store.handleChange = () => {
|
|
1950
|
+
store.index = 0;
|
|
1951
|
+
render();
|
|
1952
|
+
};
|
|
1953
|
+
store.handleChange();
|
|
1954
|
+
}
|
|
1955
|
+
return cb(cycle);
|
|
1595
1956
|
});
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
const
|
|
1599
|
-
if (
|
|
1600
|
-
throw new
|
|
1957
|
+
}
|
|
1958
|
+
function getStore() {
|
|
1959
|
+
const store = hookStorage.getStore();
|
|
1960
|
+
if (!store) {
|
|
1961
|
+
throw new HookError("[Inquirer] Hook functions can only be called from within a prompt");
|
|
1601
1962
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1963
|
+
return store;
|
|
1964
|
+
}
|
|
1965
|
+
function readline() {
|
|
1966
|
+
return getStore().rl;
|
|
1967
|
+
}
|
|
1968
|
+
function withUpdates(fn) {
|
|
1969
|
+
const wrapped = (...args) => {
|
|
1970
|
+
const store = getStore();
|
|
1971
|
+
let shouldUpdate = false;
|
|
1972
|
+
const oldHandleChange = store.handleChange;
|
|
1973
|
+
store.handleChange = () => {
|
|
1974
|
+
shouldUpdate = true;
|
|
1975
|
+
};
|
|
1976
|
+
const returnValue = fn(...args);
|
|
1977
|
+
if (shouldUpdate) {
|
|
1978
|
+
oldHandleChange();
|
|
1979
|
+
}
|
|
1980
|
+
store.handleChange = oldHandleChange;
|
|
1981
|
+
return returnValue;
|
|
1982
|
+
};
|
|
1983
|
+
return AsyncResource.bind(wrapped);
|
|
1984
|
+
}
|
|
1985
|
+
function withPointer(cb) {
|
|
1986
|
+
const store = getStore();
|
|
1987
|
+
const { index } = store;
|
|
1988
|
+
const pointer = {
|
|
1989
|
+
get() {
|
|
1990
|
+
return store.hooks[index];
|
|
1991
|
+
},
|
|
1992
|
+
set(value) {
|
|
1993
|
+
store.hooks[index] = value;
|
|
1994
|
+
},
|
|
1995
|
+
initialized: index in store.hooks
|
|
1996
|
+
};
|
|
1997
|
+
const returnValue = cb(pointer);
|
|
1998
|
+
store.index++;
|
|
1999
|
+
return returnValue;
|
|
2000
|
+
}
|
|
2001
|
+
function handleChange() {
|
|
2002
|
+
getStore().handleChange();
|
|
2003
|
+
}
|
|
2004
|
+
var effectScheduler = {
|
|
2005
|
+
queue(cb) {
|
|
2006
|
+
const store = getStore();
|
|
2007
|
+
const { index } = store;
|
|
2008
|
+
store.hooksEffect.push(() => {
|
|
2009
|
+
store.hooksCleanup[index]?.();
|
|
2010
|
+
const cleanFn = cb(readline());
|
|
2011
|
+
if (cleanFn != null && typeof cleanFn !== "function") {
|
|
2012
|
+
throw new ValidationError("useEffect return value must be a cleanup function or nothing.");
|
|
2013
|
+
}
|
|
2014
|
+
store.hooksCleanup[index] = cleanFn;
|
|
2015
|
+
});
|
|
2016
|
+
},
|
|
2017
|
+
run() {
|
|
2018
|
+
const store = getStore();
|
|
2019
|
+
withUpdates(() => {
|
|
2020
|
+
store.hooksEffect.forEach((effect) => {
|
|
2021
|
+
effect();
|
|
2022
|
+
});
|
|
2023
|
+
store.hooksEffect.length = 0;
|
|
2024
|
+
})();
|
|
2025
|
+
},
|
|
2026
|
+
clearAll() {
|
|
2027
|
+
const store = getStore();
|
|
2028
|
+
store.hooksCleanup.forEach((cleanFn) => {
|
|
2029
|
+
cleanFn?.();
|
|
2030
|
+
});
|
|
2031
|
+
store.hooksEffect.length = 0;
|
|
2032
|
+
store.hooksCleanup.length = 0;
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
|
|
2036
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/use-state.js
|
|
2037
|
+
function isFactory(value) {
|
|
2038
|
+
return typeof value === "function";
|
|
2039
|
+
}
|
|
2040
|
+
function useState(defaultValue) {
|
|
2041
|
+
return withPointer((pointer) => {
|
|
2042
|
+
const setState = AsyncResource2.bind(function setState2(newValue) {
|
|
2043
|
+
if (pointer.get() !== newValue) {
|
|
2044
|
+
pointer.set(newValue);
|
|
2045
|
+
handleChange();
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
if (pointer.initialized) {
|
|
2049
|
+
return [pointer.get(), setState];
|
|
2050
|
+
}
|
|
2051
|
+
const value = isFactory(defaultValue) ? defaultValue() : defaultValue;
|
|
2052
|
+
pointer.set(value);
|
|
2053
|
+
return [value, setState];
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/use-effect.js
|
|
2058
|
+
function useEffect(cb, depArray) {
|
|
2059
|
+
withPointer((pointer) => {
|
|
2060
|
+
const oldDeps = pointer.get();
|
|
2061
|
+
const hasChanged = !Array.isArray(oldDeps) || depArray.some((dep, i) => !Object.is(dep, oldDeps[i]));
|
|
2062
|
+
if (hasChanged) {
|
|
2063
|
+
effectScheduler.queue(cb);
|
|
2064
|
+
}
|
|
2065
|
+
pointer.set(depArray);
|
|
1605
2066
|
});
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/theme.js
|
|
2070
|
+
import { styleText } from "node:util";
|
|
2071
|
+
|
|
2072
|
+
// ../../node_modules/.bun/@inquirer+figures@2.0.7/node_modules/@inquirer/figures/dist/index.js
|
|
2073
|
+
import process2 from "node:process";
|
|
2074
|
+
function isUnicodeSupported() {
|
|
2075
|
+
if (!process2.platform.startsWith("win")) {
|
|
2076
|
+
return process2.env["TERM"] !== "linux";
|
|
2077
|
+
}
|
|
2078
|
+
return Boolean(process2.env["CI"]) || Boolean(process2.env["WT_SESSION"]) || Boolean(process2.env["TERMINUS_SUBLIME"]) || process2.env["ConEmuTask"] === "{cmd::Cmder}" || process2.env["TERM_PROGRAM"] === "Terminus-Sublime" || process2.env["TERM_PROGRAM"] === "vscode" || process2.env["TERM"] === "xterm-256color" || process2.env["TERM"] === "alacritty" || process2.env["TERMINAL_EMULATOR"] === "JetBrains-JediTerm";
|
|
2079
|
+
}
|
|
2080
|
+
var common = {
|
|
2081
|
+
circleQuestionMark: "(?)",
|
|
2082
|
+
questionMarkPrefix: "(?)",
|
|
2083
|
+
square: "█",
|
|
2084
|
+
squareDarkShade: "▓",
|
|
2085
|
+
squareMediumShade: "▒",
|
|
2086
|
+
squareLightShade: "░",
|
|
2087
|
+
squareTop: "▀",
|
|
2088
|
+
squareBottom: "▄",
|
|
2089
|
+
squareLeft: "▌",
|
|
2090
|
+
squareRight: "▐",
|
|
2091
|
+
squareCenter: "■",
|
|
2092
|
+
bullet: "●",
|
|
2093
|
+
dot: "․",
|
|
2094
|
+
ellipsis: "…",
|
|
2095
|
+
pointerSmall: "›",
|
|
2096
|
+
triangleUp: "▲",
|
|
2097
|
+
triangleUpSmall: "▴",
|
|
2098
|
+
triangleDown: "▼",
|
|
2099
|
+
triangleDownSmall: "▾",
|
|
2100
|
+
triangleLeftSmall: "◂",
|
|
2101
|
+
triangleRightSmall: "▸",
|
|
2102
|
+
home: "⌂",
|
|
2103
|
+
heart: "♥",
|
|
2104
|
+
musicNote: "♪",
|
|
2105
|
+
musicNoteBeamed: "♫",
|
|
2106
|
+
arrowUp: "↑",
|
|
2107
|
+
arrowDown: "↓",
|
|
2108
|
+
arrowLeft: "←",
|
|
2109
|
+
arrowRight: "→",
|
|
2110
|
+
arrowLeftRight: "↔",
|
|
2111
|
+
arrowUpDown: "↕",
|
|
2112
|
+
almostEqual: "≈",
|
|
2113
|
+
notEqual: "≠",
|
|
2114
|
+
lessOrEqual: "≤",
|
|
2115
|
+
greaterOrEqual: "≥",
|
|
2116
|
+
identical: "≡",
|
|
2117
|
+
infinity: "∞",
|
|
2118
|
+
subscriptZero: "₀",
|
|
2119
|
+
subscriptOne: "₁",
|
|
2120
|
+
subscriptTwo: "₂",
|
|
2121
|
+
subscriptThree: "₃",
|
|
2122
|
+
subscriptFour: "₄",
|
|
2123
|
+
subscriptFive: "₅",
|
|
2124
|
+
subscriptSix: "₆",
|
|
2125
|
+
subscriptSeven: "₇",
|
|
2126
|
+
subscriptEight: "₈",
|
|
2127
|
+
subscriptNine: "₉",
|
|
2128
|
+
oneHalf: "½",
|
|
2129
|
+
oneThird: "⅓",
|
|
2130
|
+
oneQuarter: "¼",
|
|
2131
|
+
oneFifth: "⅕",
|
|
2132
|
+
oneSixth: "⅙",
|
|
2133
|
+
oneEighth: "⅛",
|
|
2134
|
+
twoThirds: "⅔",
|
|
2135
|
+
twoFifths: "⅖",
|
|
2136
|
+
threeQuarters: "¾",
|
|
2137
|
+
threeFifths: "⅗",
|
|
2138
|
+
threeEighths: "⅜",
|
|
2139
|
+
fourFifths: "⅘",
|
|
2140
|
+
fiveSixths: "⅚",
|
|
2141
|
+
fiveEighths: "⅝",
|
|
2142
|
+
sevenEighths: "⅞",
|
|
2143
|
+
line: "─",
|
|
2144
|
+
lineBold: "━",
|
|
2145
|
+
lineDouble: "═",
|
|
2146
|
+
lineDashed0: "┄",
|
|
2147
|
+
lineDashed1: "┅",
|
|
2148
|
+
lineDashed2: "┈",
|
|
2149
|
+
lineDashed3: "┉",
|
|
2150
|
+
lineDashed4: "╌",
|
|
2151
|
+
lineDashed5: "╍",
|
|
2152
|
+
lineDashed6: "╴",
|
|
2153
|
+
lineDashed7: "╶",
|
|
2154
|
+
lineDashed8: "╸",
|
|
2155
|
+
lineDashed9: "╺",
|
|
2156
|
+
lineDashed10: "╼",
|
|
2157
|
+
lineDashed11: "╾",
|
|
2158
|
+
lineDashed12: "−",
|
|
2159
|
+
lineDashed13: "–",
|
|
2160
|
+
lineDashed14: "‐",
|
|
2161
|
+
lineDashed15: "⁃",
|
|
2162
|
+
lineVertical: "│",
|
|
2163
|
+
lineVerticalBold: "┃",
|
|
2164
|
+
lineVerticalDouble: "║",
|
|
2165
|
+
lineVerticalDashed0: "┆",
|
|
2166
|
+
lineVerticalDashed1: "┇",
|
|
2167
|
+
lineVerticalDashed2: "┊",
|
|
2168
|
+
lineVerticalDashed3: "┋",
|
|
2169
|
+
lineVerticalDashed4: "╎",
|
|
2170
|
+
lineVerticalDashed5: "╏",
|
|
2171
|
+
lineVerticalDashed6: "╵",
|
|
2172
|
+
lineVerticalDashed7: "╷",
|
|
2173
|
+
lineVerticalDashed8: "╹",
|
|
2174
|
+
lineVerticalDashed9: "╻",
|
|
2175
|
+
lineVerticalDashed10: "╽",
|
|
2176
|
+
lineVerticalDashed11: "╿",
|
|
2177
|
+
lineDownLeft: "┐",
|
|
2178
|
+
lineDownLeftArc: "╮",
|
|
2179
|
+
lineDownBoldLeftBold: "┓",
|
|
2180
|
+
lineDownBoldLeft: "┒",
|
|
2181
|
+
lineDownLeftBold: "┑",
|
|
2182
|
+
lineDownDoubleLeftDouble: "╗",
|
|
2183
|
+
lineDownDoubleLeft: "╖",
|
|
2184
|
+
lineDownLeftDouble: "╕",
|
|
2185
|
+
lineDownRight: "┌",
|
|
2186
|
+
lineDownRightArc: "╭",
|
|
2187
|
+
lineDownBoldRightBold: "┏",
|
|
2188
|
+
lineDownBoldRight: "┎",
|
|
2189
|
+
lineDownRightBold: "┍",
|
|
2190
|
+
lineDownDoubleRightDouble: "╔",
|
|
2191
|
+
lineDownDoubleRight: "╓",
|
|
2192
|
+
lineDownRightDouble: "╒",
|
|
2193
|
+
lineUpLeft: "┘",
|
|
2194
|
+
lineUpLeftArc: "╯",
|
|
2195
|
+
lineUpBoldLeftBold: "┛",
|
|
2196
|
+
lineUpBoldLeft: "┚",
|
|
2197
|
+
lineUpLeftBold: "┙",
|
|
2198
|
+
lineUpDoubleLeftDouble: "╝",
|
|
2199
|
+
lineUpDoubleLeft: "╜",
|
|
2200
|
+
lineUpLeftDouble: "╛",
|
|
2201
|
+
lineUpRight: "└",
|
|
2202
|
+
lineUpRightArc: "╰",
|
|
2203
|
+
lineUpBoldRightBold: "┗",
|
|
2204
|
+
lineUpBoldRight: "┖",
|
|
2205
|
+
lineUpRightBold: "┕",
|
|
2206
|
+
lineUpDoubleRightDouble: "╚",
|
|
2207
|
+
lineUpDoubleRight: "╙",
|
|
2208
|
+
lineUpRightDouble: "╘",
|
|
2209
|
+
lineUpDownLeft: "┤",
|
|
2210
|
+
lineUpBoldDownBoldLeftBold: "┫",
|
|
2211
|
+
lineUpBoldDownBoldLeft: "┨",
|
|
2212
|
+
lineUpDownLeftBold: "┥",
|
|
2213
|
+
lineUpBoldDownLeftBold: "┩",
|
|
2214
|
+
lineUpDownBoldLeftBold: "┪",
|
|
2215
|
+
lineUpDownBoldLeft: "┧",
|
|
2216
|
+
lineUpBoldDownLeft: "┦",
|
|
2217
|
+
lineUpDoubleDownDoubleLeftDouble: "╣",
|
|
2218
|
+
lineUpDoubleDownDoubleLeft: "╢",
|
|
2219
|
+
lineUpDownLeftDouble: "╡",
|
|
2220
|
+
lineUpDownRight: "├",
|
|
2221
|
+
lineUpBoldDownBoldRightBold: "┣",
|
|
2222
|
+
lineUpBoldDownBoldRight: "┠",
|
|
2223
|
+
lineUpDownRightBold: "┝",
|
|
2224
|
+
lineUpBoldDownRightBold: "┡",
|
|
2225
|
+
lineUpDownBoldRightBold: "┢",
|
|
2226
|
+
lineUpDownBoldRight: "┟",
|
|
2227
|
+
lineUpBoldDownRight: "┞",
|
|
2228
|
+
lineUpDoubleDownDoubleRightDouble: "╠",
|
|
2229
|
+
lineUpDoubleDownDoubleRight: "╟",
|
|
2230
|
+
lineUpDownRightDouble: "╞",
|
|
2231
|
+
lineDownLeftRight: "┬",
|
|
2232
|
+
lineDownBoldLeftBoldRightBold: "┳",
|
|
2233
|
+
lineDownLeftBoldRightBold: "┯",
|
|
2234
|
+
lineDownBoldLeftRight: "┰",
|
|
2235
|
+
lineDownBoldLeftBoldRight: "┱",
|
|
2236
|
+
lineDownBoldLeftRightBold: "┲",
|
|
2237
|
+
lineDownLeftRightBold: "┮",
|
|
2238
|
+
lineDownLeftBoldRight: "┭",
|
|
2239
|
+
lineDownDoubleLeftDoubleRightDouble: "╦",
|
|
2240
|
+
lineDownDoubleLeftRight: "╥",
|
|
2241
|
+
lineDownLeftDoubleRightDouble: "╤",
|
|
2242
|
+
lineUpLeftRight: "┴",
|
|
2243
|
+
lineUpBoldLeftBoldRightBold: "┻",
|
|
2244
|
+
lineUpLeftBoldRightBold: "┷",
|
|
2245
|
+
lineUpBoldLeftRight: "┸",
|
|
2246
|
+
lineUpBoldLeftBoldRight: "┹",
|
|
2247
|
+
lineUpBoldLeftRightBold: "┺",
|
|
2248
|
+
lineUpLeftRightBold: "┶",
|
|
2249
|
+
lineUpLeftBoldRight: "┵",
|
|
2250
|
+
lineUpDoubleLeftDoubleRightDouble: "╩",
|
|
2251
|
+
lineUpDoubleLeftRight: "╨",
|
|
2252
|
+
lineUpLeftDoubleRightDouble: "╧",
|
|
2253
|
+
lineUpDownLeftRight: "┼",
|
|
2254
|
+
lineUpBoldDownBoldLeftBoldRightBold: "╋",
|
|
2255
|
+
lineUpDownBoldLeftBoldRightBold: "╈",
|
|
2256
|
+
lineUpBoldDownLeftBoldRightBold: "╇",
|
|
2257
|
+
lineUpBoldDownBoldLeftRightBold: "╊",
|
|
2258
|
+
lineUpBoldDownBoldLeftBoldRight: "╉",
|
|
2259
|
+
lineUpBoldDownLeftRight: "╀",
|
|
2260
|
+
lineUpDownBoldLeftRight: "╁",
|
|
2261
|
+
lineUpDownLeftBoldRight: "┽",
|
|
2262
|
+
lineUpDownLeftRightBold: "┾",
|
|
2263
|
+
lineUpBoldDownBoldLeftRight: "╂",
|
|
2264
|
+
lineUpDownLeftBoldRightBold: "┿",
|
|
2265
|
+
lineUpBoldDownLeftBoldRight: "╃",
|
|
2266
|
+
lineUpBoldDownLeftRightBold: "╄",
|
|
2267
|
+
lineUpDownBoldLeftBoldRight: "╅",
|
|
2268
|
+
lineUpDownBoldLeftRightBold: "╆",
|
|
2269
|
+
lineUpDoubleDownDoubleLeftDoubleRightDouble: "╬",
|
|
2270
|
+
lineUpDoubleDownDoubleLeftRight: "╫",
|
|
2271
|
+
lineUpDownLeftDoubleRightDouble: "╪",
|
|
2272
|
+
lineCross: "╳",
|
|
2273
|
+
lineBackslash: "╲",
|
|
2274
|
+
lineSlash: "╱"
|
|
2275
|
+
};
|
|
2276
|
+
var specialMainSymbols = {
|
|
2277
|
+
tick: "✔",
|
|
2278
|
+
info: "ℹ",
|
|
2279
|
+
warning: "⚠",
|
|
2280
|
+
cross: "✘",
|
|
2281
|
+
squareSmall: "◻",
|
|
2282
|
+
squareSmallFilled: "◼",
|
|
2283
|
+
circle: "◯",
|
|
2284
|
+
circleFilled: "◉",
|
|
2285
|
+
circleDotted: "◌",
|
|
2286
|
+
circleDouble: "◎",
|
|
2287
|
+
circleCircle: "ⓞ",
|
|
2288
|
+
circleCross: "ⓧ",
|
|
2289
|
+
circlePipe: "Ⓘ",
|
|
2290
|
+
radioOn: "◉",
|
|
2291
|
+
radioOff: "◯",
|
|
2292
|
+
checkboxOn: "☒",
|
|
2293
|
+
checkboxOff: "☐",
|
|
2294
|
+
checkboxCircleOn: "ⓧ",
|
|
2295
|
+
checkboxCircleOff: "Ⓘ",
|
|
2296
|
+
pointer: "❯",
|
|
2297
|
+
triangleUpOutline: "△",
|
|
2298
|
+
triangleLeft: "◀",
|
|
2299
|
+
triangleRight: "▶",
|
|
2300
|
+
lozenge: "◆",
|
|
2301
|
+
lozengeOutline: "◇",
|
|
2302
|
+
hamburger: "☰",
|
|
2303
|
+
smiley: "㋡",
|
|
2304
|
+
mustache: "෴",
|
|
2305
|
+
star: "★",
|
|
2306
|
+
play: "▶",
|
|
2307
|
+
nodejs: "⬢",
|
|
2308
|
+
oneSeventh: "⅐",
|
|
2309
|
+
oneNinth: "⅑",
|
|
2310
|
+
oneTenth: "⅒"
|
|
2311
|
+
};
|
|
2312
|
+
var specialFallbackSymbols = {
|
|
2313
|
+
tick: "√",
|
|
2314
|
+
info: "i",
|
|
2315
|
+
warning: "‼",
|
|
2316
|
+
cross: "×",
|
|
2317
|
+
squareSmall: "□",
|
|
2318
|
+
squareSmallFilled: "■",
|
|
2319
|
+
circle: "( )",
|
|
2320
|
+
circleFilled: "(*)",
|
|
2321
|
+
circleDotted: "( )",
|
|
2322
|
+
circleDouble: "( )",
|
|
2323
|
+
circleCircle: "(○)",
|
|
2324
|
+
circleCross: "(×)",
|
|
2325
|
+
circlePipe: "(│)",
|
|
2326
|
+
radioOn: "(*)",
|
|
2327
|
+
radioOff: "( )",
|
|
2328
|
+
checkboxOn: "[×]",
|
|
2329
|
+
checkboxOff: "[ ]",
|
|
2330
|
+
checkboxCircleOn: "(×)",
|
|
2331
|
+
checkboxCircleOff: "( )",
|
|
2332
|
+
pointer: ">",
|
|
2333
|
+
triangleUpOutline: "∆",
|
|
2334
|
+
triangleLeft: "◄",
|
|
2335
|
+
triangleRight: "►",
|
|
2336
|
+
lozenge: "♦",
|
|
2337
|
+
lozengeOutline: "◊",
|
|
2338
|
+
hamburger: "≡",
|
|
2339
|
+
smiley: "☺",
|
|
2340
|
+
mustache: "┌─┐",
|
|
2341
|
+
star: "✶",
|
|
2342
|
+
play: "►",
|
|
2343
|
+
nodejs: "♦",
|
|
2344
|
+
oneSeventh: "1/7",
|
|
2345
|
+
oneNinth: "1/9",
|
|
2346
|
+
oneTenth: "1/10"
|
|
2347
|
+
};
|
|
2348
|
+
var mainSymbols = {
|
|
2349
|
+
...common,
|
|
2350
|
+
...specialMainSymbols
|
|
2351
|
+
};
|
|
2352
|
+
var fallbackSymbols = {
|
|
2353
|
+
...common,
|
|
2354
|
+
...specialFallbackSymbols
|
|
2355
|
+
};
|
|
2356
|
+
var shouldUseMain = isUnicodeSupported();
|
|
2357
|
+
var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
|
|
2358
|
+
var dist_default = figures;
|
|
2359
|
+
var replacements = Object.entries(specialMainSymbols);
|
|
2360
|
+
|
|
2361
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/theme.js
|
|
2362
|
+
var defaultTheme = {
|
|
2363
|
+
prefix: {
|
|
2364
|
+
idle: styleText("blue", "?"),
|
|
2365
|
+
done: styleText("green", dist_default.tick)
|
|
2366
|
+
},
|
|
2367
|
+
spinner: {
|
|
2368
|
+
interval: 80,
|
|
2369
|
+
frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"].map((frame) => styleText("yellow", frame))
|
|
2370
|
+
},
|
|
2371
|
+
keybindings: [],
|
|
2372
|
+
style: {
|
|
2373
|
+
answer: (text) => styleText("cyan", text),
|
|
2374
|
+
message: (text) => styleText("bold", text),
|
|
2375
|
+
error: (text) => styleText("red", `> ${text}`),
|
|
2376
|
+
defaultAnswer: (text) => styleText("dim", `(${text})`),
|
|
2377
|
+
help: (text) => styleText("dim", text),
|
|
2378
|
+
highlight: (text) => styleText("cyan", text),
|
|
2379
|
+
key: (text) => styleText("cyan", styleText("bold", `<${text}>`))
|
|
2380
|
+
}
|
|
2381
|
+
};
|
|
2382
|
+
function getDefaultTheme() {
|
|
1606
2383
|
return {
|
|
1607
|
-
...
|
|
1608
|
-
|
|
1609
|
-
includeExample: selectedModules.includes("work-order"),
|
|
1610
|
-
includeOxc,
|
|
1611
|
-
projectLicense,
|
|
1612
|
-
licenseHolder,
|
|
1613
|
-
interactive: false
|
|
2384
|
+
...defaultTheme,
|
|
2385
|
+
keybindings: getDefaultKeybindings()
|
|
1614
2386
|
};
|
|
1615
2387
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
2388
|
+
|
|
2389
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/make-theme.js
|
|
2390
|
+
function isPlainObject(value) {
|
|
2391
|
+
if (typeof value !== "object" || value === null)
|
|
2392
|
+
return false;
|
|
2393
|
+
let proto = value;
|
|
2394
|
+
while (Object.getPrototypeOf(proto) !== null) {
|
|
2395
|
+
proto = Object.getPrototypeOf(proto);
|
|
2396
|
+
}
|
|
2397
|
+
return Object.getPrototypeOf(value) === proto;
|
|
2398
|
+
}
|
|
2399
|
+
function deepMerge(...objects) {
|
|
2400
|
+
const output = {};
|
|
2401
|
+
for (const obj of objects) {
|
|
2402
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2403
|
+
const prevValue = output[key];
|
|
2404
|
+
output[key] = isPlainObject(prevValue) && isPlainObject(value) ? deepMerge(prevValue, value) : value;
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
return output;
|
|
2408
|
+
}
|
|
2409
|
+
function makeTheme(...themes) {
|
|
2410
|
+
const themesToMerge = [
|
|
2411
|
+
getDefaultTheme(),
|
|
2412
|
+
...themes.filter((theme) => theme != null)
|
|
2413
|
+
];
|
|
2414
|
+
return deepMerge(...themesToMerge);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/use-prefix.js
|
|
2418
|
+
function usePrefix({ status = "idle", theme }) {
|
|
2419
|
+
const [showLoader, setShowLoader] = useState(false);
|
|
2420
|
+
const [tick, setTick] = useState(0);
|
|
2421
|
+
const { prefix, spinner } = makeTheme(theme);
|
|
2422
|
+
useEffect(() => {
|
|
2423
|
+
if (status === "loading") {
|
|
2424
|
+
let tickInterval;
|
|
2425
|
+
let inc = -1;
|
|
2426
|
+
const delayTimeout = setTimeout(() => {
|
|
2427
|
+
setShowLoader(true);
|
|
2428
|
+
tickInterval = setInterval(() => {
|
|
2429
|
+
inc = inc + 1;
|
|
2430
|
+
setTick(inc % spinner.frames.length);
|
|
2431
|
+
}, spinner.interval);
|
|
2432
|
+
}, 300);
|
|
2433
|
+
return () => {
|
|
2434
|
+
clearTimeout(delayTimeout);
|
|
2435
|
+
clearInterval(tickInterval);
|
|
2436
|
+
};
|
|
2437
|
+
} else {
|
|
2438
|
+
setShowLoader(false);
|
|
1623
2439
|
}
|
|
2440
|
+
}, [status]);
|
|
2441
|
+
if (showLoader) {
|
|
2442
|
+
return spinner.frames[tick];
|
|
2443
|
+
}
|
|
2444
|
+
const iconName = status === "loading" ? "idle" : status;
|
|
2445
|
+
return typeof prefix === "string" ? prefix : prefix[iconName] ?? prefix["idle"];
|
|
2446
|
+
}
|
|
2447
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/use-memo.js
|
|
2448
|
+
function useMemo(fn, dependencies) {
|
|
2449
|
+
return withPointer((pointer) => {
|
|
2450
|
+
const prev = pointer.get();
|
|
2451
|
+
if (!prev || prev.dependencies.length !== dependencies.length || prev.dependencies.some((dep, i) => dep !== dependencies[i])) {
|
|
2452
|
+
const value = fn();
|
|
2453
|
+
pointer.set({ value, dependencies });
|
|
2454
|
+
return value;
|
|
2455
|
+
}
|
|
2456
|
+
return prev.value;
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/use-ref.js
|
|
2460
|
+
function useRef(val) {
|
|
2461
|
+
return useState({ current: val })[0];
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/use-keypress.js
|
|
2465
|
+
function useKeypress(userHandler) {
|
|
2466
|
+
const signal = useRef(userHandler);
|
|
2467
|
+
signal.current = userHandler;
|
|
2468
|
+
useEffect((rl) => {
|
|
2469
|
+
let ignore = false;
|
|
2470
|
+
const handler = withUpdates((_input, event) => {
|
|
2471
|
+
if (ignore)
|
|
2472
|
+
return;
|
|
2473
|
+
signal.current(event, rl);
|
|
2474
|
+
});
|
|
2475
|
+
rl.input.on("keypress", handler);
|
|
2476
|
+
return () => {
|
|
2477
|
+
ignore = true;
|
|
2478
|
+
rl.input.removeListener("keypress", handler);
|
|
2479
|
+
};
|
|
2480
|
+
}, []);
|
|
2481
|
+
}
|
|
2482
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/utils.js
|
|
2483
|
+
var import_cli_width = __toESM(require_cli_width(), 1);
|
|
2484
|
+
|
|
2485
|
+
// ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
|
|
2486
|
+
var getCodePointsLength = (() => {
|
|
2487
|
+
const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
|
|
2488
|
+
return (input) => {
|
|
2489
|
+
let surrogatePairsNr = 0;
|
|
2490
|
+
SURROGATE_PAIR_RE.lastIndex = 0;
|
|
2491
|
+
while (SURROGATE_PAIR_RE.test(input)) {
|
|
2492
|
+
surrogatePairsNr += 1;
|
|
2493
|
+
}
|
|
2494
|
+
return input.length - surrogatePairsNr;
|
|
1624
2495
|
};
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
2496
|
+
})();
|
|
2497
|
+
var isFullWidth = (x) => {
|
|
2498
|
+
return x === 12288 || x >= 65281 && x <= 65376 || x >= 65504 && x <= 65510;
|
|
2499
|
+
};
|
|
2500
|
+
var isWideNotCJKTNotEmoji = (x) => {
|
|
2501
|
+
return x === 8987 || x === 9001 || x >= 12272 && x <= 12287 || x >= 12289 && x <= 12350 || x >= 12441 && x <= 12543 || x >= 12549 && x <= 12591 || x >= 12593 && x <= 12686 || x >= 12688 && x <= 12771 || x >= 12783 && x <= 12830 || x >= 12832 && x <= 12871 || x >= 12880 && x <= 19903 || x >= 65040 && x <= 65049 || x >= 65072 && x <= 65106 || x >= 65108 && x <= 65126 || x >= 65128 && x <= 65131 || x >= 127488 && x <= 127490 || x >= 127504 && x <= 127547 || x >= 127552 && x <= 127560 || x >= 131072 && x <= 196605 || x >= 196608 && x <= 262141;
|
|
2502
|
+
};
|
|
2503
|
+
|
|
2504
|
+
// ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/index.js
|
|
2505
|
+
var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
|
|
2506
|
+
var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
|
|
2507
|
+
var CJKT_WIDE_RE = /(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu;
|
|
2508
|
+
var TAB_RE = /\t{1,1000}/y;
|
|
2509
|
+
var EMOJI_RE = /[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F\u20E3?))*/yu;
|
|
2510
|
+
var LATIN_RE = /(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y;
|
|
2511
|
+
var MODIFIER_RE = /\p{M}+/gu;
|
|
2512
|
+
var NO_TRUNCATION = { limit: Infinity, ellipsis: "" };
|
|
2513
|
+
var getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {}) => {
|
|
2514
|
+
const LIMIT = truncationOptions.limit ?? Infinity;
|
|
2515
|
+
const ELLIPSIS = truncationOptions.ellipsis ?? "";
|
|
2516
|
+
const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION, widthOptions).width : 0);
|
|
2517
|
+
const ANSI_WIDTH = 0;
|
|
2518
|
+
const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;
|
|
2519
|
+
const TAB_WIDTH = widthOptions.tabWidth ?? 8;
|
|
2520
|
+
const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;
|
|
2521
|
+
const FULL_WIDTH_WIDTH = 2;
|
|
2522
|
+
const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;
|
|
2523
|
+
const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;
|
|
2524
|
+
const PARSE_BLOCKS = [
|
|
2525
|
+
[LATIN_RE, REGULAR_WIDTH],
|
|
2526
|
+
[ANSI_RE, ANSI_WIDTH],
|
|
2527
|
+
[CONTROL_RE, CONTROL_WIDTH],
|
|
2528
|
+
[TAB_RE, TAB_WIDTH],
|
|
2529
|
+
[EMOJI_RE, EMOJI_WIDTH],
|
|
2530
|
+
[CJKT_WIDE_RE, WIDE_WIDTH]
|
|
2531
|
+
];
|
|
2532
|
+
let indexPrev = 0;
|
|
2533
|
+
let index = 0;
|
|
2534
|
+
let length = input.length;
|
|
2535
|
+
let lengthExtra = 0;
|
|
2536
|
+
let truncationEnabled = false;
|
|
2537
|
+
let truncationIndex = length;
|
|
2538
|
+
let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);
|
|
2539
|
+
let unmatchedStart = 0;
|
|
2540
|
+
let unmatchedEnd = 0;
|
|
2541
|
+
let width = 0;
|
|
2542
|
+
let widthExtra = 0;
|
|
2543
|
+
outer:
|
|
2544
|
+
while (true) {
|
|
2545
|
+
if (unmatchedEnd > unmatchedStart || index >= length && index > indexPrev) {
|
|
2546
|
+
const unmatched = input.slice(unmatchedStart, unmatchedEnd) || input.slice(indexPrev, index);
|
|
2547
|
+
lengthExtra = 0;
|
|
2548
|
+
for (const char of unmatched.replaceAll(MODIFIER_RE, "")) {
|
|
2549
|
+
const codePoint = char.codePointAt(0) || 0;
|
|
2550
|
+
if (isFullWidth(codePoint)) {
|
|
2551
|
+
widthExtra = FULL_WIDTH_WIDTH;
|
|
2552
|
+
} else if (isWideNotCJKTNotEmoji(codePoint)) {
|
|
2553
|
+
widthExtra = WIDE_WIDTH;
|
|
2554
|
+
} else {
|
|
2555
|
+
widthExtra = REGULAR_WIDTH;
|
|
2556
|
+
}
|
|
2557
|
+
if (width + widthExtra > truncationLimit) {
|
|
2558
|
+
truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);
|
|
2559
|
+
}
|
|
2560
|
+
if (width + widthExtra > LIMIT) {
|
|
2561
|
+
truncationEnabled = true;
|
|
2562
|
+
break outer;
|
|
2563
|
+
}
|
|
2564
|
+
lengthExtra += char.length;
|
|
2565
|
+
width += widthExtra;
|
|
2566
|
+
}
|
|
2567
|
+
unmatchedStart = unmatchedEnd = 0;
|
|
1641
2568
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
2569
|
+
if (index >= length) {
|
|
2570
|
+
break outer;
|
|
2571
|
+
}
|
|
2572
|
+
for (let i = 0, l = PARSE_BLOCKS.length;i < l; i++) {
|
|
2573
|
+
const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];
|
|
2574
|
+
BLOCK_RE.lastIndex = index;
|
|
2575
|
+
if (BLOCK_RE.test(input)) {
|
|
2576
|
+
lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;
|
|
2577
|
+
widthExtra = lengthExtra * BLOCK_WIDTH;
|
|
2578
|
+
if (width + widthExtra > truncationLimit) {
|
|
2579
|
+
truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));
|
|
2580
|
+
}
|
|
2581
|
+
if (width + widthExtra > LIMIT) {
|
|
2582
|
+
truncationEnabled = true;
|
|
2583
|
+
break outer;
|
|
2584
|
+
}
|
|
2585
|
+
width += widthExtra;
|
|
2586
|
+
unmatchedStart = indexPrev;
|
|
2587
|
+
unmatchedEnd = index;
|
|
2588
|
+
index = indexPrev = BLOCK_RE.lastIndex;
|
|
2589
|
+
continue outer;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
index += 1;
|
|
1664
2593
|
}
|
|
2594
|
+
return {
|
|
2595
|
+
width: truncationEnabled ? truncationLimit : width,
|
|
2596
|
+
index: truncationEnabled ? truncationIndex : length,
|
|
2597
|
+
truncated: truncationEnabled,
|
|
2598
|
+
ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH
|
|
1665
2599
|
};
|
|
1666
|
-
}
|
|
2600
|
+
};
|
|
2601
|
+
var dist_default2 = getStringTruncatedWidth;
|
|
1667
2602
|
|
|
1668
|
-
//
|
|
1669
|
-
|
|
1670
|
-
|
|
2603
|
+
// ../../node_modules/.bun/fast-string-width@3.0.2/node_modules/fast-string-width/dist/index.js
|
|
2604
|
+
var NO_TRUNCATION2 = {
|
|
2605
|
+
limit: Infinity,
|
|
2606
|
+
ellipsis: "",
|
|
2607
|
+
ellipsisWidth: 0
|
|
2608
|
+
};
|
|
2609
|
+
var fastStringWidth = (input, options = {}) => {
|
|
2610
|
+
return dist_default2(input, NO_TRUNCATION2, options).width;
|
|
2611
|
+
};
|
|
2612
|
+
var dist_default3 = fastStringWidth;
|
|
2613
|
+
|
|
2614
|
+
// ../../node_modules/.bun/fast-wrap-ansi@0.2.2/node_modules/fast-wrap-ansi/lib/main.js
|
|
2615
|
+
var ESC = "\x1B";
|
|
2616
|
+
var CSI = "";
|
|
2617
|
+
var END_CODE = 39;
|
|
2618
|
+
var ANSI_ESCAPE_BELL = "\x07";
|
|
2619
|
+
var ANSI_CSI = "[";
|
|
2620
|
+
var ANSI_OSC = "]";
|
|
2621
|
+
var ANSI_SGR_TERMINATOR = "m";
|
|
2622
|
+
var ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
|
|
2623
|
+
var GROUP_REGEX = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`, "y");
|
|
2624
|
+
var getClosingCode = (openingCode) => {
|
|
2625
|
+
if (openingCode >= 30 && openingCode <= 37)
|
|
2626
|
+
return 39;
|
|
2627
|
+
if (openingCode >= 90 && openingCode <= 97)
|
|
2628
|
+
return 39;
|
|
2629
|
+
if (openingCode >= 40 && openingCode <= 47)
|
|
2630
|
+
return 49;
|
|
2631
|
+
if (openingCode >= 100 && openingCode <= 107)
|
|
2632
|
+
return 49;
|
|
2633
|
+
if (openingCode === 1 || openingCode === 2)
|
|
2634
|
+
return 22;
|
|
2635
|
+
if (openingCode === 3)
|
|
2636
|
+
return 23;
|
|
2637
|
+
if (openingCode === 4)
|
|
2638
|
+
return 24;
|
|
2639
|
+
if (openingCode === 7)
|
|
2640
|
+
return 27;
|
|
2641
|
+
if (openingCode === 8)
|
|
2642
|
+
return 28;
|
|
2643
|
+
if (openingCode === 9)
|
|
2644
|
+
return 29;
|
|
2645
|
+
if (openingCode === 0)
|
|
2646
|
+
return 0;
|
|
2647
|
+
return;
|
|
2648
|
+
};
|
|
2649
|
+
var wrapAnsiCode = (code) => `${ESC}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
|
|
2650
|
+
var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
|
|
2651
|
+
var wrapWord = (rows, word, columns) => {
|
|
2652
|
+
const characters = word[Symbol.iterator]();
|
|
2653
|
+
let isInsideEscape = false;
|
|
2654
|
+
let isInsideLinkEscape = false;
|
|
2655
|
+
let lastRow = rows.at(-1);
|
|
2656
|
+
let visible = lastRow === undefined ? 0 : dist_default3(lastRow);
|
|
2657
|
+
let currentCharacter = characters.next();
|
|
2658
|
+
let nextCharacter = characters.next();
|
|
2659
|
+
let rawCharacterIndex = 0;
|
|
2660
|
+
while (!currentCharacter.done) {
|
|
2661
|
+
const character = currentCharacter.value;
|
|
2662
|
+
const characterLength = dist_default3(character);
|
|
2663
|
+
if (visible + characterLength <= columns) {
|
|
2664
|
+
rows[rows.length - 1] += character;
|
|
2665
|
+
} else {
|
|
2666
|
+
rows.push(character);
|
|
2667
|
+
visible = 0;
|
|
2668
|
+
}
|
|
2669
|
+
if (character === ESC || character === CSI) {
|
|
2670
|
+
isInsideEscape = true;
|
|
2671
|
+
isInsideLinkEscape = word.startsWith(ANSI_ESCAPE_LINK, rawCharacterIndex + 1);
|
|
2672
|
+
}
|
|
2673
|
+
if (isInsideEscape) {
|
|
2674
|
+
if (isInsideLinkEscape) {
|
|
2675
|
+
if (character === ANSI_ESCAPE_BELL) {
|
|
2676
|
+
isInsideEscape = false;
|
|
2677
|
+
isInsideLinkEscape = false;
|
|
2678
|
+
}
|
|
2679
|
+
} else if (character === ANSI_SGR_TERMINATOR) {
|
|
2680
|
+
isInsideEscape = false;
|
|
2681
|
+
}
|
|
2682
|
+
} else {
|
|
2683
|
+
visible += characterLength;
|
|
2684
|
+
if (visible === columns && !nextCharacter.done) {
|
|
2685
|
+
rows.push("");
|
|
2686
|
+
visible = 0;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
currentCharacter = nextCharacter;
|
|
2690
|
+
nextCharacter = characters.next();
|
|
2691
|
+
rawCharacterIndex += character.length;
|
|
2692
|
+
}
|
|
2693
|
+
lastRow = rows.at(-1);
|
|
2694
|
+
if (!visible && lastRow !== undefined && lastRow.length && rows.length > 1) {
|
|
2695
|
+
rows[rows.length - 2] += rows.pop();
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
var stringVisibleTrimSpacesRight = (string) => {
|
|
2699
|
+
const words = string.split(" ");
|
|
2700
|
+
let last = words.length;
|
|
2701
|
+
while (last) {
|
|
2702
|
+
if (dist_default3(words[last - 1])) {
|
|
2703
|
+
break;
|
|
2704
|
+
}
|
|
2705
|
+
last--;
|
|
2706
|
+
}
|
|
2707
|
+
if (last === words.length) {
|
|
2708
|
+
return string;
|
|
2709
|
+
}
|
|
2710
|
+
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
2711
|
+
};
|
|
2712
|
+
var exec = (string, columns, options = {}) => {
|
|
2713
|
+
if (options.trim !== false && string.trim() === "") {
|
|
2714
|
+
return "";
|
|
2715
|
+
}
|
|
2716
|
+
let returnValue = "";
|
|
2717
|
+
let escapeCode;
|
|
2718
|
+
let escapeUrl;
|
|
2719
|
+
const words = string.split(" ");
|
|
2720
|
+
let rows = [""];
|
|
2721
|
+
let rowLength = 0;
|
|
2722
|
+
for (let index = 0;index < words.length; index++) {
|
|
2723
|
+
const word = words[index];
|
|
2724
|
+
if (options.trim !== false) {
|
|
2725
|
+
const row = rows.at(-1) ?? "";
|
|
2726
|
+
const trimmed = row.trimStart();
|
|
2727
|
+
if (row.length !== trimmed.length) {
|
|
2728
|
+
rows[rows.length - 1] = trimmed;
|
|
2729
|
+
rowLength = dist_default3(trimmed);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
if (index !== 0) {
|
|
2733
|
+
if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
|
|
2734
|
+
rows.push("");
|
|
2735
|
+
rowLength = 0;
|
|
2736
|
+
}
|
|
2737
|
+
if (rowLength || options.trim === false) {
|
|
2738
|
+
rows[rows.length - 1] += " ";
|
|
2739
|
+
rowLength++;
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
const wordLength = dist_default3(word);
|
|
2743
|
+
if (options.hard && wordLength > columns) {
|
|
2744
|
+
const remainingColumns = columns - rowLength;
|
|
2745
|
+
const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns);
|
|
2746
|
+
const breaksStartingNextLine = Math.floor((wordLength - 1) / columns);
|
|
2747
|
+
if (breaksStartingNextLine < breaksStartingThisLine) {
|
|
2748
|
+
rows.push("");
|
|
2749
|
+
}
|
|
2750
|
+
wrapWord(rows, word, columns);
|
|
2751
|
+
rowLength = dist_default3(rows.at(-1) ?? "");
|
|
2752
|
+
continue;
|
|
2753
|
+
}
|
|
2754
|
+
if (rowLength + wordLength > columns && rowLength && wordLength) {
|
|
2755
|
+
if (options.wordWrap === false && rowLength < columns) {
|
|
2756
|
+
wrapWord(rows, word, columns);
|
|
2757
|
+
rowLength = dist_default3(rows.at(-1) ?? "");
|
|
2758
|
+
continue;
|
|
2759
|
+
}
|
|
2760
|
+
rows.push("");
|
|
2761
|
+
rowLength = 0;
|
|
2762
|
+
}
|
|
2763
|
+
if (rowLength + wordLength > columns && options.wordWrap === false) {
|
|
2764
|
+
wrapWord(rows, word, columns);
|
|
2765
|
+
rowLength = dist_default3(rows.at(-1) ?? "");
|
|
2766
|
+
continue;
|
|
2767
|
+
}
|
|
2768
|
+
rows[rows.length - 1] += word;
|
|
2769
|
+
rowLength += wordLength;
|
|
2770
|
+
}
|
|
2771
|
+
if (options.trim !== false) {
|
|
2772
|
+
rows = rows.map((row) => stringVisibleTrimSpacesRight(row));
|
|
2773
|
+
}
|
|
2774
|
+
const preString = rows.join(`
|
|
2775
|
+
`);
|
|
2776
|
+
let inSurrogate = false;
|
|
2777
|
+
for (let i = 0;i < preString.length; i++) {
|
|
2778
|
+
const character = preString[i];
|
|
2779
|
+
returnValue += character;
|
|
2780
|
+
if (!inSurrogate) {
|
|
2781
|
+
inSurrogate = character >= "\uD800" && character <= "\uDBFF";
|
|
2782
|
+
if (inSurrogate) {
|
|
2783
|
+
continue;
|
|
2784
|
+
}
|
|
2785
|
+
} else {
|
|
2786
|
+
inSurrogate = false;
|
|
2787
|
+
}
|
|
2788
|
+
if (character === ESC || character === CSI) {
|
|
2789
|
+
GROUP_REGEX.lastIndex = i + 1;
|
|
2790
|
+
const groupsResult = GROUP_REGEX.exec(preString);
|
|
2791
|
+
const groups = groupsResult?.groups;
|
|
2792
|
+
if (groups?.code !== undefined) {
|
|
2793
|
+
const code = Number.parseFloat(groups.code);
|
|
2794
|
+
escapeCode = code === END_CODE ? undefined : code;
|
|
2795
|
+
} else if (groups?.uri !== undefined) {
|
|
2796
|
+
escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
if (preString[i + 1] === `
|
|
2800
|
+
`) {
|
|
2801
|
+
if (escapeUrl) {
|
|
2802
|
+
returnValue += wrapAnsiHyperlink("");
|
|
2803
|
+
}
|
|
2804
|
+
const closingCode = escapeCode ? getClosingCode(escapeCode) : undefined;
|
|
2805
|
+
if (escapeCode && closingCode) {
|
|
2806
|
+
returnValue += wrapAnsiCode(closingCode);
|
|
2807
|
+
}
|
|
2808
|
+
} else if (character === `
|
|
2809
|
+
`) {
|
|
2810
|
+
if (escapeCode && getClosingCode(escapeCode)) {
|
|
2811
|
+
returnValue += wrapAnsiCode(escapeCode);
|
|
2812
|
+
}
|
|
2813
|
+
if (escapeUrl) {
|
|
2814
|
+
returnValue += wrapAnsiHyperlink(escapeUrl);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
return returnValue;
|
|
2819
|
+
};
|
|
2820
|
+
var CRLF_OR_LF = /\r?\n/;
|
|
2821
|
+
function wrapAnsi(string, columns, options) {
|
|
2822
|
+
return String(string).normalize().split(CRLF_OR_LF).map((line) => exec(line, columns, options)).join(`
|
|
2823
|
+
`);
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/utils.js
|
|
2827
|
+
function breakLines(content, width) {
|
|
2828
|
+
return content.split(`
|
|
2829
|
+
`).flatMap((line) => wrapAnsi(line, width, { trim: false, wordWrap: false }).split(`
|
|
2830
|
+
`).map((str) => str.trimEnd())).join(`
|
|
2831
|
+
`);
|
|
2832
|
+
}
|
|
2833
|
+
function readlineWidth() {
|
|
2834
|
+
return import_cli_width.default({ defaultWidth: 80, output: readline().output });
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
|
|
2838
|
+
function usePointerPosition({ active, renderedItems, pageSize, loop }) {
|
|
2839
|
+
const state = useRef({
|
|
2840
|
+
lastPointer: active,
|
|
2841
|
+
lastActive: undefined
|
|
2842
|
+
});
|
|
2843
|
+
const { lastPointer, lastActive } = state.current;
|
|
2844
|
+
const middle = Math.floor(pageSize / 2);
|
|
2845
|
+
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
|
|
2846
|
+
const defaultPointerPosition = renderedItems.slice(0, active).reduce((acc, item) => acc + item.length, 0);
|
|
2847
|
+
let pointer = defaultPointerPosition;
|
|
2848
|
+
if (renderedLength > pageSize) {
|
|
2849
|
+
if (loop) {
|
|
2850
|
+
pointer = lastPointer;
|
|
2851
|
+
if (lastActive != null && lastActive < active && active - lastActive < pageSize) {
|
|
2852
|
+
pointer = Math.min(middle, Math.abs(active - lastActive) === 1 ? Math.min(lastPointer + (renderedItems[lastActive]?.length ?? 0), Math.max(defaultPointerPosition, lastPointer)) : lastPointer + active - lastActive);
|
|
2853
|
+
}
|
|
2854
|
+
} else {
|
|
2855
|
+
const spaceUnderActive = renderedItems.slice(active).reduce((acc, item) => acc + item.length, 0);
|
|
2856
|
+
pointer = spaceUnderActive < pageSize - middle ? pageSize - spaceUnderActive : Math.min(defaultPointerPosition, middle);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
state.current.lastPointer = pointer;
|
|
2860
|
+
state.current.lastActive = active;
|
|
2861
|
+
return pointer;
|
|
2862
|
+
}
|
|
2863
|
+
function usePagination({ items, active, renderItem, pageSize, loop = true }) {
|
|
2864
|
+
const width = readlineWidth();
|
|
2865
|
+
const bound = (num) => (num % items.length + items.length) % items.length;
|
|
2866
|
+
const renderedItems = items.map((item, index) => {
|
|
2867
|
+
if (item == null)
|
|
2868
|
+
return [];
|
|
2869
|
+
return breakLines(renderItem({ item, index, isActive: index === active }), width).split(`
|
|
2870
|
+
`);
|
|
2871
|
+
});
|
|
2872
|
+
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
|
|
2873
|
+
const renderItemAtIndex = (index) => renderedItems[index] ?? [];
|
|
2874
|
+
const pointer = usePointerPosition({ active, renderedItems, pageSize, loop });
|
|
2875
|
+
const activeItem = renderItemAtIndex(active).slice(0, pageSize);
|
|
2876
|
+
const activeItemPosition = pointer + activeItem.length <= pageSize ? pointer : pageSize - activeItem.length;
|
|
2877
|
+
const pageBuffer = Array.from({ length: pageSize });
|
|
2878
|
+
pageBuffer.splice(activeItemPosition, activeItem.length, ...activeItem);
|
|
2879
|
+
const itemVisited = new Set([active]);
|
|
2880
|
+
let bufferPointer = activeItemPosition + activeItem.length;
|
|
2881
|
+
let itemPointer = bound(active + 1);
|
|
2882
|
+
while (bufferPointer < pageSize && !itemVisited.has(itemPointer) && (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer > active)) {
|
|
2883
|
+
const lines = renderItemAtIndex(itemPointer);
|
|
2884
|
+
const linesToAdd = lines.slice(0, pageSize - bufferPointer);
|
|
2885
|
+
pageBuffer.splice(bufferPointer, linesToAdd.length, ...linesToAdd);
|
|
2886
|
+
itemVisited.add(itemPointer);
|
|
2887
|
+
bufferPointer += linesToAdd.length;
|
|
2888
|
+
itemPointer = bound(itemPointer + 1);
|
|
2889
|
+
}
|
|
2890
|
+
bufferPointer = activeItemPosition - 1;
|
|
2891
|
+
itemPointer = bound(active - 1);
|
|
2892
|
+
while (bufferPointer >= 0 && !itemVisited.has(itemPointer) && (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer < active)) {
|
|
2893
|
+
const lines = renderItemAtIndex(itemPointer);
|
|
2894
|
+
const linesToAdd = lines.slice(Math.max(0, lines.length - bufferPointer - 1));
|
|
2895
|
+
pageBuffer.splice(bufferPointer - linesToAdd.length + 1, linesToAdd.length, ...linesToAdd);
|
|
2896
|
+
itemVisited.add(itemPointer);
|
|
2897
|
+
bufferPointer -= linesToAdd.length;
|
|
2898
|
+
itemPointer = bound(itemPointer - 1);
|
|
2899
|
+
}
|
|
2900
|
+
return pageBuffer.filter((line) => typeof line === "string").join(`
|
|
2901
|
+
`);
|
|
2902
|
+
}
|
|
2903
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/create-prompt.js
|
|
2904
|
+
var import_mute_stream = __toESM(require_lib(), 1);
|
|
2905
|
+
import * as readline2 from "node:readline";
|
|
2906
|
+
import { AsyncResource as AsyncResource3 } from "node:async_hooks";
|
|
2907
|
+
|
|
2908
|
+
// ../../node_modules/.bun/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/signals.js
|
|
2909
|
+
var signals = [];
|
|
2910
|
+
signals.push("SIGHUP", "SIGINT", "SIGTERM");
|
|
2911
|
+
if (process.platform !== "win32") {
|
|
2912
|
+
signals.push("SIGALRM", "SIGABRT", "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "SIGUSR2", "SIGTRAP", "SIGSYS", "SIGQUIT", "SIGIOT");
|
|
2913
|
+
}
|
|
2914
|
+
if (process.platform === "linux") {
|
|
2915
|
+
signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT");
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
// ../../node_modules/.bun/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.js
|
|
2919
|
+
var processOk = (process3) => !!process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
|
|
2920
|
+
var kExitEmitter = Symbol.for("signal-exit emitter");
|
|
2921
|
+
var global = globalThis;
|
|
2922
|
+
var ObjectDefineProperty = Object.defineProperty.bind(Object);
|
|
2923
|
+
|
|
2924
|
+
class Emitter {
|
|
2925
|
+
emitted = {
|
|
2926
|
+
afterExit: false,
|
|
2927
|
+
exit: false
|
|
2928
|
+
};
|
|
2929
|
+
listeners = {
|
|
2930
|
+
afterExit: [],
|
|
2931
|
+
exit: []
|
|
2932
|
+
};
|
|
2933
|
+
count = 0;
|
|
2934
|
+
id = Math.random();
|
|
2935
|
+
constructor() {
|
|
2936
|
+
if (global[kExitEmitter]) {
|
|
2937
|
+
return global[kExitEmitter];
|
|
2938
|
+
}
|
|
2939
|
+
ObjectDefineProperty(global, kExitEmitter, {
|
|
2940
|
+
value: this,
|
|
2941
|
+
writable: false,
|
|
2942
|
+
enumerable: false,
|
|
2943
|
+
configurable: false
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
on(ev, fn) {
|
|
2947
|
+
this.listeners[ev].push(fn);
|
|
2948
|
+
}
|
|
2949
|
+
removeListener(ev, fn) {
|
|
2950
|
+
const list = this.listeners[ev];
|
|
2951
|
+
const i = list.indexOf(fn);
|
|
2952
|
+
if (i === -1) {
|
|
2953
|
+
return;
|
|
2954
|
+
}
|
|
2955
|
+
if (i === 0 && list.length === 1) {
|
|
2956
|
+
list.length = 0;
|
|
2957
|
+
} else {
|
|
2958
|
+
list.splice(i, 1);
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
emit(ev, code, signal) {
|
|
2962
|
+
if (this.emitted[ev]) {
|
|
2963
|
+
return false;
|
|
2964
|
+
}
|
|
2965
|
+
this.emitted[ev] = true;
|
|
2966
|
+
let ret = false;
|
|
2967
|
+
for (const fn of this.listeners[ev]) {
|
|
2968
|
+
ret = fn(code, signal) === true || ret;
|
|
2969
|
+
}
|
|
2970
|
+
if (ev === "exit") {
|
|
2971
|
+
ret = this.emit("afterExit", code, signal) || ret;
|
|
2972
|
+
}
|
|
2973
|
+
return ret;
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
class SignalExitBase {
|
|
2978
|
+
}
|
|
2979
|
+
var signalExitWrap = (handler) => {
|
|
2980
|
+
return {
|
|
2981
|
+
onExit(cb, opts) {
|
|
2982
|
+
return handler.onExit(cb, opts);
|
|
2983
|
+
},
|
|
2984
|
+
load() {
|
|
2985
|
+
return handler.load();
|
|
2986
|
+
},
|
|
2987
|
+
unload() {
|
|
2988
|
+
return handler.unload();
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
};
|
|
2992
|
+
|
|
2993
|
+
class SignalExitFallback extends SignalExitBase {
|
|
2994
|
+
onExit() {
|
|
2995
|
+
return () => {};
|
|
2996
|
+
}
|
|
2997
|
+
load() {}
|
|
2998
|
+
unload() {}
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
class SignalExit extends SignalExitBase {
|
|
3002
|
+
#hupSig = process3.platform === "win32" ? "SIGINT" : "SIGHUP";
|
|
3003
|
+
#emitter = new Emitter;
|
|
3004
|
+
#process;
|
|
3005
|
+
#originalProcessEmit;
|
|
3006
|
+
#originalProcessReallyExit;
|
|
3007
|
+
#sigListeners = {};
|
|
3008
|
+
#loaded = false;
|
|
3009
|
+
constructor(process3) {
|
|
3010
|
+
super();
|
|
3011
|
+
this.#process = process3;
|
|
3012
|
+
this.#sigListeners = {};
|
|
3013
|
+
for (const sig of signals) {
|
|
3014
|
+
this.#sigListeners[sig] = () => {
|
|
3015
|
+
const listeners = this.#process.listeners(sig);
|
|
3016
|
+
let { count } = this.#emitter;
|
|
3017
|
+
const p = process3;
|
|
3018
|
+
if (typeof p.__signal_exit_emitter__ === "object" && typeof p.__signal_exit_emitter__.count === "number") {
|
|
3019
|
+
count += p.__signal_exit_emitter__.count;
|
|
3020
|
+
}
|
|
3021
|
+
if (listeners.length === count) {
|
|
3022
|
+
this.unload();
|
|
3023
|
+
const ret = this.#emitter.emit("exit", null, sig);
|
|
3024
|
+
const s = sig === "SIGHUP" ? this.#hupSig : sig;
|
|
3025
|
+
if (!ret)
|
|
3026
|
+
process3.kill(process3.pid, s);
|
|
3027
|
+
}
|
|
3028
|
+
};
|
|
3029
|
+
}
|
|
3030
|
+
this.#originalProcessReallyExit = process3.reallyExit;
|
|
3031
|
+
this.#originalProcessEmit = process3.emit;
|
|
3032
|
+
}
|
|
3033
|
+
onExit(cb, opts) {
|
|
3034
|
+
if (!processOk(this.#process)) {
|
|
3035
|
+
return () => {};
|
|
3036
|
+
}
|
|
3037
|
+
if (this.#loaded === false) {
|
|
3038
|
+
this.load();
|
|
3039
|
+
}
|
|
3040
|
+
const ev = opts?.alwaysLast ? "afterExit" : "exit";
|
|
3041
|
+
this.#emitter.on(ev, cb);
|
|
3042
|
+
return () => {
|
|
3043
|
+
this.#emitter.removeListener(ev, cb);
|
|
3044
|
+
if (this.#emitter.listeners["exit"].length === 0 && this.#emitter.listeners["afterExit"].length === 0) {
|
|
3045
|
+
this.unload();
|
|
3046
|
+
}
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
load() {
|
|
3050
|
+
if (this.#loaded) {
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
this.#loaded = true;
|
|
3054
|
+
this.#emitter.count += 1;
|
|
3055
|
+
for (const sig of signals) {
|
|
3056
|
+
try {
|
|
3057
|
+
const fn = this.#sigListeners[sig];
|
|
3058
|
+
if (fn)
|
|
3059
|
+
this.#process.on(sig, fn);
|
|
3060
|
+
} catch (_) {}
|
|
3061
|
+
}
|
|
3062
|
+
this.#process.emit = (ev, ...a) => {
|
|
3063
|
+
return this.#processEmit(ev, ...a);
|
|
3064
|
+
};
|
|
3065
|
+
this.#process.reallyExit = (code) => {
|
|
3066
|
+
return this.#processReallyExit(code);
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
unload() {
|
|
3070
|
+
if (!this.#loaded) {
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
3073
|
+
this.#loaded = false;
|
|
3074
|
+
signals.forEach((sig) => {
|
|
3075
|
+
const listener = this.#sigListeners[sig];
|
|
3076
|
+
if (!listener) {
|
|
3077
|
+
throw new Error("Listener not defined for signal: " + sig);
|
|
3078
|
+
}
|
|
3079
|
+
try {
|
|
3080
|
+
this.#process.removeListener(sig, listener);
|
|
3081
|
+
} catch (_) {}
|
|
3082
|
+
});
|
|
3083
|
+
this.#process.emit = this.#originalProcessEmit;
|
|
3084
|
+
this.#process.reallyExit = this.#originalProcessReallyExit;
|
|
3085
|
+
this.#emitter.count -= 1;
|
|
3086
|
+
}
|
|
3087
|
+
#processReallyExit(code) {
|
|
3088
|
+
if (!processOk(this.#process)) {
|
|
3089
|
+
return 0;
|
|
3090
|
+
}
|
|
3091
|
+
this.#process.exitCode = code || 0;
|
|
3092
|
+
this.#emitter.emit("exit", this.#process.exitCode, null);
|
|
3093
|
+
return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
|
|
3094
|
+
}
|
|
3095
|
+
#processEmit(ev, ...args) {
|
|
3096
|
+
const og = this.#originalProcessEmit;
|
|
3097
|
+
if (ev === "exit" && processOk(this.#process)) {
|
|
3098
|
+
if (typeof args[0] === "number") {
|
|
3099
|
+
this.#process.exitCode = args[0];
|
|
3100
|
+
}
|
|
3101
|
+
const ret = og.call(this.#process, ev, ...args);
|
|
3102
|
+
this.#emitter.emit("exit", this.#process.exitCode, null);
|
|
3103
|
+
return ret;
|
|
3104
|
+
} else {
|
|
3105
|
+
return og.call(this.#process, ev, ...args);
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
var process3 = globalThis.process;
|
|
3110
|
+
var {
|
|
3111
|
+
onExit,
|
|
3112
|
+
load,
|
|
3113
|
+
unload
|
|
3114
|
+
} = signalExitWrap(processOk(process3) ? new SignalExit(process3) : new SignalExitFallback);
|
|
3115
|
+
|
|
3116
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/screen-manager.js
|
|
3117
|
+
import { stripVTControlCharacters } from "node:util";
|
|
3118
|
+
|
|
3119
|
+
// ../../node_modules/.bun/@inquirer+ansi@2.0.7/node_modules/@inquirer/ansi/dist/index.js
|
|
3120
|
+
var ESC2 = "\x1B[";
|
|
3121
|
+
var cursorLeft = ESC2 + "G";
|
|
3122
|
+
var cursorHide = ESC2 + "?25l";
|
|
3123
|
+
var cursorShow = ESC2 + "?25h";
|
|
3124
|
+
var cursorUp = (rows = 1) => rows > 0 ? `${ESC2}${rows}A` : "";
|
|
3125
|
+
var cursorDown = (rows = 1) => rows > 0 ? `${ESC2}${rows}B` : "";
|
|
3126
|
+
var cursorTo = (x, y) => {
|
|
3127
|
+
if (typeof y === "number" && !Number.isNaN(y)) {
|
|
3128
|
+
return `${ESC2}${y + 1};${x + 1}H`;
|
|
3129
|
+
}
|
|
3130
|
+
return `${ESC2}${x + 1}G`;
|
|
3131
|
+
};
|
|
3132
|
+
var eraseLine = ESC2 + "2K";
|
|
3133
|
+
var eraseLines = (lines) => lines > 0 ? (eraseLine + cursorUp(1)).repeat(lines - 1) + eraseLine + cursorLeft : "";
|
|
3134
|
+
|
|
3135
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/screen-manager.js
|
|
3136
|
+
var height = (content) => content.split(`
|
|
3137
|
+
`).length;
|
|
3138
|
+
var lastLine = (content) => content.split(`
|
|
3139
|
+
`).pop() ?? "";
|
|
3140
|
+
|
|
3141
|
+
class ScreenManager {
|
|
3142
|
+
height = 0;
|
|
3143
|
+
extraLinesUnderPrompt = 0;
|
|
3144
|
+
cursorPos;
|
|
3145
|
+
rl;
|
|
3146
|
+
constructor(rl) {
|
|
3147
|
+
this.rl = rl;
|
|
3148
|
+
this.cursorPos = rl.getCursorPos();
|
|
3149
|
+
}
|
|
3150
|
+
write(content) {
|
|
3151
|
+
this.rl.output.unmute();
|
|
3152
|
+
this.rl.output.write(content);
|
|
3153
|
+
this.rl.output.mute();
|
|
3154
|
+
}
|
|
3155
|
+
render(content, bottomContent = "") {
|
|
3156
|
+
const promptLine = lastLine(content);
|
|
3157
|
+
const rawPromptLine = stripVTControlCharacters(promptLine);
|
|
3158
|
+
let prompt = rawPromptLine;
|
|
3159
|
+
if (this.rl.line.length > 0) {
|
|
3160
|
+
prompt = prompt.slice(0, -this.rl.line.length);
|
|
3161
|
+
}
|
|
3162
|
+
this.rl.setPrompt(prompt);
|
|
3163
|
+
this.cursorPos = this.rl.getCursorPos();
|
|
3164
|
+
const width = readlineWidth();
|
|
3165
|
+
content = breakLines(content, width);
|
|
3166
|
+
bottomContent = breakLines(bottomContent, width);
|
|
3167
|
+
if (rawPromptLine.length % width === 0) {
|
|
3168
|
+
content += `
|
|
3169
|
+
`;
|
|
3170
|
+
}
|
|
3171
|
+
let output = content + (bottomContent ? `
|
|
3172
|
+
` + bottomContent : "");
|
|
3173
|
+
const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows;
|
|
3174
|
+
const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
|
|
3175
|
+
if (bottomContentHeight > 0)
|
|
3176
|
+
output += cursorUp(bottomContentHeight);
|
|
3177
|
+
output += cursorTo(this.cursorPos.cols);
|
|
3178
|
+
this.write(cursorDown(this.extraLinesUnderPrompt) + eraseLines(this.height) + output);
|
|
3179
|
+
this.extraLinesUnderPrompt = bottomContentHeight;
|
|
3180
|
+
this.height = height(output);
|
|
3181
|
+
}
|
|
3182
|
+
checkCursorPos() {
|
|
3183
|
+
const cursorPos = this.rl.getCursorPos();
|
|
3184
|
+
if (cursorPos.cols !== this.cursorPos.cols) {
|
|
3185
|
+
this.write(cursorTo(cursorPos.cols));
|
|
3186
|
+
this.cursorPos = cursorPos;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
done({ clearContent }) {
|
|
3190
|
+
this.rl.setPrompt("");
|
|
3191
|
+
let output = cursorDown(this.extraLinesUnderPrompt);
|
|
3192
|
+
output += clearContent ? eraseLines(this.height) : `
|
|
3193
|
+
`;
|
|
3194
|
+
output += cursorLeft;
|
|
3195
|
+
output += cursorShow;
|
|
3196
|
+
this.write(output);
|
|
3197
|
+
this.rl.close();
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/promise-polyfill.js
|
|
3202
|
+
class PromisePolyfill extends Promise {
|
|
3203
|
+
static withResolver() {
|
|
3204
|
+
let resolve4;
|
|
3205
|
+
let reject;
|
|
3206
|
+
const promise = new Promise((res, rej) => {
|
|
3207
|
+
resolve4 = res;
|
|
3208
|
+
reject = rej;
|
|
3209
|
+
});
|
|
3210
|
+
return { promise, resolve: resolve4, reject };
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/create-prompt.js
|
|
3215
|
+
import path from "node:path";
|
|
3216
|
+
var nativeSetImmediate = globalThis.setImmediate;
|
|
3217
|
+
function getCallSites() {
|
|
3218
|
+
const savedPrepareStackTrace = Error.prepareStackTrace;
|
|
3219
|
+
let result = [];
|
|
3220
|
+
try {
|
|
3221
|
+
Error.prepareStackTrace = (_, callSites) => {
|
|
3222
|
+
const callSitesWithoutCurrent = callSites.slice(1);
|
|
3223
|
+
result = callSitesWithoutCurrent;
|
|
3224
|
+
return callSitesWithoutCurrent;
|
|
3225
|
+
};
|
|
3226
|
+
new Error().stack;
|
|
3227
|
+
} catch {
|
|
3228
|
+
return result;
|
|
3229
|
+
}
|
|
3230
|
+
Error.prepareStackTrace = savedPrepareStackTrace;
|
|
3231
|
+
return result;
|
|
3232
|
+
}
|
|
3233
|
+
function createPrompt(view) {
|
|
3234
|
+
const callSites = getCallSites();
|
|
3235
|
+
const prompt = (config, context = {}) => {
|
|
3236
|
+
const { input = process.stdin, signal } = context;
|
|
3237
|
+
const cleanups = new Set;
|
|
3238
|
+
const output = new import_mute_stream.default;
|
|
3239
|
+
output.pipe(context.output ?? process.stdout);
|
|
3240
|
+
const rl = readline2.createInterface({
|
|
3241
|
+
terminal: true,
|
|
3242
|
+
input,
|
|
3243
|
+
output
|
|
3244
|
+
});
|
|
3245
|
+
output.mute();
|
|
3246
|
+
const screen = new ScreenManager(rl);
|
|
3247
|
+
const { promise, resolve: resolve4, reject } = PromisePolyfill.withResolver();
|
|
3248
|
+
const cancel = () => reject(new CancelPromptError);
|
|
3249
|
+
if (signal) {
|
|
3250
|
+
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
|
3251
|
+
if (signal.aborted) {
|
|
3252
|
+
abort();
|
|
3253
|
+
return Object.assign(promise, { cancel });
|
|
3254
|
+
}
|
|
3255
|
+
signal.addEventListener("abort", abort);
|
|
3256
|
+
cleanups.add(() => signal.removeEventListener("abort", abort));
|
|
3257
|
+
}
|
|
3258
|
+
cleanups.add(onExit((code, signal2) => {
|
|
3259
|
+
reject(new ExitPromptError(`User force closed the prompt with ${code} ${signal2}`));
|
|
3260
|
+
}));
|
|
3261
|
+
const sigint = () => reject(new ExitPromptError(`User force closed the prompt with SIGINT`));
|
|
3262
|
+
rl.on("SIGINT", sigint);
|
|
3263
|
+
cleanups.add(() => rl.removeListener("SIGINT", sigint));
|
|
3264
|
+
return withHooks(rl, (cycle) => {
|
|
3265
|
+
const hooksCleanup = AsyncResource3.bind(() => effectScheduler.clearAll());
|
|
3266
|
+
rl.on("close", hooksCleanup);
|
|
3267
|
+
cleanups.add(() => rl.removeListener("close", hooksCleanup));
|
|
3268
|
+
const startCycle = () => {
|
|
3269
|
+
const checkCursorPos = () => screen.checkCursorPos();
|
|
3270
|
+
rl.input.on("keypress", checkCursorPos);
|
|
3271
|
+
cleanups.add(() => rl.input.removeListener("keypress", checkCursorPos));
|
|
3272
|
+
let pendingDone = null;
|
|
3273
|
+
cycle(() => {
|
|
3274
|
+
let effectsSettled = false;
|
|
3275
|
+
try {
|
|
3276
|
+
const nextView = view(config, (value) => {
|
|
3277
|
+
if (effectsSettled) {
|
|
3278
|
+
resolve4(value);
|
|
3279
|
+
} else {
|
|
3280
|
+
pendingDone = { value };
|
|
3281
|
+
}
|
|
3282
|
+
});
|
|
3283
|
+
if (nextView === undefined) {
|
|
3284
|
+
let callerFilename = callSites[1]?.getFileName();
|
|
3285
|
+
if (callerFilename && !callerFilename.startsWith("file://")) {
|
|
3286
|
+
callerFilename = path.resolve(callerFilename);
|
|
3287
|
+
}
|
|
3288
|
+
throw new Error(`Prompt functions must return a string.
|
|
3289
|
+
at ${callerFilename}`);
|
|
3290
|
+
}
|
|
3291
|
+
const [content, bottomContent] = typeof nextView === "string" ? [nextView] : nextView;
|
|
3292
|
+
screen.render(content, bottomContent);
|
|
3293
|
+
effectScheduler.run();
|
|
3294
|
+
} catch (error) {
|
|
3295
|
+
reject(error);
|
|
3296
|
+
}
|
|
3297
|
+
effectsSettled = true;
|
|
3298
|
+
if (pendingDone !== null) {
|
|
3299
|
+
const { value } = pendingDone;
|
|
3300
|
+
pendingDone = null;
|
|
3301
|
+
resolve4(value);
|
|
3302
|
+
}
|
|
3303
|
+
});
|
|
3304
|
+
};
|
|
3305
|
+
if ("readableFlowing" in input) {
|
|
3306
|
+
nativeSetImmediate(startCycle);
|
|
3307
|
+
} else {
|
|
3308
|
+
startCycle();
|
|
3309
|
+
}
|
|
3310
|
+
return Object.assign(promise.then((answer) => {
|
|
3311
|
+
effectScheduler.clearAll();
|
|
3312
|
+
return answer;
|
|
3313
|
+
}, (error) => {
|
|
3314
|
+
effectScheduler.clearAll();
|
|
3315
|
+
throw error;
|
|
3316
|
+
}).finally(() => {
|
|
3317
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
3318
|
+
screen.done({ clearContent: Boolean(context.clearPromptOnDone) });
|
|
3319
|
+
output.end();
|
|
3320
|
+
}).then(() => promise), { cancel });
|
|
3321
|
+
});
|
|
3322
|
+
};
|
|
3323
|
+
return prompt;
|
|
3324
|
+
}
|
|
3325
|
+
// ../../node_modules/.bun/@inquirer+core@11.2.1+e154a6929a67600d/node_modules/@inquirer/core/dist/lib/Separator.js
|
|
3326
|
+
import { styleText as styleText2 } from "node:util";
|
|
3327
|
+
class Separator {
|
|
3328
|
+
separator = styleText2("dim", Array.from({ length: 15 }).join(dist_default.line));
|
|
3329
|
+
type = "separator";
|
|
3330
|
+
constructor(separator) {
|
|
3331
|
+
if (separator) {
|
|
3332
|
+
this.separator = separator;
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
static isSeparator(choice) {
|
|
3336
|
+
return Boolean(choice && typeof choice === "object" && "type" in choice && choice.type === "separator");
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
// ../../node_modules/.bun/@inquirer+checkbox@5.2.1+e154a6929a67600d/node_modules/@inquirer/checkbox/dist/index.js
|
|
3340
|
+
import { styleText as styleText3 } from "node:util";
|
|
3341
|
+
var checkboxTheme = {
|
|
3342
|
+
icon: {
|
|
3343
|
+
checked: styleText3("green", dist_default.circleFilled),
|
|
3344
|
+
unchecked: dist_default.circle,
|
|
3345
|
+
cursor: dist_default.pointer,
|
|
3346
|
+
disabledChecked: styleText3("green", dist_default.circleDouble),
|
|
3347
|
+
disabledUnchecked: "-"
|
|
3348
|
+
},
|
|
3349
|
+
style: {
|
|
3350
|
+
disabled: (text) => styleText3("dim", text),
|
|
3351
|
+
renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
|
|
3352
|
+
description: (text) => styleText3("cyan", text),
|
|
3353
|
+
keysHelpTip: (keys) => keys.map(([key, action]) => `${styleText3("bold", key)} ${styleText3("dim", action)}`).join(styleText3("dim", " • "))
|
|
3354
|
+
},
|
|
3355
|
+
i18n: { disabledError: "This option is disabled and cannot be toggled." }
|
|
3356
|
+
};
|
|
3357
|
+
function isSelectable(item) {
|
|
3358
|
+
return !Separator.isSeparator(item) && !item.disabled;
|
|
3359
|
+
}
|
|
3360
|
+
function isNavigable(item) {
|
|
3361
|
+
return !Separator.isSeparator(item);
|
|
3362
|
+
}
|
|
3363
|
+
function isChecked(item) {
|
|
3364
|
+
return !Separator.isSeparator(item) && item.checked;
|
|
3365
|
+
}
|
|
3366
|
+
function toggle(item) {
|
|
3367
|
+
return isSelectable(item) ? { ...item, checked: !item.checked } : item;
|
|
3368
|
+
}
|
|
3369
|
+
function check(checked) {
|
|
3370
|
+
return function(item) {
|
|
3371
|
+
return isSelectable(item) ? { ...item, checked } : item;
|
|
3372
|
+
};
|
|
3373
|
+
}
|
|
3374
|
+
function normalizeChoices(choices) {
|
|
3375
|
+
return choices.map((choice) => {
|
|
3376
|
+
if (Separator.isSeparator(choice))
|
|
3377
|
+
return choice;
|
|
3378
|
+
if (typeof choice !== "object" || choice === null || !("value" in choice)) {
|
|
3379
|
+
const name2 = String(choice);
|
|
3380
|
+
return {
|
|
3381
|
+
value: choice,
|
|
3382
|
+
name: name2,
|
|
3383
|
+
short: name2,
|
|
3384
|
+
checkedName: name2,
|
|
3385
|
+
disabled: false,
|
|
3386
|
+
checked: false
|
|
3387
|
+
};
|
|
3388
|
+
}
|
|
3389
|
+
const name = choice.name ?? String(choice.value);
|
|
3390
|
+
const normalizedChoice = {
|
|
3391
|
+
value: choice.value,
|
|
3392
|
+
name,
|
|
3393
|
+
short: choice.short ?? name,
|
|
3394
|
+
checkedName: choice.checkedName ?? name,
|
|
3395
|
+
disabled: choice.disabled ?? false,
|
|
3396
|
+
checked: choice.checked ?? false
|
|
3397
|
+
};
|
|
3398
|
+
if (choice.description) {
|
|
3399
|
+
normalizedChoice.description = choice.description;
|
|
3400
|
+
}
|
|
3401
|
+
return normalizedChoice;
|
|
3402
|
+
});
|
|
3403
|
+
}
|
|
3404
|
+
var dist_default4 = createPrompt((config, done) => {
|
|
3405
|
+
const { pageSize = 7, loop = true, required, validate = () => true } = config;
|
|
3406
|
+
const shortcuts = { all: "a", invert: "i", ...config.shortcuts };
|
|
3407
|
+
const theme = makeTheme(checkboxTheme, config.theme);
|
|
3408
|
+
const { keybindings: keybindings2 } = theme;
|
|
3409
|
+
const [status, setStatus] = useState("idle");
|
|
3410
|
+
const prefix = usePrefix({ status, theme });
|
|
3411
|
+
const [items, setItems] = useState(normalizeChoices(config.choices));
|
|
3412
|
+
const bounds = useMemo(() => {
|
|
3413
|
+
const first = items.findIndex(isNavigable);
|
|
3414
|
+
const last = items.findLastIndex(isNavigable);
|
|
3415
|
+
if (first === -1) {
|
|
3416
|
+
throw new ValidationError("[checkbox prompt] No selectable choices. All choices are disabled.");
|
|
3417
|
+
}
|
|
3418
|
+
return { first, last };
|
|
3419
|
+
}, [items]);
|
|
3420
|
+
const [active, setActive] = useState(bounds.first);
|
|
3421
|
+
const [errorMsg, setError] = useState();
|
|
3422
|
+
useKeypress(async (key) => {
|
|
3423
|
+
if (isEnterKey(key)) {
|
|
3424
|
+
const selection = items.filter(isChecked);
|
|
3425
|
+
const isValid = await validate([...selection]);
|
|
3426
|
+
if (required && !selection.length) {
|
|
3427
|
+
setError("At least one choice must be selected");
|
|
3428
|
+
} else if (isValid === true) {
|
|
3429
|
+
setStatus("done");
|
|
3430
|
+
done(selection.map((choice) => choice.value));
|
|
3431
|
+
} else {
|
|
3432
|
+
setError(isValid || "You must select a valid value");
|
|
3433
|
+
}
|
|
3434
|
+
} else if (isUpKey(key, keybindings2) || isDownKey(key, keybindings2)) {
|
|
3435
|
+
if (errorMsg) {
|
|
3436
|
+
setError(undefined);
|
|
3437
|
+
}
|
|
3438
|
+
if (loop || isUpKey(key, keybindings2) && active !== bounds.first || isDownKey(key, keybindings2) && active !== bounds.last) {
|
|
3439
|
+
const offset = isUpKey(key, keybindings2) ? -1 : 1;
|
|
3440
|
+
let next = active;
|
|
3441
|
+
do {
|
|
3442
|
+
next = (next + offset + items.length) % items.length;
|
|
3443
|
+
} while (!isNavigable(items[next]));
|
|
3444
|
+
setActive(next);
|
|
3445
|
+
}
|
|
3446
|
+
} else if (isSpaceKey(key)) {
|
|
3447
|
+
const activeItem = items[active];
|
|
3448
|
+
if (activeItem && !Separator.isSeparator(activeItem)) {
|
|
3449
|
+
if (activeItem.disabled) {
|
|
3450
|
+
setError(theme.i18n.disabledError);
|
|
3451
|
+
} else {
|
|
3452
|
+
setError(undefined);
|
|
3453
|
+
setItems(items.map((choice, i) => i === active ? toggle(choice) : choice));
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
} else if (key.name === shortcuts.all) {
|
|
3457
|
+
const selectAll = items.some((choice) => isSelectable(choice) && !choice.checked);
|
|
3458
|
+
setItems(items.map(check(selectAll)));
|
|
3459
|
+
} else if (key.name === shortcuts.invert) {
|
|
3460
|
+
setItems(items.map(toggle));
|
|
3461
|
+
} else if (isNumberKey(key)) {
|
|
3462
|
+
const selectedIndex = Number(key.name) - 1;
|
|
3463
|
+
let selectableIndex = -1;
|
|
3464
|
+
const position = items.findIndex((item) => {
|
|
3465
|
+
if (Separator.isSeparator(item))
|
|
3466
|
+
return false;
|
|
3467
|
+
selectableIndex++;
|
|
3468
|
+
return selectableIndex === selectedIndex;
|
|
3469
|
+
});
|
|
3470
|
+
const selectedItem = items[position];
|
|
3471
|
+
if (selectedItem && isSelectable(selectedItem)) {
|
|
3472
|
+
setActive(position);
|
|
3473
|
+
setItems(items.map((choice, i) => i === position ? toggle(choice) : choice));
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
});
|
|
3477
|
+
const message = theme.style.message(config.message, status);
|
|
3478
|
+
let description;
|
|
3479
|
+
const page = usePagination({
|
|
3480
|
+
items,
|
|
3481
|
+
active,
|
|
3482
|
+
renderItem({ item, isActive }) {
|
|
3483
|
+
if (Separator.isSeparator(item)) {
|
|
3484
|
+
return ` ${item.separator}`;
|
|
3485
|
+
}
|
|
3486
|
+
const cursor = isActive ? theme.icon.cursor : " ";
|
|
3487
|
+
if (item.disabled) {
|
|
3488
|
+
const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
|
|
3489
|
+
const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
|
|
3490
|
+
return theme.style.disabled(`${cursor}${checkbox2} ${item.name} ${disabledLabel}`);
|
|
3491
|
+
}
|
|
3492
|
+
if (isActive) {
|
|
3493
|
+
description = item.description;
|
|
3494
|
+
}
|
|
3495
|
+
const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
|
|
3496
|
+
const name = item.checked ? item.checkedName : item.name;
|
|
3497
|
+
const color = isActive ? theme.style.highlight : (x) => x;
|
|
3498
|
+
return color(`${cursor}${checkbox} ${name}`);
|
|
3499
|
+
},
|
|
3500
|
+
pageSize,
|
|
3501
|
+
loop
|
|
3502
|
+
});
|
|
3503
|
+
if (status === "done") {
|
|
3504
|
+
const selection = items.filter(isChecked);
|
|
3505
|
+
const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
|
|
3506
|
+
return [prefix, message, answer].filter(Boolean).join(" ");
|
|
3507
|
+
}
|
|
3508
|
+
const keys = [
|
|
3509
|
+
["↑↓", "navigate"],
|
|
3510
|
+
["space", "select"]
|
|
3511
|
+
];
|
|
3512
|
+
if (shortcuts.all)
|
|
3513
|
+
keys.push([shortcuts.all, "all"]);
|
|
3514
|
+
if (shortcuts.invert)
|
|
3515
|
+
keys.push([shortcuts.invert, "invert"]);
|
|
3516
|
+
keys.push(["⏎", "submit"]);
|
|
3517
|
+
const helpLine = theme.style.keysHelpTip(keys);
|
|
3518
|
+
const lines = [
|
|
3519
|
+
[prefix, message].filter(Boolean).join(" "),
|
|
3520
|
+
page,
|
|
3521
|
+
" ",
|
|
3522
|
+
description ? theme.style.description(description) : "",
|
|
3523
|
+
errorMsg ? theme.style.error(errorMsg) : "",
|
|
3524
|
+
helpLine
|
|
3525
|
+
].filter(Boolean).join(`
|
|
3526
|
+
`).trimEnd();
|
|
3527
|
+
return `${lines}${cursorHide}`;
|
|
3528
|
+
});
|
|
3529
|
+
|
|
3530
|
+
// src/interactive-selection.ts
|
|
3531
|
+
var oxcChoiceKey = "toolchain.oxc";
|
|
3532
|
+
async function applyInteractiveSelection(options, prompter = terminalPrompter()) {
|
|
3533
|
+
if (!options.interactive)
|
|
3534
|
+
return options;
|
|
3535
|
+
const optional = moduleDistributionCatalog.filter(({ name }) => !requiredModuleNames.includes(name));
|
|
3536
|
+
const requested = await prompter.selectGenerationChoices({
|
|
3537
|
+
choices: [
|
|
3538
|
+
...optional.map(({ name, label, description, selection }) => ({
|
|
3539
|
+
value: name,
|
|
3540
|
+
label: `${label}(${sentenceWithoutStop(description)})`,
|
|
3541
|
+
hint: selection === "recommended" ? "推荐,默认选中" : "可选模块"
|
|
3542
|
+
})),
|
|
3543
|
+
{
|
|
3544
|
+
value: oxcChoiceKey,
|
|
3545
|
+
label: "Oxc 代码质量(Oxlint、Oxfmt 与提交格式化)",
|
|
3546
|
+
hint: "可选基础设施,默认选中"
|
|
3547
|
+
}
|
|
3548
|
+
],
|
|
3549
|
+
defaults: [...recommendedModuleNames, oxcChoiceKey]
|
|
3550
|
+
});
|
|
3551
|
+
const includeOxc = requested.includes(oxcChoiceKey);
|
|
3552
|
+
const projectLicense = await prompter.selectLicense();
|
|
3553
|
+
const licenseHolder = projectLicense === "MIT" ? (await prompter.text("MIT 许可证权利人名称:")).trim() : undefined;
|
|
3554
|
+
if (projectLicense === "MIT" && !licenseHolder) {
|
|
3555
|
+
throw new Error("选择 MIT 时必须填写许可证权利人名称");
|
|
3556
|
+
}
|
|
3557
|
+
const selectedModules = resolveModuleSelection({
|
|
3558
|
+
includeRecommended: false,
|
|
3559
|
+
requested: requested.filter((value) => value !== oxcChoiceKey)
|
|
3560
|
+
});
|
|
3561
|
+
return {
|
|
3562
|
+
...options,
|
|
3563
|
+
selectedModules,
|
|
3564
|
+
includeExample: selectedModules.includes("work-order"),
|
|
3565
|
+
includeOxc,
|
|
3566
|
+
projectLicense,
|
|
3567
|
+
licenseHolder,
|
|
3568
|
+
interactive: false
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
function terminalPrompter() {
|
|
3572
|
+
const question = async (message) => {
|
|
3573
|
+
const readline3 = createInterface2({ input: stdin, output: stdout });
|
|
3574
|
+
try {
|
|
3575
|
+
return await readline3.question(message);
|
|
3576
|
+
} finally {
|
|
3577
|
+
readline3.close();
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3580
|
+
return {
|
|
3581
|
+
async selectGenerationChoices({ choices, defaults }) {
|
|
3582
|
+
return dist_default4({
|
|
3583
|
+
message: "请选择要生成的平台模块和基础设施",
|
|
3584
|
+
choices: choices.map((choice) => ({
|
|
3585
|
+
value: choice.value,
|
|
3586
|
+
name: choice.label,
|
|
3587
|
+
description: choice.hint,
|
|
3588
|
+
checked: defaults.includes(choice.value)
|
|
3589
|
+
})),
|
|
3590
|
+
pageSize: choices.length,
|
|
3591
|
+
loop: false,
|
|
3592
|
+
shortcuts: { all: null, invert: null },
|
|
3593
|
+
theme: {
|
|
3594
|
+
style: {
|
|
3595
|
+
keysHelpTip: () => "↑/↓ 切换 · 空格 选中或取消 · 回车 提交"
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
});
|
|
3599
|
+
},
|
|
3600
|
+
async selectLicense() {
|
|
3601
|
+
const answer = (await question(`项目法律许可证:1. UNLICENSED(默认) 2. MIT 3. Apache-2.0
|
|
3602
|
+
> `)).trim();
|
|
3603
|
+
if (!answer || answer === "1")
|
|
3604
|
+
return "UNLICENSED";
|
|
3605
|
+
if (answer === "2")
|
|
3606
|
+
return "MIT";
|
|
3607
|
+
if (answer === "3")
|
|
3608
|
+
return "Apache-2.0";
|
|
3609
|
+
throw new Error("许可证选择包含无效编号");
|
|
3610
|
+
},
|
|
3611
|
+
text(message) {
|
|
3612
|
+
return question(message);
|
|
3613
|
+
}
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
function sentenceWithoutStop(value) {
|
|
3617
|
+
return value.replace(/[。.]$/, "");
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
// src/update/cli.ts
|
|
3621
|
+
import { rm as rm8 } from "node:fs/promises";
|
|
3622
|
+
import { resolve as resolve4 } from "node:path";
|
|
3623
|
+
|
|
3624
|
+
// src/update/doctor.ts
|
|
3625
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
3626
|
+
import { join as join9 } from "node:path";
|
|
3627
|
+
|
|
3628
|
+
// src/update/process.ts
|
|
3629
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
3630
|
+
async function captureProcess(command, args, cwd, inherit = false) {
|
|
3631
|
+
return await new Promise((resolve4, reject) => {
|
|
3632
|
+
const child = spawn3(command, args, {
|
|
3633
|
+
cwd,
|
|
3634
|
+
stdio: inherit ? "inherit" : ["ignore", "pipe", "pipe"]
|
|
3635
|
+
});
|
|
3636
|
+
let stdout2 = "";
|
|
3637
|
+
let stderr = "";
|
|
3638
|
+
child.stdout?.on("data", (chunk) => stdout2 += chunk.toString());
|
|
3639
|
+
child.stderr?.on("data", (chunk) => stderr += chunk.toString());
|
|
3640
|
+
child.once("error", reject);
|
|
3641
|
+
child.once("exit", (code) => resolve4({ code: code ?? 1, stdout: stdout2, stderr }));
|
|
3642
|
+
});
|
|
3643
|
+
}
|
|
3644
|
+
async function assertProcess(command, args, cwd, inherit = false) {
|
|
3645
|
+
const result = await captureProcess(command, args, cwd, inherit);
|
|
3646
|
+
if (result.code !== 0) {
|
|
3647
|
+
const detail = result.stderr.trim() || result.stdout.trim();
|
|
3648
|
+
throw new Error(`${command} ${args.join(" ")} 执行失败${detail ? `:${detail}` : ""}`);
|
|
3649
|
+
}
|
|
3650
|
+
return result;
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
// src/update/transaction.ts
|
|
3654
|
+
import {
|
|
3655
|
+
access as access2,
|
|
3656
|
+
copyFile as copyFile2,
|
|
3657
|
+
mkdir as mkdir5,
|
|
3658
|
+
readFile as readFile8,
|
|
3659
|
+
rm as rm5,
|
|
3660
|
+
writeFile as writeFile8
|
|
3661
|
+
} from "node:fs/promises";
|
|
3662
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
3663
|
+
var updateStatePath = ".windy/update-state.json";
|
|
3664
|
+
async function applyTransaction(plan) {
|
|
3665
|
+
if (plan.conflicts.length) {
|
|
3666
|
+
throw new Error(`更新存在 ${plan.conflicts.length} 个冲突,未写入任何项目文件`);
|
|
3667
|
+
}
|
|
3668
|
+
const changed = plan.files.filter(({ action }) => action === "create" || action === "update" || action === "delete");
|
|
3669
|
+
for (const file of changed)
|
|
3670
|
+
assertSafePath(file.path);
|
|
3671
|
+
const backupDirectory = join8(plan.projectDirectory, ".windy/backups", plan.id);
|
|
3672
|
+
const protectedPaths = new Set([
|
|
3673
|
+
...changed.map(({ path: path2 }) => path2),
|
|
3674
|
+
".windy/generation.json",
|
|
3675
|
+
".windy/files.json",
|
|
3676
|
+
"bun.lock"
|
|
3677
|
+
]);
|
|
3678
|
+
const backupEntries = [];
|
|
3679
|
+
for (const path2 of protectedPaths) {
|
|
3680
|
+
const source = join8(plan.projectDirectory, path2);
|
|
3681
|
+
const existed = await exists(source);
|
|
3682
|
+
backupEntries.push({ path: path2, existed });
|
|
3683
|
+
if (!existed)
|
|
3684
|
+
continue;
|
|
3685
|
+
const target = join8(backupDirectory, "files", path2);
|
|
3686
|
+
await mkdir5(dirname3(target), { recursive: true });
|
|
3687
|
+
await copyFile2(source, target);
|
|
3688
|
+
}
|
|
3689
|
+
const state = {
|
|
3690
|
+
schemaVersion: 1,
|
|
3691
|
+
updateId: plan.id,
|
|
3692
|
+
backupDirectory,
|
|
3693
|
+
entries: backupEntries
|
|
3694
|
+
};
|
|
3695
|
+
await writeState(plan.projectDirectory, state);
|
|
3696
|
+
try {
|
|
3697
|
+
for (const file of changed) {
|
|
3698
|
+
const target = join8(plan.projectDirectory, file.path);
|
|
3699
|
+
if (file.action === "delete") {
|
|
3700
|
+
await rm5(target, { force: true });
|
|
3701
|
+
continue;
|
|
3702
|
+
}
|
|
3703
|
+
if (!file.content)
|
|
3704
|
+
throw new Error(`更新计划缺少文件内容:${file.path}`);
|
|
3705
|
+
await mkdir5(dirname3(target), { recursive: true });
|
|
3706
|
+
await writeFile8(target, file.content);
|
|
3707
|
+
}
|
|
3708
|
+
} catch (error) {
|
|
3709
|
+
await restoreUpdateState(plan.projectDirectory, state);
|
|
3710
|
+
throw error;
|
|
3711
|
+
}
|
|
3712
|
+
return {
|
|
3713
|
+
plan,
|
|
3714
|
+
backupDirectory,
|
|
3715
|
+
backupEntries,
|
|
3716
|
+
changedFiles: changed.map(({ path: path2 }) => path2)
|
|
3717
|
+
};
|
|
3718
|
+
}
|
|
3719
|
+
async function readUpdateState(projectDirectory) {
|
|
3720
|
+
try {
|
|
3721
|
+
const parsed = JSON.parse(await readFile8(join8(projectDirectory, updateStatePath), "utf8"));
|
|
3722
|
+
if (!isUpdateState(parsed))
|
|
3723
|
+
throw new Error("更新恢复状态无效,请人工检查 .windy 目录");
|
|
3724
|
+
return parsed;
|
|
3725
|
+
} catch (error) {
|
|
3726
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
3727
|
+
return;
|
|
3728
|
+
throw error;
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
async function restoreUpdateState(projectDirectory, state) {
|
|
3732
|
+
for (const entry of state.entries) {
|
|
3733
|
+
assertSafePath(entry.path);
|
|
3734
|
+
const target = join8(projectDirectory, entry.path);
|
|
3735
|
+
if (!entry.existed) {
|
|
3736
|
+
await rm5(target, { force: true });
|
|
3737
|
+
continue;
|
|
3738
|
+
}
|
|
3739
|
+
const backup = join8(state.backupDirectory, "files", entry.path);
|
|
3740
|
+
await mkdir5(dirname3(target), { recursive: true });
|
|
3741
|
+
await copyFile2(backup, target);
|
|
3742
|
+
}
|
|
3743
|
+
await rm5(join8(projectDirectory, updateStatePath), { force: true });
|
|
3744
|
+
}
|
|
3745
|
+
async function clearUpdateState(projectDirectory) {
|
|
3746
|
+
await rm5(join8(projectDirectory, updateStatePath), { force: true });
|
|
3747
|
+
}
|
|
3748
|
+
async function writeState(projectDirectory, state) {
|
|
3749
|
+
const path2 = join8(projectDirectory, updateStatePath);
|
|
3750
|
+
await mkdir5(dirname3(path2), { recursive: true });
|
|
3751
|
+
await writeFile8(path2, `${JSON.stringify(state, null, 2)}
|
|
3752
|
+
`);
|
|
3753
|
+
}
|
|
3754
|
+
async function exists(path2) {
|
|
3755
|
+
try {
|
|
3756
|
+
await access2(path2);
|
|
3757
|
+
return true;
|
|
3758
|
+
} catch {
|
|
3759
|
+
return false;
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
function assertSafePath(path2) {
|
|
3763
|
+
if (!path2 || path2.startsWith("/") || path2.split("/").includes("..")) {
|
|
3764
|
+
throw new Error(`更新计划包含不安全路径:${path2}`);
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
function isUpdateState(value) {
|
|
3768
|
+
if (!value || typeof value !== "object")
|
|
3769
|
+
return false;
|
|
3770
|
+
const state = value;
|
|
3771
|
+
return state.schemaVersion === 1 && typeof state.updateId === "string" && typeof state.backupDirectory === "string" && Array.isArray(state.entries);
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
// src/update/doctor.ts
|
|
3775
|
+
var oxcPaths = new Set([
|
|
3776
|
+
".oxfmtrc.json",
|
|
3777
|
+
".oxlintrc.json",
|
|
3778
|
+
".husky/pre-commit"
|
|
3779
|
+
]);
|
|
3780
|
+
async function diagnoseProject(projectDirectory, repair = false) {
|
|
3781
|
+
const pending = await readUpdateState(projectDirectory);
|
|
3782
|
+
let repaired = false;
|
|
3783
|
+
if (pending && repair) {
|
|
3784
|
+
await restoreUpdateState(projectDirectory, pending);
|
|
3785
|
+
repaired = true;
|
|
3786
|
+
}
|
|
3787
|
+
const [provenance, manifest, git] = await Promise.all([
|
|
3788
|
+
readGenerationProvenance(projectDirectory),
|
|
3789
|
+
readManagedFilesManifest(projectDirectory),
|
|
3790
|
+
captureProcess("git", ["status", "--porcelain"], projectDirectory)
|
|
3791
|
+
]);
|
|
3792
|
+
const modifiedManagedFiles = [];
|
|
3793
|
+
const missingManagedFiles = [];
|
|
3794
|
+
let detachedToolchain = false;
|
|
3795
|
+
for (const file of manifest.files) {
|
|
3796
|
+
if (file.ownership === "project-owned")
|
|
3797
|
+
continue;
|
|
3798
|
+
try {
|
|
3799
|
+
const changed = hashContent(await readFile9(join9(projectDirectory, file.path))) !== file.hash;
|
|
3800
|
+
if (!changed)
|
|
3801
|
+
continue;
|
|
3802
|
+
modifiedManagedFiles.push(file.path);
|
|
3803
|
+
if (oxcPaths.has(file.path))
|
|
3804
|
+
detachedToolchain = true;
|
|
3805
|
+
} catch (error) {
|
|
3806
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
3807
|
+
missingManagedFiles.push(file.path);
|
|
3808
|
+
if (oxcPaths.has(file.path))
|
|
3809
|
+
detachedToolchain = true;
|
|
3810
|
+
continue;
|
|
3811
|
+
}
|
|
3812
|
+
throw error;
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
return {
|
|
3816
|
+
version: provenance.windyVersion,
|
|
3817
|
+
gitClean: git.code === 0 && !git.stdout.trim(),
|
|
3818
|
+
modifiedManagedFiles,
|
|
3819
|
+
missingManagedFiles,
|
|
3820
|
+
detachedToolchain,
|
|
3821
|
+
recoveryRequired: Boolean(pending) && !repaired,
|
|
3822
|
+
repaired
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
// src/update/recipes.ts
|
|
3827
|
+
var recipes = [
|
|
3828
|
+
{ id: "starter-0.1.1-to-0.1.2", from: "0.1.1", to: "0.1.2" },
|
|
3829
|
+
{ id: "starter-0.1.2-to-0.2.0", from: "0.1.2", to: "0.2.0" }
|
|
3830
|
+
];
|
|
3831
|
+
function resolveRecipeChain(sourceVersion, targetVersion) {
|
|
3832
|
+
if (sourceVersion === targetVersion)
|
|
3833
|
+
return [];
|
|
3834
|
+
const chain = [];
|
|
3835
|
+
let current = sourceVersion;
|
|
3836
|
+
const visited = new Set;
|
|
3837
|
+
while (current !== targetVersion) {
|
|
3838
|
+
if (visited.has(current))
|
|
3839
|
+
break;
|
|
3840
|
+
visited.add(current);
|
|
3841
|
+
const recipe = recipes.find(({ from }) => from === current);
|
|
3842
|
+
if (!recipe)
|
|
3843
|
+
break;
|
|
3844
|
+
chain.push(recipe);
|
|
3845
|
+
current = recipe.to;
|
|
3846
|
+
}
|
|
3847
|
+
if (current !== targetVersion) {
|
|
3848
|
+
throw new Error(`没有从 ${sourceVersion} 到 ${targetVersion} 的连续更新 recipe,禁止跳级`);
|
|
3849
|
+
}
|
|
3850
|
+
return chain;
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
// src/update/updater.ts
|
|
3854
|
+
import { mkdir as mkdir7, readFile as readFile12, rm as rm7, writeFile as writeFile10 } from "node:fs/promises";
|
|
3855
|
+
import { dirname as dirname4, join as join13 } from "node:path";
|
|
3856
|
+
|
|
3857
|
+
// src/update/artifacts.ts
|
|
3858
|
+
import { mkdir as mkdir6, mkdtemp, readFile as readFile10 } from "node:fs/promises";
|
|
3859
|
+
import { tmpdir } from "node:os";
|
|
3860
|
+
import { basename as basename3, join as join10 } from "node:path";
|
|
3861
|
+
class PublishedArtifactProvider {
|
|
3862
|
+
async prepare(projectDirectory, provenance) {
|
|
3863
|
+
const temporaryRoot = await mkdtemp(join10(tmpdir(), "windy-update-"));
|
|
3864
|
+
const projectName = await readProjectName2(projectDirectory);
|
|
3865
|
+
const baseDirectory = join10(temporaryRoot, "base", projectName);
|
|
3866
|
+
const targetDirectory = join10(temporaryRoot, "target", projectName);
|
|
3867
|
+
await generatePublishedBase(temporaryRoot, projectName, provenance, baseDirectory);
|
|
3868
|
+
await generateProject({
|
|
3869
|
+
sourceRoot: await resolveTemplateRoot(),
|
|
3870
|
+
targetDirectory,
|
|
3871
|
+
projectName,
|
|
3872
|
+
selectedModules: provenance.modules.map(({ name }) => name),
|
|
3873
|
+
includeOxc: provenance.choices.includeOxc,
|
|
3874
|
+
projectLicense: provenance.legalLicense,
|
|
3875
|
+
licenseHolder: provenance.legalLicense === "MIT" ? "Windy project owner" : undefined,
|
|
3876
|
+
profile: provenance.profile
|
|
3877
|
+
});
|
|
3878
|
+
return { temporaryRoot, baseDirectory, targetDirectory };
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
async function generatePublishedBase(temporaryRoot, projectName, provenance, expectedDirectory) {
|
|
3882
|
+
const parent = join10(temporaryRoot, "base");
|
|
3883
|
+
await mkdir6(parent, { recursive: true });
|
|
3884
|
+
const args = [
|
|
3885
|
+
"--bun",
|
|
3886
|
+
`create-windy@${provenance.generatorVersion}`,
|
|
3887
|
+
projectName,
|
|
3888
|
+
"--yes",
|
|
3889
|
+
"--skip-install",
|
|
3890
|
+
"--no-git",
|
|
3891
|
+
"--no-optional-modules",
|
|
3892
|
+
"--license",
|
|
3893
|
+
provenance.legalLicense
|
|
3894
|
+
];
|
|
3895
|
+
if (!provenance.choices.includeOxc)
|
|
3896
|
+
args.push("--no-oxc");
|
|
3897
|
+
if (provenance.legalLicense === "MIT") {
|
|
3898
|
+
args.push("--license-holder", "Windy project owner");
|
|
3899
|
+
}
|
|
3900
|
+
for (const { name } of provenance.modules) {
|
|
3901
|
+
if (!requiredModuleNames.includes(name)) {
|
|
3902
|
+
args.push("--module", name);
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
await assertProcess("bunx", args, parent);
|
|
3906
|
+
if (join10(parent, projectName) !== expectedDirectory) {
|
|
3907
|
+
throw new Error("历史 artifact 生成目录异常");
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
async function readProjectName2(projectDirectory) {
|
|
3911
|
+
const source = JSON.parse(await readFile10(join10(projectDirectory, "package.json"), "utf8"));
|
|
3912
|
+
return source.name || basename3(projectDirectory);
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
// src/update/planner.ts
|
|
3916
|
+
import { access as access3, readFile as readFile11 } from "node:fs/promises";
|
|
3917
|
+
import { join as join12 } from "node:path";
|
|
3918
|
+
|
|
3919
|
+
// src/update/merge.ts
|
|
3920
|
+
import { mkdtemp as mkdtemp2, rm as rm6, writeFile as writeFile9 } from "node:fs/promises";
|
|
3921
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
3922
|
+
import { join as join11 } from "node:path";
|
|
3923
|
+
async function mergeTextFile(base, ours, theirs) {
|
|
3924
|
+
const root = await mkdtemp2(join11(tmpdir2(), "windy-merge-"));
|
|
3925
|
+
try {
|
|
3926
|
+
const oursPath = join11(root, "ours");
|
|
3927
|
+
const basePath = join11(root, "base");
|
|
3928
|
+
const theirsPath = join11(root, "theirs");
|
|
3929
|
+
await Promise.all([
|
|
3930
|
+
writeFile9(oursPath, ours),
|
|
3931
|
+
writeFile9(basePath, base),
|
|
3932
|
+
writeFile9(theirsPath, theirs)
|
|
3933
|
+
]);
|
|
3934
|
+
const result = await captureProcess("git", ["merge-file", "-p", "--diff3", oursPath, basePath, theirsPath], root);
|
|
3935
|
+
if (result.code === 0) {
|
|
3936
|
+
return { content: new TextEncoder().encode(result.stdout) };
|
|
3937
|
+
}
|
|
3938
|
+
if (result.code === 1)
|
|
3939
|
+
return { conflict: "三方合并存在重叠修改" };
|
|
3940
|
+
return { conflict: result.stderr.trim() || "git merge-file 执行失败" };
|
|
3941
|
+
} finally {
|
|
3942
|
+
await rm6(root, { recursive: true, force: true });
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
// src/update/package-merge.ts
|
|
3947
|
+
function mergePackageJson(base, ours, theirs) {
|
|
3948
|
+
const conflicts = [];
|
|
3949
|
+
const merged = mergeValue(JSON.parse(new TextDecoder().decode(base)), JSON.parse(new TextDecoder().decode(ours)), JSON.parse(new TextDecoder().decode(theirs)), "package.json", conflicts);
|
|
3950
|
+
return conflicts.length ? { conflicts } : {
|
|
3951
|
+
conflicts,
|
|
3952
|
+
content: new TextEncoder().encode(`${JSON.stringify(merged, null, 2)}
|
|
3953
|
+
`)
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
function mergeValue(base, ours, theirs, path2, conflicts) {
|
|
3957
|
+
if (equal(ours, theirs))
|
|
3958
|
+
return ours;
|
|
3959
|
+
if (equal(ours, base))
|
|
3960
|
+
return theirs;
|
|
3961
|
+
if (equal(theirs, base))
|
|
3962
|
+
return ours;
|
|
3963
|
+
if (isObject(base) && isObject(ours) && isObject(theirs)) {
|
|
3964
|
+
const output = {};
|
|
3965
|
+
const keys = new Set([
|
|
3966
|
+
...Object.keys(base),
|
|
3967
|
+
...Object.keys(ours),
|
|
3968
|
+
...Object.keys(theirs)
|
|
3969
|
+
]);
|
|
3970
|
+
for (const key of keys) {
|
|
3971
|
+
const value = mergeValue(base[key], ours[key], theirs[key], `${path2}.${key}`, conflicts);
|
|
3972
|
+
if (value !== undefined)
|
|
3973
|
+
output[key] = value;
|
|
3974
|
+
}
|
|
3975
|
+
return output;
|
|
3976
|
+
}
|
|
3977
|
+
conflicts.push(path2);
|
|
3978
|
+
return ours;
|
|
3979
|
+
}
|
|
3980
|
+
function equal(left, right) {
|
|
3981
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
3982
|
+
}
|
|
3983
|
+
function isObject(value) {
|
|
3984
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3987
|
+
// src/update/planner.ts
|
|
3988
|
+
var metadataPaths = new Set([
|
|
3989
|
+
".windy/generation.json",
|
|
3990
|
+
".windy/files.json",
|
|
3991
|
+
".windy-template.json"
|
|
3992
|
+
]);
|
|
3993
|
+
var oxcManagedPaths = new Set([
|
|
3994
|
+
".oxfmtrc.json",
|
|
3995
|
+
".oxlintrc.json",
|
|
3996
|
+
".husky/pre-commit"
|
|
3997
|
+
]);
|
|
3998
|
+
async function createUpdatePlan(request, artifacts) {
|
|
3999
|
+
await assertGitClean(request.projectDirectory);
|
|
4000
|
+
await assertNoPendingUpdate(request.projectDirectory);
|
|
4001
|
+
const provenance = await readGenerationProvenance(request.projectDirectory);
|
|
4002
|
+
const artifactSet = await artifacts.prepare(request.projectDirectory, provenance);
|
|
4003
|
+
const targetProvenance = await readGenerationProvenance(artifactSet.targetDirectory);
|
|
4004
|
+
const targetVersion = request.targetVersion || targetProvenance.generatorVersion;
|
|
4005
|
+
if (targetVersion !== targetProvenance.generatorVersion) {
|
|
4006
|
+
throw new Error(`当前 CLI 只能更新到 ${targetProvenance.generatorVersion},请运行 create-windy@${targetVersion}`);
|
|
4007
|
+
}
|
|
4008
|
+
const recipes2 = resolveRecipeChain(provenance.windyVersion, targetVersion);
|
|
4009
|
+
const [sourceManifest, targetManifest] = await Promise.all([
|
|
4010
|
+
readManagedFilesManifest(request.projectDirectory),
|
|
4011
|
+
readManagedFilesManifest(artifactSet.targetDirectory)
|
|
4012
|
+
]);
|
|
4013
|
+
targetProvenance.appliedRecipes = [
|
|
4014
|
+
...provenance.appliedRecipes,
|
|
4015
|
+
...recipes2.map(({ id }) => id)
|
|
4016
|
+
];
|
|
4017
|
+
const sourceEntries = new Map(sourceManifest.files.map((item) => [item.path, item]));
|
|
4018
|
+
const targetEntries = new Map(targetManifest.files.map((item) => [item.path, item]));
|
|
4019
|
+
const paths = new Set([...sourceEntries.keys(), ...targetEntries.keys()]);
|
|
4020
|
+
const files = [];
|
|
4021
|
+
let detachedToolchain = false;
|
|
4022
|
+
for (const path2 of [...paths].sort()) {
|
|
4023
|
+
if (metadataPaths.has(path2))
|
|
4024
|
+
continue;
|
|
4025
|
+
const file = await planFile(path2, request.projectDirectory, artifactSet.baseDirectory, artifactSet.targetDirectory, sourceEntries.get(path2), targetEntries.get(path2));
|
|
4026
|
+
if (oxcManagedPaths.has(path2) && file.reason === "客户已修改") {
|
|
4027
|
+
file.action = "preserve";
|
|
4028
|
+
delete file.conflict;
|
|
4029
|
+
detachedToolchain = true;
|
|
4030
|
+
}
|
|
4031
|
+
files.push(file);
|
|
4032
|
+
}
|
|
4033
|
+
const conflicts = files.flatMap((file) => file.conflict ? [`${file.path}:${file.conflict}`] : []);
|
|
4034
|
+
return {
|
|
4035
|
+
id: createUpdateId(provenance.windyVersion, targetVersion),
|
|
4036
|
+
projectDirectory: request.projectDirectory,
|
|
4037
|
+
sourceVersion: provenance.windyVersion,
|
|
4038
|
+
targetVersion,
|
|
4039
|
+
recipes: recipes2.map(({ id }) => id),
|
|
4040
|
+
files,
|
|
4041
|
+
conflicts,
|
|
4042
|
+
detachedToolchain,
|
|
4043
|
+
targetProvenance,
|
|
4044
|
+
targetManifest,
|
|
4045
|
+
temporaryRoot: artifactSet.temporaryRoot
|
|
4046
|
+
};
|
|
4047
|
+
}
|
|
4048
|
+
async function planFile(path2, project, baseRoot, targetRoot, source, target) {
|
|
4049
|
+
const [ours, base, theirs] = await Promise.all([
|
|
4050
|
+
readOptional(join12(project, path2)),
|
|
4051
|
+
readOptional(join12(baseRoot, path2)),
|
|
4052
|
+
readOptional(join12(targetRoot, path2))
|
|
4053
|
+
]);
|
|
4054
|
+
const ownership = target?.ownership || source?.ownership || classifyOwnership(path2);
|
|
4055
|
+
if (path2.startsWith("packages/database/drizzle/") && path2.endsWith(".sql")) {
|
|
4056
|
+
if (source && !target)
|
|
4057
|
+
return conflict(path2, ownership, "已发布 migration 不可删除");
|
|
4058
|
+
if (source && target && source.hash !== target.hash) {
|
|
4059
|
+
return conflict(path2, ownership, "已发布 migration 不可改写,只能追加新文件");
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
if (ownership === "project-owned") {
|
|
4063
|
+
if (!ours && theirs)
|
|
4064
|
+
return ready(path2, ownership, "create", "新增项目扩展点", theirs);
|
|
4065
|
+
return ready(path2, ownership, "preserve", "项目拥有,Windy 不覆盖");
|
|
4066
|
+
}
|
|
4067
|
+
if (!source && target) {
|
|
4068
|
+
if (!ours)
|
|
4069
|
+
return ready(path2, ownership, "create", "目标版本新增", theirs);
|
|
4070
|
+
if (theirs && hashContent(ours) === target.hash) {
|
|
4071
|
+
return ready(path2, ownership, "preserve", "已包含目标内容");
|
|
4072
|
+
}
|
|
4073
|
+
return conflict(path2, ownership, "目标版本新增路径已被项目占用");
|
|
4074
|
+
}
|
|
4075
|
+
if (source && !target) {
|
|
4076
|
+
if (!ours)
|
|
4077
|
+
return ready(path2, ownership, "preserve", "项目已删除");
|
|
4078
|
+
if (hashContent(ours) === source.hash) {
|
|
4079
|
+
return ready(path2, ownership, "delete", "目标版本删除");
|
|
4080
|
+
}
|
|
4081
|
+
return conflict(path2, ownership, "客户修改后 Windy 又删除");
|
|
4082
|
+
}
|
|
4083
|
+
if (!source || !target || !ours || !theirs) {
|
|
4084
|
+
return conflict(path2, ownership, "文件状态不完整,不能安全推断");
|
|
4085
|
+
}
|
|
4086
|
+
const oursChanged = hashContent(ours) !== source.hash;
|
|
4087
|
+
const targetChanged = target.hash !== source.hash;
|
|
4088
|
+
if (!oursChanged && !targetChanged)
|
|
4089
|
+
return ready(path2, ownership, "preserve", "双方未修改");
|
|
4090
|
+
if (!oursChanged)
|
|
4091
|
+
return ready(path2, ownership, "update", "仅 Windy 修改", theirs);
|
|
4092
|
+
if (!targetChanged)
|
|
4093
|
+
return ready(path2, ownership, "preserve", "客户已修改");
|
|
4094
|
+
if (hashContent(ours) === target.hash)
|
|
4095
|
+
return ready(path2, ownership, "preserve", "已包含目标内容");
|
|
4096
|
+
if (path2 === "package.json" && base) {
|
|
4097
|
+
const merged = mergePackageJson(base, ours, theirs);
|
|
4098
|
+
return merged.content ? ready(path2, ownership, "update", "package.json 字段级合并", merged.content) : conflict(path2, ownership, `字段冲突:${merged.conflicts.join("、")}`);
|
|
4099
|
+
}
|
|
4100
|
+
if (ownership === "shared-scaffold" && base) {
|
|
4101
|
+
const merged = await mergeTextFile(base, ours, theirs);
|
|
4102
|
+
return merged.content ? ready(path2, ownership, "update", "三方合并", merged.content) : conflict(path2, ownership, merged.conflict || "三方合并失败");
|
|
4103
|
+
}
|
|
4104
|
+
return conflict(path2, ownership, "客户和 Windy 均已修改");
|
|
4105
|
+
}
|
|
4106
|
+
function ready(path2, ownership, action, reason, content) {
|
|
4107
|
+
return { path: path2, ownership, action, reason, content };
|
|
4108
|
+
}
|
|
4109
|
+
function conflict(path2, ownership, reason) {
|
|
4110
|
+
return {
|
|
4111
|
+
path: path2,
|
|
4112
|
+
ownership,
|
|
4113
|
+
action: "preserve",
|
|
4114
|
+
reason: "客户已修改",
|
|
4115
|
+
conflict: reason
|
|
4116
|
+
};
|
|
4117
|
+
}
|
|
4118
|
+
async function readOptional(path2) {
|
|
4119
|
+
try {
|
|
4120
|
+
return await readFile11(path2);
|
|
4121
|
+
} catch (error) {
|
|
4122
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
4123
|
+
return;
|
|
4124
|
+
throw error;
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
async function assertGitClean(projectDirectory) {
|
|
4128
|
+
const result = await captureProcess("git", ["status", "--porcelain"], projectDirectory);
|
|
4129
|
+
if (result.code !== 0)
|
|
4130
|
+
throw new Error("更新要求当前目录是有效的 Git 仓库");
|
|
4131
|
+
if (result.stdout.trim())
|
|
4132
|
+
throw new Error("更新前请先提交或暂存之外妥善处理 Git 工作区改动");
|
|
4133
|
+
}
|
|
4134
|
+
async function assertNoPendingUpdate(projectDirectory) {
|
|
4135
|
+
try {
|
|
4136
|
+
await access3(join12(projectDirectory, ".windy/update-state.json"));
|
|
4137
|
+
throw new Error("检测到未完成更新,请先运行 bun run windy doctor --repair");
|
|
4138
|
+
} catch (error) {
|
|
4139
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
4140
|
+
return;
|
|
4141
|
+
throw error;
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
function createUpdateId(from, to) {
|
|
4145
|
+
return `${new Date().toISOString().replaceAll(/[:.]/g, "-")}-${from}-to-${to}`;
|
|
4146
|
+
}
|
|
4147
|
+
|
|
4148
|
+
// src/update/updater.ts
|
|
4149
|
+
class DefaultStarterUpdater {
|
|
4150
|
+
#artifacts;
|
|
4151
|
+
#execute;
|
|
4152
|
+
constructor(dependencies = {}) {
|
|
4153
|
+
this.#artifacts = dependencies.artifacts ?? new PublishedArtifactProvider;
|
|
4154
|
+
this.#execute = dependencies.execute ?? runCommand;
|
|
4155
|
+
}
|
|
4156
|
+
async plan(input) {
|
|
4157
|
+
return await createUpdatePlan(input, this.#artifacts);
|
|
4158
|
+
}
|
|
4159
|
+
async apply(plan) {
|
|
4160
|
+
try {
|
|
4161
|
+
return await applyTransaction(plan);
|
|
4162
|
+
} catch (error) {
|
|
4163
|
+
await rm7(plan.temporaryRoot, { recursive: true, force: true });
|
|
4164
|
+
throw error;
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
async verify(result) {
|
|
4168
|
+
const steps = [];
|
|
4169
|
+
try {
|
|
4170
|
+
await this.#execute("bun", ["install"], result.plan.projectDirectory);
|
|
4171
|
+
steps.push({ name: "bun install", status: "passed" });
|
|
4172
|
+
const packageJson = JSON.parse(await readFile12(join13(result.plan.projectDirectory, "package.json"), "utf8"));
|
|
4173
|
+
for (const name of ["lint", "typecheck", "test", "build"]) {
|
|
4174
|
+
if (!packageJson.scripts?.[name]) {
|
|
4175
|
+
steps.push({ name: `bun run ${name}`, status: "skipped" });
|
|
4176
|
+
continue;
|
|
4177
|
+
}
|
|
4178
|
+
await this.#execute("bun", ["run", name], result.plan.projectDirectory);
|
|
4179
|
+
steps.push({ name: `bun run ${name}`, status: "passed" });
|
|
4180
|
+
}
|
|
4181
|
+
await finalize(result);
|
|
4182
|
+
const reportPath = `.windy/reports/${result.plan.id}.md`;
|
|
4183
|
+
await writeReport(result, steps, reportPath);
|
|
4184
|
+
await clearUpdateState(result.plan.projectDirectory);
|
|
4185
|
+
await rm7(result.plan.temporaryRoot, { recursive: true, force: true });
|
|
4186
|
+
return { updateId: result.plan.id, status: "passed", steps, reportPath };
|
|
4187
|
+
} catch (error) {
|
|
4188
|
+
await restoreUpdateState(result.plan.projectDirectory, {
|
|
4189
|
+
schemaVersion: 1,
|
|
4190
|
+
updateId: result.plan.id,
|
|
4191
|
+
backupDirectory: result.backupDirectory,
|
|
4192
|
+
entries: result.backupEntries
|
|
4193
|
+
});
|
|
4194
|
+
await rm7(result.plan.temporaryRoot, { recursive: true, force: true });
|
|
4195
|
+
throw new Error(`更新验证失败,项目已回滚到 ${result.plan.sourceVersion}:${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
async function finalize(result) {
|
|
4200
|
+
const root = result.plan.projectDirectory;
|
|
4201
|
+
await writeFile10(join13(root, ".windy/generation.json"), `${JSON.stringify(result.plan.targetProvenance, null, 2)}
|
|
4202
|
+
`);
|
|
4203
|
+
await writeFile10(join13(root, ".windy/files.json"), `${JSON.stringify(result.plan.targetManifest, null, 2)}
|
|
4204
|
+
`);
|
|
4205
|
+
}
|
|
4206
|
+
async function writeReport(result, steps, relativePath2) {
|
|
4207
|
+
const path2 = join13(result.plan.projectDirectory, relativePath2);
|
|
4208
|
+
await mkdir7(dirname4(path2), { recursive: true });
|
|
4209
|
+
const lines = [
|
|
4210
|
+
`# Windy 更新报告 ${result.plan.id}`,
|
|
4211
|
+
"",
|
|
4212
|
+
`- 来源版本:${result.plan.sourceVersion}`,
|
|
4213
|
+
`- 目标版本:${result.plan.targetVersion}`,
|
|
4214
|
+
`- Recipe:${result.plan.recipes.join("、") || "无"}`,
|
|
4215
|
+
`- 修改文件:${result.changedFiles.length}`,
|
|
4216
|
+
`- Oxc 代管:${result.plan.detachedToolchain ? "已检测到客户接管并保留" : "保持"}`,
|
|
4217
|
+
"",
|
|
4218
|
+
"## 验证",
|
|
4219
|
+
"",
|
|
4220
|
+
...steps.map(({ name, status }) => `- ${name}:${status}`),
|
|
4221
|
+
"",
|
|
4222
|
+
`备份保留在 \`.windy/backups/${result.plan.id}\`,确认稳定后可人工删除。`,
|
|
4223
|
+
""
|
|
4224
|
+
];
|
|
4225
|
+
await writeFile10(path2, lines.join(`
|
|
4226
|
+
`));
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4229
|
+
// src/update/cli.ts
|
|
4230
|
+
async function runLifecycleCommand(args) {
|
|
4231
|
+
const command = args[0];
|
|
4232
|
+
if (command !== "doctor" && command !== "update")
|
|
4233
|
+
return false;
|
|
4234
|
+
const projectDirectory = resolve4(readValue2(args, "--cwd") || process.cwd());
|
|
4235
|
+
if (command === "doctor") {
|
|
4236
|
+
const report = await diagnoseProject(projectDirectory, args.includes("--repair"));
|
|
4237
|
+
printDoctor(report);
|
|
4238
|
+
if (report.recoveryRequired)
|
|
4239
|
+
process.exitCode = 1;
|
|
4240
|
+
return true;
|
|
4241
|
+
}
|
|
4242
|
+
await runUpdate(args, projectDirectory);
|
|
4243
|
+
return true;
|
|
4244
|
+
}
|
|
4245
|
+
async function runUpdate(args, projectDirectory) {
|
|
4246
|
+
const template = await readTemplateMetadata(await resolveTemplateRoot());
|
|
4247
|
+
const requested = readValue2(args, "--to");
|
|
4248
|
+
const registryVersion = requested ? undefined : await readRegistryLatest();
|
|
4249
|
+
if (registryVersion && compareVersions2(registryVersion, template.generatorVersion) > 0 && !args.includes("--no-delegate")) {
|
|
4250
|
+
if (args.includes("--check")) {
|
|
4251
|
+
const current2 = await readGenerationProvenance(projectDirectory);
|
|
4252
|
+
console.log(`可更新:${current2.windyVersion} → ${registryVersion}(请使用新版 CLI 生成完整计划)`);
|
|
4253
|
+
return;
|
|
4254
|
+
}
|
|
4255
|
+
await delegateUpdate(args, registryVersion, projectDirectory);
|
|
4256
|
+
return;
|
|
4257
|
+
}
|
|
4258
|
+
if (requested && requested !== template.generatorVersion && !args.includes("--no-delegate")) {
|
|
4259
|
+
await delegateUpdate(args, requested, projectDirectory);
|
|
4260
|
+
return;
|
|
4261
|
+
}
|
|
4262
|
+
const current = await readGenerationProvenance(projectDirectory);
|
|
4263
|
+
const target = requested || template.generatorVersion;
|
|
4264
|
+
const recipes2 = resolveRecipeChain(current.windyVersion, target);
|
|
4265
|
+
if (args.includes("--check")) {
|
|
4266
|
+
console.log(recipes2.length ? `可更新:${current.windyVersion} → ${target}(${recipes2.length} 个连续 recipe)` : `已是当前 CLI 支持的最新版本 ${current.windyVersion}`);
|
|
4267
|
+
return;
|
|
4268
|
+
}
|
|
4269
|
+
if (!recipes2.length) {
|
|
4270
|
+
console.log(`项目已经是 ${target},无需更新`);
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
4273
|
+
const updater = new DefaultStarterUpdater;
|
|
4274
|
+
const plan = await updater.plan({ projectDirectory, targetVersion: target });
|
|
4275
|
+
printPlan(plan);
|
|
4276
|
+
if (args.includes("--dry-run")) {
|
|
4277
|
+
await rm8(plan.temporaryRoot, { recursive: true, force: true });
|
|
4278
|
+
console.log(`
|
|
4279
|
+
演练完成:未写入项目文件`);
|
|
4280
|
+
return;
|
|
4281
|
+
}
|
|
4282
|
+
if (plan.conflicts.length) {
|
|
4283
|
+
await rm8(plan.temporaryRoot, { recursive: true, force: true });
|
|
4284
|
+
throw new Error("存在更新冲突;请按上方清单处理后重新运行,项目未被修改");
|
|
4285
|
+
}
|
|
4286
|
+
const result = await updater.apply(plan);
|
|
4287
|
+
const report = await updater.verify(result);
|
|
4288
|
+
console.log(`
|
|
4289
|
+
更新完成:${plan.sourceVersion} → ${plan.targetVersion}`);
|
|
4290
|
+
console.log(`报告:${report.reportPath}`);
|
|
4291
|
+
}
|
|
4292
|
+
async function delegateUpdate(args, target, projectDirectory) {
|
|
4293
|
+
const forwarded = args.slice(1).filter((item, index, all) => {
|
|
4294
|
+
if (item === "--cwd" || item === "--to")
|
|
4295
|
+
return false;
|
|
4296
|
+
return all[index - 1] !== "--cwd" && all[index - 1] !== "--to";
|
|
4297
|
+
});
|
|
4298
|
+
await assertProcess("bunx", [
|
|
4299
|
+
"--bun",
|
|
4300
|
+
`create-windy@${target}`,
|
|
4301
|
+
"update",
|
|
4302
|
+
"--cwd",
|
|
4303
|
+
projectDirectory,
|
|
4304
|
+
"--to",
|
|
4305
|
+
target,
|
|
4306
|
+
"--no-delegate",
|
|
4307
|
+
...forwarded
|
|
4308
|
+
], projectDirectory, true);
|
|
4309
|
+
}
|
|
4310
|
+
function printPlan(plan) {
|
|
4311
|
+
const changed = plan.files.filter(({ action }) => action !== "preserve");
|
|
4312
|
+
console.log(`Windy 更新计划:${plan.sourceVersion} → ${plan.targetVersion}`);
|
|
4313
|
+
console.log(`Recipe:${plan.recipes.join("、")}`);
|
|
4314
|
+
console.log(`计划改动:${changed.length} 个文件;冲突:${plan.conflicts.length}`);
|
|
4315
|
+
for (const file of changed)
|
|
4316
|
+
console.log(` ${file.action.padEnd(6)} ${file.path}(${file.reason})`);
|
|
4317
|
+
for (const conflict2 of plan.conflicts)
|
|
4318
|
+
console.log(` conflict ${conflict2}`);
|
|
4319
|
+
if (plan.detachedToolchain)
|
|
4320
|
+
console.log("Oxc:检测到客户接管,相关配置保持不变");
|
|
4321
|
+
}
|
|
4322
|
+
function printDoctor(report) {
|
|
4323
|
+
console.log(`Windy Project Lock:${report.version}`);
|
|
4324
|
+
console.log(`Git 工作区:${report.gitClean ? "干净" : "有改动"}`);
|
|
4325
|
+
console.log(`客户修改的受管文件:${report.modifiedManagedFiles.length}`);
|
|
4326
|
+
console.log(`缺失的受管文件:${report.missingManagedFiles.length}`);
|
|
4327
|
+
console.log(`Oxc:${report.detachedToolchain ? "客户已接管" : "仍由 Windy 代管或未启用"}`);
|
|
4328
|
+
if (report.recoveryRequired)
|
|
4329
|
+
console.log("恢复:检测到中断更新,请运行 doctor --repair");
|
|
4330
|
+
if (report.repaired)
|
|
4331
|
+
console.log("恢复:已从事务备份回滚");
|
|
4332
|
+
}
|
|
4333
|
+
function readValue2(args, flag) {
|
|
4334
|
+
const index = args.indexOf(flag);
|
|
4335
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
4336
|
+
}
|
|
4337
|
+
async function readRegistryLatest() {
|
|
4338
|
+
try {
|
|
4339
|
+
const response = await fetch("https://registry.npmjs.org/create-windy/latest");
|
|
4340
|
+
if (!response.ok)
|
|
4341
|
+
return;
|
|
4342
|
+
const payload = await response.json();
|
|
4343
|
+
return payload.version;
|
|
4344
|
+
} catch {
|
|
4345
|
+
return;
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
function compareVersions2(left, right) {
|
|
4349
|
+
const leftParts = left.split(".").map(Number);
|
|
4350
|
+
const rightParts = right.split(".").map(Number);
|
|
4351
|
+
for (let index = 0;index < 3; index += 1) {
|
|
4352
|
+
const difference = (leftParts[index] || 0) - (rightParts[index] || 0);
|
|
4353
|
+
if (difference)
|
|
4354
|
+
return difference;
|
|
4355
|
+
}
|
|
4356
|
+
return 0;
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
// src/cli.ts
|
|
4360
|
+
async function main() {
|
|
4361
|
+
const args = process.argv.slice(2);
|
|
4362
|
+
if (await runLifecycleCommand(args))
|
|
4363
|
+
return;
|
|
4364
|
+
const options = parseArguments(args);
|
|
1671
4365
|
if (options === "help") {
|
|
1672
4366
|
console.log(helpText);
|
|
1673
4367
|
return;
|