modelstage 0.5.24 → 0.5.28
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 +7 -0
- package/bin/modelstage.mjs +48 -12
- package/config/models/live2d.azurlane.json +1 -1
- package/config/models/live2d.azurlane_spine.json +1 -1
- package/config/models/live2d.npc.json +1 -1
- package/config/models/spine.azurlane.json +1 -1
- package/config/models/spine.azurlane_spine.json +1 -1
- package/config/models/spine.npc.json +1 -1
- package/package.json +7 -2
- package/src/server/http/write.mjs +8 -1
- package/src/server/serve.mjs +62 -0
- package/src/shared/type-labels.mjs +31 -0
- package/src/web/components.css +59 -0
- package/src/web/index.html +2 -0
- package/src/web/roster/persist.js +40 -0
- package/src/web/roster/snapshot.js +2 -0
- package/src/web/roster/type-filter.js +97 -0
- package/src/web/roster/view.js +93 -2
- package/tool/lib/check-perms.mjs +0 -114
- package/tool/lib/contract-checks.mjs +0 -582
- package/tool/lib/cubism-core.mjs +0 -122
- package/tool/lib/export-bundle.mjs +0 -328
- package/tool/lib/spine-runtime.mjs +0 -131
- package/tool/lib/test-lock.mjs +0 -178
- package/tool/lib/test-report.mjs +0 -133
package/README.md
CHANGED
|
@@ -184,8 +184,15 @@ node tool/resolve-live2d-runtimes.mjs # 查看 Live2D 运行时探测结果
|
|
|
184
184
|
|
|
185
185
|
| 版本 | 日期 | 要点 |
|
|
186
186
|
|---|---|---|
|
|
187
|
+
| 0.5.26 | 2026-11-01 | 对齐成熟包的四项:`bin` 加 realpath 直接执行判据(import 不再起服务)、`serve.mjs` 加 EADDRINUSE 人话提示(退出码 2)、新增 `/api/health`(`web.sh status` 优先用它、旧实例回落首页码)、`web.sh` 日志 5MB 轮转(先 stop 再轮转、只留 1 份);顺带修 health 的 `plugins` 恒为 0(引用了不存在的 ctx) |
|
|
188
|
+
| 0.5.28 | 2026-11-05 | 名册类型筛选:左栏搜索框下方「类型胶囊」按内容类型过滤(动态派生、多选并集、与关键词交集、计数按当前搜索、`unknown` 归「待解包」、自动换行不折叠、选中态持久化);类型显示名单一来源 `src/shared/type-labels.mjs` |
|
|
189
|
+
| 0.5.27 | 2026-11-05 | 测试分级迁移 P4:底栏「单动循环」断言判为已覆盖**整条删除**(缺口改由 schema 表断言补)、顶栏按钮集合 → `test/toolbar-contract.test.mjs`(断言 import 的 TOOLBAR_ACTIONS / 图标白名单 / 插件上限);两个浏览器脚本共删 41 行,套件 33 → 34 |
|
|
190
|
+
| 0.5.26 | 2026-11-05 | 浏览器测试分级迁移(批次 3/5):panel-actions 的 6 条契约断言迁为静态测试 `test/panel-contract.test.mjs`(断言对象是 schema 导出表与 CSS 规则,不起浏览器);分组标题由写死 9 个改为 schema 推导;套件 32 → 33 |
|
|
191
|
+
| 0.5.25 | 2026-11-01 | 包体积收窄:`tool/lib/` 整目录放行改为 **6 个运行时文件**(其余 7 个是测试/校验/开发件,`check-perms` / `test-lock` / `test-report` / `contract-checks` / `export-bundle` / `cubism-core` / `spine-runtime`);发布守卫新增四类禁止项(`test/`、`*.test.*`、`*.map`、开发工具、缓存/上传/日志);包内 286 → 279 文件 |
|
|
187
192
|
| 0.5.24 | 2026-11-01 | 发布准备:补 `THIRD_PARTY_NOTICES.md`、README「合规状态」扩到 Live2D Cubism 与 pixi.js 并注明「服务端把运行时报给浏览器 = 分发行为」;补 author/keywords/bugs、清理 `files` 无效条目;去掉 `private` 以便发布;README 标注项目进入维护为主阶段 |
|
|
188
193
|
| 0.5.23 | 2026-11-01 | 解包节点判据由「名字前缀」改为「结构形式」(`/^CAB-[0-9a-f]{32}$/i`,四处统一 + 防漂移单测):`.resS` / `.resource`(FSB5 音频银行)不再被误当 serialized file;顺带修好两个包的不完整提取(假节点抛错曾中断循环,真节点没被处理);测试套件 29 → 30 |
|
|
194
|
+
| 0.5.25 | 2026-11-05 | 测试分级迁移批次 2:新增 `test/roster-filter.test.mjs`(搜索/合并语义 unit + 键名静态断言),`roster-actions.js` 删 3 条搜索断言合并为 1 条接线冒烟;顺带修一条既有必红断言(写死渲染上限)与 4 处过时 skip 名;套件 31 → 32 |
|
|
195
|
+
| 0.5.24 | 2026-11-05 | 浏览器测试迁移第 1 批:资源根 CRUD 的 7 条 HTTP 契约断言迁到 `test/roots-api.test.mjs`(浏览器侧只留 3 条交互断言 + 4 条冒烟);顺带修 `409` 响应丢 `path`/`existing` 的透传缺陷(已补回归断言) |
|
|
189
196
|
| 0.5.22 | 2026-11-01 | `tool/lib/runtime-package.mjs` 的 `writeAtomic` 写前自建父目录(原子写函数不依赖调用方建目录);`src/server/http/atomic-json.mjs` 核实后本就有 `ensureDir`,未改动 |
|
|
190
197
|
| 0.5.21 | 2026-11-01 | 修 `document.files` 的 catalog 请求无超时:加 `DOC_FILES_TIMEOUT_MS = 6000` + `AbortSignal.timeout`,`pending` 清理移入 `finally`(挂起后可重试,不再永久占位),失败记 warn 含 rootId/url/reason |
|
|
191
198
|
| 0.5.20 | 2026-10-31 | 全量审计后清理真实硬编码:插件清单 `depsDir` 改 `${MODELSTAGE_DEPS_DIR}` 占位符(换机只改 env)、删 `interpreter` 交给探测链、MIME 字面量各文件提常量、6 个脚本统一 `here(import.meta.url)`、清 5 处注释日期 |
|
package/bin/modelstage.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// --port <n> → PORT
|
|
11
11
|
// --data-root <path> → MODELSTAGE_DATA_ROOT
|
|
12
12
|
|
|
13
|
-
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import os from 'node:os';
|
|
16
16
|
import path from 'node:path';
|
|
@@ -61,32 +61,62 @@ function parseArgs(argv) {
|
|
|
61
61
|
return parsed;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* 本文件是否被**直接执行**(而不是被别人 import)。
|
|
66
|
+
*
|
|
67
|
+
* 为什么必须判:`src/server/serve.mjs` 是"import 即 listen"的模块,
|
|
68
|
+
* 如果 bin 在顶层无条件 import 它,那么**任何人 import 这个 bin 都会把服务起起来**
|
|
69
|
+
* (测试里想复用 parseArgs 也会意外监听端口)。
|
|
70
|
+
*
|
|
71
|
+
* 为什么用 realpath 而不是 path.resolve 比较:
|
|
72
|
+
* `path.resolve` 只做字符串归一,不解析符号链接。而 npx / 全局安装时
|
|
73
|
+
* `process.argv[1]` 是 `<bin 目录>/modelstage` 这个**符号链接**,
|
|
74
|
+
* `import.meta.url` 却是真实文件路径 ⇒ 两者字符串永远不等 ⇒ 静默什么都不做(exit 0 但没有服务)。
|
|
75
|
+
* 这类"看着成功了其实没启动"最难查,所以两边都先 realpath 再比。
|
|
76
|
+
* @returns {boolean}
|
|
77
|
+
*/
|
|
78
|
+
function isDirectInvocation() {
|
|
79
|
+
try {
|
|
80
|
+
const entry = process.argv[1];
|
|
81
|
+
if (!entry) return false; // 例如 `node -e "..."`:没有入口文件
|
|
82
|
+
const selfPath = fileURLToPath(import.meta.url);
|
|
83
|
+
return realpathSync(selfPath) === realpathSync(entry);
|
|
84
|
+
} catch {
|
|
85
|
+
return false; // argv[1] 指向不存在的文件等:按"不是直接执行"处理
|
|
86
|
+
}
|
|
87
|
+
}
|
|
65
88
|
|
|
66
|
-
|
|
89
|
+
/**
|
|
90
|
+
* 真正的启动流程。**只在直接执行时调用** —— 被 import 时既不解析参数、也不起服务。
|
|
91
|
+
* @returns {Promise<void>}
|
|
92
|
+
*/
|
|
93
|
+
async function main() {
|
|
94
|
+
const args = parseArgs(process.argv.slice(2));
|
|
95
|
+
|
|
96
|
+
if (args.help) {
|
|
67
97
|
process.stdout.write(`${USAGE}\n`);
|
|
68
98
|
process.exit(0);
|
|
69
|
-
}
|
|
99
|
+
}
|
|
70
100
|
|
|
71
|
-
if (args.version) {
|
|
101
|
+
if (args.version) {
|
|
72
102
|
// 用 fs 读而不是 `import pkg from './package.json' with { type: 'json' }`:
|
|
73
103
|
// import attributes 在 Node 20/22 之间行为不一致(实验特性),fs 读在任何版本都稳定
|
|
74
104
|
const pkg = JSON.parse(readFileSync(PKG_PATH, 'utf8'));
|
|
75
105
|
process.stdout.write(`${pkg.version}\n`);
|
|
76
106
|
process.exit(0);
|
|
77
|
-
}
|
|
107
|
+
}
|
|
78
108
|
|
|
79
|
-
if (args.port !== null) {
|
|
109
|
+
if (args.port !== null) {
|
|
80
110
|
// 端口必须能转成整数:serve.mjs 用的是 Number(process.env.PORT) || 8804,
|
|
81
111
|
// 传个 "abc" 会静默回落到 8804 —— 看上去启动了、其实端口不是期望的那个,这里提前拦下
|
|
82
112
|
if (!/^\d+$/.test(args.port)) fail(`--port 必须是整数,收到 ${args.port}`);
|
|
83
113
|
process.env.PORT = args.port;
|
|
84
|
-
}
|
|
114
|
+
}
|
|
85
115
|
|
|
86
|
-
if (args.dataRoot) {
|
|
116
|
+
if (args.dataRoot) {
|
|
87
117
|
// 转成绝对路径:serve.mjs 会把它当数据根拼日志与缓存路径,相对路径会随 cwd 漂移
|
|
88
118
|
process.env.MODELSTAGE_DATA_ROOT = path.resolve(args.dataRoot);
|
|
89
|
-
} else if (!process.env.MODELSTAGE_DATA_ROOT) {
|
|
119
|
+
} else if (!process.env.MODELSTAGE_DATA_ROOT) {
|
|
90
120
|
// 没给 --data-root、也没有环境变量 ⇒ **装成包运行时落用户目录,不是包目录**。
|
|
91
121
|
//
|
|
92
122
|
// 为什么必须在这里注入、而不是改 serve.mjs 的默认值:
|
|
@@ -95,7 +125,13 @@ if (args.dataRoot) {
|
|
|
95
125
|
// 日志、解包缓存、runtimes 全落进包目录:升级即丢,且包目录可能只读。
|
|
96
126
|
// ⇒ 只在 bin 层(包运行专用的入口)注入,仓库运行路径完全不受影响。
|
|
97
127
|
process.env.MODELSTAGE_DATA_ROOT = path.join(os.homedir(), '.modelstage');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 交给真正的服务入口:它是模块顶层直接 listen 的,import 即启动,无需再 await 返回值
|
|
131
|
+
await import('../src/server/serve.mjs');
|
|
98
132
|
}
|
|
99
133
|
|
|
100
|
-
//
|
|
101
|
-
await
|
|
134
|
+
// 只有"直接跑这个文件"才启动服务;被 import 时只导出函数(测试可安全 import)
|
|
135
|
+
if (isDirectInvocation()) await main();
|
|
136
|
+
|
|
137
|
+
export { isDirectInvocation, main, parseArgs };
|