@mbws/plugin-sdk 0.3.0 → 0.3.1

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 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>;
@@ -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
- * 未填/路径不存在时 dev 自动按 name+version 从官方附件库下载缓存),不进产物。
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
- * 未填/路径不存在时 dev 自动按 name+version 从官方附件库下载缓存),不进产物。
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
@@ -12,4 +12,4 @@ export declare const PROTOCOL_VERSION: 1;
12
12
  * SDK 自身版本(与 package.json version 保持一致)。
13
13
  * init-ack 上报给宿主做审计,与 manifest.sdk(build 写入)交叉核对。
14
14
  */
15
- export declare const SDK_VERSION = "0.3.0";
15
+ export declare const SDK_VERSION = "0.3.1";
package/dist/version.js CHANGED
@@ -12,4 +12,4 @@ export const PROTOCOL_VERSION = 1;
12
12
  * SDK 自身版本(与 package.json version 保持一致)。
13
13
  * init-ack 上报给宿主做审计,与 manifest.sdk(build 写入)交叉核对。
14
14
  */
15
- export const SDK_VERSION = "0.3.0";
15
+ export const SDK_VERSION = "0.3.1";
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,不进根入口)
@@ -245,10 +247,41 @@ const inflightDevAttachments = new Map();
245
247
  */
246
248
  const devAttachmentResolves = new Map();
247
249
  /**
248
- * 确保官方附件库附件在本地就绪:按 config.attachments 声明查 resolve 端点,
249
- * 内容寻址缓存 `.mbws/attachments/<name>@<version>/<sha256>/<entry>`——命中零下载;
250
- * 未命中流式下载(sha256 校验)+ `tar -xf` 解包(bsdtar 通吃 tar.gz/zip)+ chmod 755。
251
- * 附件未声明 / 库未登记返回 null(调用方回 501);下载/校验/解包失败抛错。
250
+ * 流式下载到文件:边下边算 sha256(不整包进内存),每 5MB 报一次进度行。
251
+ * sizeHint 已知(库 resolve 下发)报「x / y MB」,直链未知报「x MB」。返回 sha256
252
+ */
253
+ async function downloadToFile(url, dest, log, sizeHint) {
254
+ const response = await fetch(url);
255
+ if (!response.ok || !response.body)
256
+ throw new Error(`下载 HTTP ${response.status}`);
257
+ const hash = crypto.createHash("sha256");
258
+ const out = fs.createWriteStream(dest);
259
+ let received = 0;
260
+ let lastReported = 0;
261
+ // Node fetch 的 body 是 async iterable
262
+ for await (const chunk of response.body) {
263
+ const bytes = Buffer.from(chunk);
264
+ hash.update(bytes);
265
+ out.write(bytes);
266
+ received += bytes.byteLength;
267
+ if (received - lastReported >= 5 * 1e6) {
268
+ lastReported = received;
269
+ log(sizeHint != null
270
+ ? `[mbws:dev] 下载中 ${Math.round(received / 1e6)} / ${Math.round(sizeHint / 1e6)} MB`
271
+ : `[mbws:dev] 下载中 ${Math.round(received / 1e6)} MB`);
272
+ }
273
+ }
274
+ await new Promise((resolve, reject) => out.end((error) => (error ? reject(error) : resolve())));
275
+ return hash.digest("hex");
276
+ }
277
+ /**
278
+ * 确保附件在本地就绪(两条来源,优先级:url 直链 > 官方附件库 resolve):
279
+ * - url 直链(暂由开发者提供官方地址):无 sha256,缓存目录不带 hash 层
280
+ * (`.mbws/attachments/<name>@<version>/`,同 version 重传视为修正);
281
+ * 按后缀分派 tar.gz/zip 解包、.gz 单文件解压、其余当裸二进制。
282
+ * - 官方附件库:resolve 端点拿 url/sha256/size,内容寻址缓存
283
+ * `.mbws/attachments/<name>@<version>/<sha256>/`,下载后 sha256 校验 + tar -xf。
284
+ * 附件未声明 / 无 url 且库未登记返回 null(调用方回 501);下载/校验/解包失败抛错。
252
285
  * log 收下载进度行(调用方转 NDJSON 事件流)。
253
286
  */
254
287
  async function ensureLibraryAttachment(config, root, name, log) {
@@ -261,6 +294,44 @@ async function ensureLibraryAttachment(config, root, name, log) {
261
294
  if (!ref)
262
295
  return null;
263
296
  const platform = currentAttachmentPlatform();
297
+ // win 平台包内入口带 .exe 后缀(publish 脚本布局),显式 entry 原样用
298
+ const entry = ref.entry ?? `${name}${platform.startsWith("win") ? ".exe" : ""}`;
299
+ // —— url 直链分支(开发者提供的官方地址)——
300
+ const directUrl = typeof ref.url === "string" ? ref.url : ref.url?.[platform];
301
+ if (directUrl) {
302
+ const dir = path.join(root, DEV_ATTACHMENT_CACHE, `${name}@${ref.version}`);
303
+ const entryPath = path.join(dir, entry);
304
+ if (fs.existsSync(entryPath))
305
+ return entryPath;
306
+ log(`[mbws:dev] 下载附件 ${name}@${ref.version}(${platform} 直链,无 hash 校验)`);
307
+ fs.mkdirSync(dir, { recursive: true });
308
+ const tmpPath = path.join(dir, "download.tmp");
309
+ try {
310
+ await downloadToFile(directUrl, tmpPath, log);
311
+ const lower = directUrl.toLowerCase();
312
+ if (/\.(tar\.gz|tgz|zip)$/.test(lower)) {
313
+ const extracted = spawnSync("tar", ["-xf", tmpPath, "-C", dir]);
314
+ if (extracted.status !== 0) {
315
+ throw new Error(`解包失败:${extracted.stderr?.toString().trim() || `tar exit ${extracted.status}`}`);
316
+ }
317
+ }
318
+ else if (/\.gz$/.test(lower)) {
319
+ await pipeline(fs.createReadStream(tmpPath), zlib.createGunzip(), fs.createWriteStream(entryPath));
320
+ }
321
+ else {
322
+ fs.copyFileSync(tmpPath, entryPath);
323
+ }
324
+ if (!fs.existsSync(entryPath))
325
+ throw new Error(`解包后未见入口 ${entry}`);
326
+ fs.chmodSync(entryPath, 0o755);
327
+ log(`[mbws:dev] 附件 ${name}@${ref.version} 就绪(已缓存,后续零下载)`);
328
+ return entryPath;
329
+ }
330
+ finally {
331
+ fs.rmSync(tmpPath, { force: true });
332
+ }
333
+ }
334
+ // —— 官方附件库分支 ——
264
335
  const resolveKey = `${name}@${ref.version}`;
265
336
  let resolved = devAttachmentResolves.get(resolveKey);
266
337
  if (!resolved) {
@@ -278,8 +349,6 @@ async function ensureLibraryAttachment(config, root, name, log) {
278
349
  return null;
279
350
  }
280
351
  }
281
- // win 平台包内入口带 .exe 后缀(publish 脚本布局),显式 entry 原样用
282
- const entry = ref.entry ?? `${name}${platform.startsWith("win") ? ".exe" : ""}`;
283
352
  const dir = path.join(root, DEV_ATTACHMENT_CACHE, `${name}@${ref.version}`, resolved.sha256);
284
353
  const entryPath = path.join(dir, entry);
285
354
  if (fs.existsSync(entryPath))
@@ -288,26 +357,7 @@ async function ensureLibraryAttachment(config, root, name, log) {
288
357
  fs.mkdirSync(dir, { recursive: true });
289
358
  const tmpPath = path.join(dir, `download.${resolved.archive}`);
290
359
  try {
291
- const response = await fetch(resolved.url);
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");
360
+ const sha256 = await downloadToFile(resolved.url, tmpPath, log, resolved.size);
311
361
  if (sha256 !== resolved.sha256) {
312
362
  throw new Error(`sha256 不符(期望 ${resolved.sha256},实得 ${sha256})`);
313
363
  }
@@ -375,7 +425,7 @@ async function handleDevExec(config, root, req, res) {
375
425
  res.statusCode = 501;
376
426
  res.end(JSON.stringify({
377
427
  message: declared
378
- ? `附件 ${attachment} 未在官方附件库登记当前平台产物(发布见 scripts/publish-attachment.mjs),或后端不可达;本机覆盖可配 attachments 的 devPath / env MBWS_DEV_ATTACHMENT_${attachment.replace(/-/g, "_").toUpperCase()}`
428
+ ? `附件 ${attachment} url 直链且未在官方附件库登记当前平台产物(或后端不可达);可配 attachments 的 url 直链 / devPath / env MBWS_DEV_ATTACHMENT_${attachment.replace(/-/g, "_").toUpperCase()}`
379
429
  : `mbws.config.ts 的 attachments 未声明附件 ${attachment}(dev 自动下载依赖声明);本机覆盖可配该项 devPath / env MBWS_DEV_ATTACHMENT_${attachment.replace(/-/g, "_").toUpperCase()}`,
380
430
  }));
381
431
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mbws/plugin-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Moye 插件开发 SDK——panel/viewer 与宿主的类型定义与运行时端口适配",
5
5
  "license": "MIT",
6
6
  "type": "module",