@mbws/plugin-sdk 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +1 -0
- package/dist/manifest.d.ts +4 -2
- package/dist/manifest.js +4 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/vite.js +88 -30
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -94,6 +94,7 @@ declare const UserConfigSchema: z.ZodObject<{
|
|
|
94
94
|
name: z.ZodString;
|
|
95
95
|
entry: z.ZodOptional<z.ZodString>;
|
|
96
96
|
devPath: z.ZodOptional<z.ZodString>;
|
|
97
|
+
url: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
97
98
|
}, z.core.$strip>>>;
|
|
98
99
|
devKey: z.ZodOptional<z.ZodString>;
|
|
99
100
|
apiBaseUrl: z.ZodOptional<z.ZodString>;
|
package/dist/manifest.d.ts
CHANGED
|
@@ -48,14 +48,16 @@ export declare const AttachmentDeclSchema: z.ZodObject<{
|
|
|
48
48
|
export type AttachmentDecl = z.output<typeof AttachmentDeclSchema>;
|
|
49
49
|
/**
|
|
50
50
|
* 开发者侧的附件引用(mbws.config.ts 可填形态)——platforms 由构建解析填充。
|
|
51
|
-
* devPath 是 dev-only
|
|
52
|
-
*
|
|
51
|
+
* devPath 是 dev-only 的本机路径覆盖(线上还没有的新版本附件指到本机开发),不进产物。
|
|
52
|
+
* url 是 dev 下载直链(暂由开发者提供官方地址;自建附件中心上线后改走 name+version
|
|
53
|
+
* 解析):string = 全平台同地址,record = 按平台键(darwin-arm64 等),不进产物。
|
|
53
54
|
*/
|
|
54
55
|
export declare const AttachmentRefSchema: z.ZodObject<{
|
|
55
56
|
version: z.ZodString;
|
|
56
57
|
name: z.ZodString;
|
|
57
58
|
entry: z.ZodOptional<z.ZodString>;
|
|
58
59
|
devPath: z.ZodOptional<z.ZodString>;
|
|
60
|
+
url: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
59
61
|
}, z.core.$strip>;
|
|
60
62
|
export type AttachmentRef = z.output<typeof AttachmentRefSchema>;
|
|
61
63
|
export declare const ManifestSchema: z.ZodObject<{
|
package/dist/manifest.js
CHANGED
|
@@ -72,11 +72,13 @@ export const AttachmentDeclSchema = z.object({
|
|
|
72
72
|
});
|
|
73
73
|
/**
|
|
74
74
|
* 开发者侧的附件引用(mbws.config.ts 可填形态)——platforms 由构建解析填充。
|
|
75
|
-
* devPath 是 dev-only
|
|
76
|
-
*
|
|
75
|
+
* devPath 是 dev-only 的本机路径覆盖(线上还没有的新版本附件指到本机开发),不进产物。
|
|
76
|
+
* url 是 dev 下载直链(暂由开发者提供官方地址;自建附件中心上线后改走 name+version
|
|
77
|
+
* 解析):string = 全平台同地址,record = 按平台键(darwin-arm64 等),不进产物。
|
|
77
78
|
*/
|
|
78
79
|
export const AttachmentRefSchema = AttachmentDeclSchema.omit({ platforms: true }).extend({
|
|
79
80
|
devPath: z.string().optional(),
|
|
81
|
+
url: z.union([z.string(), z.record(z.string(), z.string())]).optional(),
|
|
80
82
|
});
|
|
81
83
|
/**
|
|
82
84
|
* 权限声明,两种语法(CSP connect-src 与权限预览文案的推导输入,设计稿 §6.5):
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/dist/vite.js
CHANGED
|
@@ -18,6 +18,8 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
18
18
|
import crypto from "node:crypto";
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import path from "node:path";
|
|
21
|
+
import { pipeline } from "node:stream/promises";
|
|
22
|
+
import zlib from "node:zlib";
|
|
21
23
|
import { buildManifest, resolveAttachmentRefs } from "./config.js";
|
|
22
24
|
import { generateContractTypes } from "./schema-types.js";
|
|
23
25
|
// 契约类型生成经本子路径暴露(Node 端工具链用;依赖 prettier,不进根入口)
|
|
@@ -30,6 +32,14 @@ const DEFAULT_API_BASE = "https://union.mobiwusi.com";
|
|
|
30
32
|
function resolveApiBase(config) {
|
|
31
33
|
return (config.apiBaseUrl ?? process.env.MBWS_API_BASE_URL ?? DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
32
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* 官方附件库地址(构建期解析 platforms hash 用):env > 线上,**刻意不走
|
|
37
|
+
* config.apiBaseUrl**——它是本地联调字段,而附件库是平台生产设施,产物 hash 必须
|
|
38
|
+
* 钉生产库(在线构建容器里按本地地址 fetch 会不可达)。
|
|
39
|
+
*/
|
|
40
|
+
function attachmentApiBase() {
|
|
41
|
+
return (process.env.MBWS_API_BASE_URL ?? DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
42
|
+
}
|
|
33
43
|
/** 读请求体(POST 等带 body 的转发用;GET/HEAD 直接跳过) */
|
|
34
44
|
function readRequestBody(req) {
|
|
35
45
|
return new Promise((resolve, reject) => {
|
|
@@ -57,7 +67,7 @@ export function mbwsVitePlugin(opts) {
|
|
|
57
67
|
return [];
|
|
58
68
|
if (resolvedAttachmentsCache)
|
|
59
69
|
return resolvedAttachmentsCache;
|
|
60
|
-
resolvedAttachmentsCache = await resolveAttachmentRefs(refs,
|
|
70
|
+
resolvedAttachmentsCache = await resolveAttachmentRefs(refs, attachmentApiBase());
|
|
61
71
|
return resolvedAttachmentsCache;
|
|
62
72
|
};
|
|
63
73
|
// manifest 是纯派生物,构建/供给共用同一序列化姿势(尾换行对齐文件写习惯)。
|
|
@@ -180,7 +190,7 @@ export function mbwsVitePlugin(opts) {
|
|
|
180
190
|
// mkdir 容错:在线构建场景 outDir 被平台覆盖,本目录可能不存在(写失败会炸构建)
|
|
181
191
|
fs.mkdirSync(outDir, { recursive: true });
|
|
182
192
|
const refs = config.attachments ?? [];
|
|
183
|
-
const decls = refs.length > 0 ? await resolveAttachmentRefs(refs,
|
|
193
|
+
const decls = refs.length > 0 ? await resolveAttachmentRefs(refs, attachmentApiBase()) : [];
|
|
184
194
|
fs.writeFileSync(path.join(outDir, "manifest.json"), `${JSON.stringify(buildManifest(config, { resolvedAttachments: decls }), null, 2)}\n`);
|
|
185
195
|
},
|
|
186
196
|
};
|
|
@@ -245,10 +255,41 @@ const inflightDevAttachments = new Map();
|
|
|
245
255
|
*/
|
|
246
256
|
const devAttachmentResolves = new Map();
|
|
247
257
|
/**
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
|
|
251
|
-
|
|
258
|
+
* 流式下载到文件:边下边算 sha256(不整包进内存),每 5MB 报一次进度行。
|
|
259
|
+
* sizeHint 已知(库 resolve 下发)报「x / y MB」,直链未知报「x MB」。返回 sha256。
|
|
260
|
+
*/
|
|
261
|
+
async function downloadToFile(url, dest, log, sizeHint) {
|
|
262
|
+
const response = await fetch(url);
|
|
263
|
+
if (!response.ok || !response.body)
|
|
264
|
+
throw new Error(`下载 HTTP ${response.status}`);
|
|
265
|
+
const hash = crypto.createHash("sha256");
|
|
266
|
+
const out = fs.createWriteStream(dest);
|
|
267
|
+
let received = 0;
|
|
268
|
+
let lastReported = 0;
|
|
269
|
+
// Node fetch 的 body 是 async iterable
|
|
270
|
+
for await (const chunk of response.body) {
|
|
271
|
+
const bytes = Buffer.from(chunk);
|
|
272
|
+
hash.update(bytes);
|
|
273
|
+
out.write(bytes);
|
|
274
|
+
received += bytes.byteLength;
|
|
275
|
+
if (received - lastReported >= 5 * 1e6) {
|
|
276
|
+
lastReported = received;
|
|
277
|
+
log(sizeHint != null
|
|
278
|
+
? `[mbws:dev] 下载中 ${Math.round(received / 1e6)} / ${Math.round(sizeHint / 1e6)} MB`
|
|
279
|
+
: `[mbws:dev] 下载中 ${Math.round(received / 1e6)} MB`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
await new Promise((resolve, reject) => out.end((error) => (error ? reject(error) : resolve())));
|
|
283
|
+
return hash.digest("hex");
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* 确保附件在本地就绪(两条来源,优先级:url 直链 > 官方附件库 resolve):
|
|
287
|
+
* - url 直链(暂由开发者提供官方地址):无 sha256,缓存目录不带 hash 层
|
|
288
|
+
* (`.mbws/attachments/<name>@<version>/`,同 version 重传视为修正);
|
|
289
|
+
* 按后缀分派 tar.gz/zip 解包、.gz 单文件解压、其余当裸二进制。
|
|
290
|
+
* - 官方附件库:resolve 端点拿 url/sha256/size,内容寻址缓存
|
|
291
|
+
* `.mbws/attachments/<name>@<version>/<sha256>/`,下载后 sha256 校验 + tar -xf。
|
|
292
|
+
* 附件未声明 / 无 url 且库未登记返回 null(调用方回 501);下载/校验/解包失败抛错。
|
|
252
293
|
* log 收下载进度行(调用方转 NDJSON 事件流)。
|
|
253
294
|
*/
|
|
254
295
|
async function ensureLibraryAttachment(config, root, name, log) {
|
|
@@ -261,11 +302,49 @@ async function ensureLibraryAttachment(config, root, name, log) {
|
|
|
261
302
|
if (!ref)
|
|
262
303
|
return null;
|
|
263
304
|
const platform = currentAttachmentPlatform();
|
|
305
|
+
// win 平台包内入口带 .exe 后缀(publish 脚本布局),显式 entry 原样用
|
|
306
|
+
const entry = ref.entry ?? `${name}${platform.startsWith("win") ? ".exe" : ""}`;
|
|
307
|
+
// —— url 直链分支(开发者提供的官方地址)——
|
|
308
|
+
const directUrl = typeof ref.url === "string" ? ref.url : ref.url?.[platform];
|
|
309
|
+
if (directUrl) {
|
|
310
|
+
const dir = path.join(root, DEV_ATTACHMENT_CACHE, `${name}@${ref.version}`);
|
|
311
|
+
const entryPath = path.join(dir, entry);
|
|
312
|
+
if (fs.existsSync(entryPath))
|
|
313
|
+
return entryPath;
|
|
314
|
+
log(`[mbws:dev] 下载附件 ${name}@${ref.version}(${platform} 直链,无 hash 校验)`);
|
|
315
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
316
|
+
const tmpPath = path.join(dir, "download.tmp");
|
|
317
|
+
try {
|
|
318
|
+
await downloadToFile(directUrl, tmpPath, log);
|
|
319
|
+
const lower = directUrl.toLowerCase();
|
|
320
|
+
if (/\.(tar\.gz|tgz|zip)$/.test(lower)) {
|
|
321
|
+
const extracted = spawnSync("tar", ["-xf", tmpPath, "-C", dir]);
|
|
322
|
+
if (extracted.status !== 0) {
|
|
323
|
+
throw new Error(`解包失败:${extracted.stderr?.toString().trim() || `tar exit ${extracted.status}`}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (/\.gz$/.test(lower)) {
|
|
327
|
+
await pipeline(fs.createReadStream(tmpPath), zlib.createGunzip(), fs.createWriteStream(entryPath));
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
fs.copyFileSync(tmpPath, entryPath);
|
|
331
|
+
}
|
|
332
|
+
if (!fs.existsSync(entryPath))
|
|
333
|
+
throw new Error(`解包后未见入口 ${entry}`);
|
|
334
|
+
fs.chmodSync(entryPath, 0o755);
|
|
335
|
+
log(`[mbws:dev] 附件 ${name}@${ref.version} 就绪(已缓存,后续零下载)`);
|
|
336
|
+
return entryPath;
|
|
337
|
+
}
|
|
338
|
+
finally {
|
|
339
|
+
fs.rmSync(tmpPath, { force: true });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// —— 官方附件库分支 ——
|
|
264
343
|
const resolveKey = `${name}@${ref.version}`;
|
|
265
344
|
let resolved = devAttachmentResolves.get(resolveKey);
|
|
266
345
|
if (!resolved) {
|
|
267
346
|
try {
|
|
268
|
-
const response = await fetch(`${
|
|
347
|
+
const response = await fetch(`${attachmentApiBase()}/api/plugin-attachments/resolve` +
|
|
269
348
|
`?name=${encodeURIComponent(name)}&version=${encodeURIComponent(ref.version)}` +
|
|
270
349
|
`&platform=${platform}`);
|
|
271
350
|
// 未登记/平台缺失/后端不可达都归「dev 不可用」,交给调用方 501 文案
|
|
@@ -278,8 +357,6 @@ async function ensureLibraryAttachment(config, root, name, log) {
|
|
|
278
357
|
return null;
|
|
279
358
|
}
|
|
280
359
|
}
|
|
281
|
-
// win 平台包内入口带 .exe 后缀(publish 脚本布局),显式 entry 原样用
|
|
282
|
-
const entry = ref.entry ?? `${name}${platform.startsWith("win") ? ".exe" : ""}`;
|
|
283
360
|
const dir = path.join(root, DEV_ATTACHMENT_CACHE, `${name}@${ref.version}`, resolved.sha256);
|
|
284
361
|
const entryPath = path.join(dir, entry);
|
|
285
362
|
if (fs.existsSync(entryPath))
|
|
@@ -288,26 +365,7 @@ async function ensureLibraryAttachment(config, root, name, log) {
|
|
|
288
365
|
fs.mkdirSync(dir, { recursive: true });
|
|
289
366
|
const tmpPath = path.join(dir, `download.${resolved.archive}`);
|
|
290
367
|
try {
|
|
291
|
-
const
|
|
292
|
-
if (!response.ok || !response.body)
|
|
293
|
-
throw new Error(`下载 HTTP ${response.status}`);
|
|
294
|
-
const hash = crypto.createHash("sha256");
|
|
295
|
-
const out = fs.createWriteStream(tmpPath);
|
|
296
|
-
let received = 0;
|
|
297
|
-
let lastReported = 0;
|
|
298
|
-
// Node fetch 的 body 是 async iterable:流式落盘 + 边下边算 hash(不整包进内存)
|
|
299
|
-
for await (const chunk of response.body) {
|
|
300
|
-
const bytes = Buffer.from(chunk);
|
|
301
|
-
hash.update(bytes);
|
|
302
|
-
out.write(bytes);
|
|
303
|
-
received += bytes.byteLength;
|
|
304
|
-
if (received - lastReported >= 5 * 1e6) {
|
|
305
|
-
lastReported = received;
|
|
306
|
-
log(`[mbws:dev] 下载中 ${Math.round(received / 1e6)} / ${Math.round(resolved.size / 1e6)} MB`);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
await new Promise((resolve, reject) => out.end((error) => (error ? reject(error) : resolve())));
|
|
310
|
-
const sha256 = hash.digest("hex");
|
|
368
|
+
const sha256 = await downloadToFile(resolved.url, tmpPath, log, resolved.size);
|
|
311
369
|
if (sha256 !== resolved.sha256) {
|
|
312
370
|
throw new Error(`sha256 不符(期望 ${resolved.sha256},实得 ${sha256})`);
|
|
313
371
|
}
|
|
@@ -375,7 +433,7 @@ async function handleDevExec(config, root, req, res) {
|
|
|
375
433
|
res.statusCode = 501;
|
|
376
434
|
res.end(JSON.stringify({
|
|
377
435
|
message: declared
|
|
378
|
-
? `附件 ${attachment}
|
|
436
|
+
? `附件 ${attachment} 无 url 直链且未在官方附件库登记当前平台产物(或后端不可达);可配 attachments 的 url 直链 / devPath / env MBWS_DEV_ATTACHMENT_${attachment.replace(/-/g, "_").toUpperCase()}`
|
|
379
437
|
: `mbws.config.ts 的 attachments 未声明附件 ${attachment}(dev 自动下载依赖声明);本机覆盖可配该项 devPath / env MBWS_DEV_ATTACHMENT_${attachment.replace(/-/g, "_").toUpperCase()}`,
|
|
380
438
|
}));
|
|
381
439
|
return;
|