@longzai-intelligence-release/core 0.0.1 → 0.0.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/index.d.ts CHANGED
@@ -105,8 +105,10 @@ type PublishResult = 'success' | 'skip' | 'fail';
105
105
  /**
106
106
  * 发布目标轨道(ADR 0082:包级自治的双轨发布)
107
107
  *
108
- * - `npm`:公网 npm 轨(bun publish --registry 显式钉死 npm 官方源,免疫 bunfig/.npmrc/env 中 mirror 加速配置的劫持;见
109
- * apps/cli issue 0004)
108
+ * - `npm`:公网 npm 轨(bun publish --registry 显式指定 npm 官方源。注意旗标不具
109
+ * 终局性:bun 的 registry 解析优先级为 scope 映射 > `--registry` 旗标,scope 被
110
+ * 映射到镜像源或本仓 scope 未注册时发布仍会被改写目标并以 missing authentication
111
+ * 失败——发布前的 scope 映射预检见 registry-resolution.utils,apps/cli issue 0004)
110
112
  * - `local`:本地私有轨(bun publish --registry <url>;发布前 ensure 本地 registry 在线, 地址来自 env
111
113
  * `LZI_NPM_REGISTRY_URL`,凭据经 `NPM_CONFIG_TOKEN` env 继承)
112
114
  */
@@ -130,14 +132,16 @@ type PublishTarget = {
130
132
  *
131
133
  * mirror 加速是 install 侧的既定治理(ADR 0030:bunfig `[install].registry`、.npmrc、
132
134
  * `NPM_CONFIG_REGISTRY` env),对发布写入一律不合法——npm 轨发布目标固定为官方源,
133
- * 不可配置(apps/cli issue 0004)。
135
+ * 不可配置(apps/cli issue 0004)。旗标之外以发布前 scope 映射预检兜底(旗标优先级
136
+ * 低于 scope 映射,见 registry-resolution.utils)。
134
137
  */
135
138
  declare const NPM_OFFICIAL_REGISTRY_URL = "https://registry.npmjs.org/";
136
139
  /**
137
140
  * 构造 bun publish 参数(纯函数,测试注入用)
138
141
  *
139
- * 两轨均显式 `--registry`:npm 轨钉死官方源常量(免疫 mirror 劫持,issue 0004),
140
- * local 轨指向 env 注入的本地私有 registry(ADR 0082)。
142
+ * 两轨均显式 `--registry`:npm 轨指向官方源常量,local 轨指向 env 注入的本地私有
143
+ * registry(ADR 0082)。旗标不具终局性(bun 优先级 scope 映射 > 旗标),scope
144
+ * 劫持/缺位的拦截由发布前 registry 预检负责(registry-resolution.utils)。
141
145
  *
142
146
  * @param target - 发布目标轨道
143
147
  * @param access - npm 发布访问级别
@@ -279,4 +283,164 @@ declare const getPackageVersion: (packagePath: string) => Promise<string>;
279
283
  */
280
284
  declare const printSummary: (stats: PublishStats) => void;
281
285
  //#endregion
282
- export { NPM_OFFICIAL_REGISTRY_URL, type PackageManifest, type PackagePublishResult, type PublishOptions, type PublishResult, type PublishStats, type PublishTarget, buildPackage, buildPublishArgs, buildPublishEnv, describePublishTarget, ensureLocalRegistry, getPackageVersion, installDependencies, logError, logInfo, logSuccess, logWarn, normalizePublishTracks, printSummary, publishCommand, publishPackage, readPackageJson, resolvePackageManifest, scanAllPackages, topologicalSort, updateDependencies };
286
+ //#region src/utils/registry-resolution.utils.d.ts
287
+ /**
288
+ * 发布 registry 解析预检(scope 映射优先级防线)
289
+ *
290
+ * bun publish 的 registry 解析优先级:scope 映射(bunfig.toml `[install.scopes]` /
291
+ * .npmrc `@scope:registry=`)> `--registry` 旗标 > 默认 registry(bunfig
292
+ * `[install].registry` / .npmrc `registry=` / env)。
293
+ *
294
+ * lzi-release 两轨均以 `--registry` 显式指定目标,但旗标不具终局性——scope 映射
295
+ * 优先于旗标(bun 文档 Custom Registry 节),发布目标可能被静默改写:
296
+ *
297
+ * 1. npm 轨包的 scope 被映射到镜像源(bunfig 或 .npmrc):发布写入只读镜像,
298
+ * 以 missing authentication 失败;
299
+ * 2. npm 轨包的 scope 未注册(映射缺位):bun 1.3.x 实测旗标路径凭据解析不可靠
300
+ * (github-trending-markdown 仓公网轨发布失败实录),同样以 missing
301
+ * authentication 失败;
302
+ * 3. local 轨包的 scope 被映射到非本轨源(如 globals 预设统一映射 npmjs):
303
+ * 发布偏离本地私有 registry。
304
+ *
305
+ * 发布前按上述优先级实际解析每个待发包的 registry 并与轨道声明比对,不符即
306
+ * fail fast,把 bun 侧误导性的 missing authentication 翻译成根因清晰的指引。
307
+ */
308
+ /**
309
+ * scope 映射来源(label 为人读展示名,禁止携带机器绝对路径)
310
+ */
311
+ type ScopeRegistrySource = {
312
+ /**
313
+ * 来源展示名(如 bunfig.toml / 项目 .npmrc / 用户 ~/.npmrc)
314
+ */
315
+ readonly label: string;
316
+ /**
317
+ * 该来源解析出的 scope → registry 映射(scope 键不含 @ 前缀)
318
+ */
319
+ readonly scopes: Readonly<Record<string, string>>;
320
+ };
321
+ /**
322
+ * 单包预检入参
323
+ */
324
+ type ScopeRegistryCheckInput = {
325
+ /**
326
+ * 待发布包全名(@scope/name 形态)
327
+ */
328
+ readonly packageName: string;
329
+ /**
330
+ * 发布轨道(ADR 0082:npm 公网轨 / local 本地私有轨)
331
+ */
332
+ readonly track: 'npm' | 'local';
333
+ /**
334
+ * 轨道声明的目标 registry 基址(npm 轨为官方源常量,local 轨为 env 注入的私有源)
335
+ */
336
+ readonly expectedRegistryUrl: string;
337
+ /**
338
+ * 参与解析的 scope 映射来源清单(bunfig / 项目 .npmrc / 用户 .npmrc)
339
+ */
340
+ readonly sources: readonly ScopeRegistrySource[];
341
+ };
342
+ /**
343
+ * 从包名提取 scope 段(不含 @ 前缀的规范形式)
344
+ *
345
+ * `@longzai-intelligence-release/cli` → `longzai-intelligence-release`;
346
+ * 非 scoped 包名返回 undefined(无 scope 映射可命中,旗标即终局)。
347
+ *
348
+ * @param packageName - 包全名
349
+ * @returns scope 规范形式,非 scoped 包名返回 undefined
350
+ */
351
+ declare const extractPackageScope: (packageName: string) => string | undefined;
352
+ /**
353
+ * 归一化 registry URL 为可比较形式(去尾部斜杠;比较用,不用于展示)
354
+ *
355
+ * `https://registry.npmjs.org/` 与 `https://registry.npmjs.org` 视为同一源,
356
+ * 避免 bunfig 与轨道常量的斜杠差造成误报。
357
+ *
358
+ * @param url - registry 基址
359
+ * @returns 去尾部斜杠后的可比较形式
360
+ */
361
+ declare const normalizeRegistryUrl: (url: string) => string;
362
+ /**
363
+ * 解析 bunfig.toml 文本中的 [install.scopes] 映射(纯函数)
364
+ *
365
+ * 键剥离 @ 前缀(bunfig 两种写法 `@scope` / `scope` 等价);无 install.scopes 段
366
+ * 或解析失败返回空映射(解析失败由调用方另行告警,预检不因杂项 TOML 问题误拦)。
367
+ *
368
+ * @param tomlText - bunfig.toml 文件内容
369
+ * @returns scope → registry 映射(scope 键不含 @ 前缀)
370
+ */
371
+ declare const parseBunfigScopeRegistries: (tomlText: string) => Record<string, string>;
372
+ /**
373
+ * 解析 .npmrc 文本中的 @scope:registry= 映射(纯函数)
374
+ *
375
+ * 仅识别字面量 URL 行(`@scope:registry=https://...`);`${VAR}` 插值行无法离线
376
+ * 解析,跳过(此类行在产品群 .npmrc 中不存在,出现时由 bun 运行时自身报错更直接)。
377
+ *
378
+ * @param npmrcText - .npmrc 文件内容
379
+ * @returns scope → registry 映射(scope 键不含 @ 前缀)
380
+ */
381
+ declare const parseNpmrcScopeRegistries: (npmrcText: string) => Record<string, string>;
382
+ /**
383
+ * 加载参与预检的 scope 映射来源(IO 层,纯函数解析器的薄封装)
384
+ *
385
+ * 三个来源:发布仓根 bunfig.toml、发布仓根 .npmrc、用户 ~/.npmrc。文件缺失跳过;
386
+ * bunfig.toml 存在但 TOML 解析失败时告警并按空映射参与(不拦截,交由 bun 自身
387
+ * 对损坏配置的报错处置)。
388
+ *
389
+ * @param rootDir - 发布仓根目录(monorepo 根,bun publish 子进程的 bunfig 发现根)
390
+ * @returns 非空映射来源清单(全部缺失时为空数组,预检自然放行为旗标终局形态)
391
+ */
392
+ declare const loadScopeRegistrySources: (rootDir: string) => Promise<ScopeRegistrySource[]>;
393
+ /**
394
+ * 单包 scope 映射预检(纯函数,违规明细聚合一次返回)
395
+ *
396
+ * 判定规则(按轨道,ADR 0082 + bun scope 映射优先级事实):
397
+ *
398
+ * - npm 轨:scope 映射存在则必须等于官方源(映射劫持即违规);scoped 包的映射
399
+ * 缺位同样违规(bun 1.3.x 旗标路径凭据解析不可靠的实录,且本仓 scope 兜底
400
+ * 注入后缺位即漂移信号);非 scoped 包无映射可命中,放行。
401
+ * - local 轨:scope 映射存在则必须等于本轨私有源(偏离即违规);映射缺位放行
402
+ * (ADR 0082 privateScopes 的设计形态即缺位 + 旗标 + NPM_CONFIG_TOKEN env 凭据)。
403
+ *
404
+ * @param input - 单包预检入参(包名、轨道、期望源、映射来源清单)
405
+ * @returns 违规明细列表(空数组即通过)
406
+ */
407
+ declare const checkScopeRegistryForPublish: (input: ScopeRegistryCheckInput) => string[];
408
+ /**
409
+ * 预检入参(发布命令层装配)
410
+ */
411
+ type PublishRegistryPreflightInput = {
412
+ /**
413
+ * 参与发布的包清单(已解析路径与名称)
414
+ */
415
+ readonly manifests: readonly {
416
+ /**
417
+ * 包全名
418
+ */
419
+ readonly name: string;
420
+ }[];
421
+ /**
422
+ * 包名 → 发布轨道映射(ADR 0082 双轨归一化结果)
423
+ */
424
+ readonly trackByName: ReadonlyMap<string, 'npm' | 'local'>;
425
+ /**
426
+ * 本地私有轨 registry 基址(env LZI_NPM_REGISTRY_URL)
427
+ */
428
+ readonly localRegistryUrl: string;
429
+ /**
430
+ * 发布仓根目录(scope 映射来源的加载根)
431
+ */
432
+ readonly rootDir: string;
433
+ };
434
+ /**
435
+ * 执行发布 registry 预检(scope 映射优先级防线,fail fast)
436
+ *
437
+ * 逐包按 checkScopeRegistryForPublish 判定,全部违规一次报出;通过输出确认日志。
438
+ * 返回是否通过(false 时调用方 exit(1),dry-run 与真实发布均拦截——预览形态
439
+ * 同样不应展示注定失败的发布计划)。
440
+ *
441
+ * @param input - 预检入参(包清单、轨道映射、私有源基址、仓根目录)
442
+ * @returns 全部通过返回 true,存在违规返回 false
443
+ */
444
+ declare const runPublishRegistryPreflight: (input: PublishRegistryPreflightInput) => Promise<boolean>;
445
+ //#endregion
446
+ export { NPM_OFFICIAL_REGISTRY_URL, type PackageManifest, type PackagePublishResult, type PublishOptions, type PublishRegistryPreflightInput, type PublishResult, type PublishStats, type PublishTarget, type ScopeRegistryCheckInput, type ScopeRegistrySource, buildPackage, buildPublishArgs, buildPublishEnv, checkScopeRegistryForPublish, describePublishTarget, ensureLocalRegistry, extractPackageScope, getPackageVersion, installDependencies, loadScopeRegistrySources, logError, logInfo, logSuccess, logWarn, normalizePublishTracks, normalizeRegistryUrl, parseBunfigScopeRegistries, parseNpmrcScopeRegistries, printSummary, publishCommand, publishPackage, readPackageJson, resolvePackageManifest, runPublishRegistryPreflight, scanAllPackages, topologicalSort, updateDependencies };
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
- import"./rolldown-runtime-BqT_7tdF.js";import{loadReleaseConfig as e}from"@longzai-intelligence-release/config";import{spawnSync as t}from"node:child_process";import{join as n,resolve as r}from"node:path";import{createGraph as i,extractWorkspaceDeps as a,scanAllPackages as o,topologicalSort as s}from"@longzai-intelligence-dev-tools/dependency-graph";import*as c from"zod";function l(e){return e.map(e=>{if(typeof e==`string`)return e;if(e instanceof Error)return e.message;try{return JSON.stringify(e)}catch{return String(e)}}).join(` `)}function u(...e){process.stdout.write(`${l(e)}\n`)}const d=c.record(c.string(),c.unknown());c.record(c.string(),c.string());const f=c.object({name:c.string()}),p=async e=>{let t=Bun.file(`${e}/package.json`),n=d.safeParse(await t.json());if(!n.success)throw Error(`package.json 格式不合法: ${e}`);return n.data},m=async(e,t,n)=>{let i=`${t}/${e}/package.json`;if(await Bun.file(i).exists()){let n=await p(`${t}/${e}`),i=f.safeParse(n);return i.success?{name:i.data.name,path:r(`${t}/${e}`),workspaceDeps:a(n)}:null}let o=n.get(e);if(o){let e=await p(o),t=f.safeParse(e);return t.success?{name:t.data.name,path:r(o),workspaceDeps:a(e)}:null}return null},h={RED:`\x1B[0;31m`,GREEN:`\x1B[0;32m`,YELLOW:`\x1B[1;33m`,BLUE:`\x1B[0;34m`,RESET:`\x1B[0m`},g=`https://registry.npmjs.org/`,_=[`NPM_CONFIG_REGISTRY`,`npm_config_registry`],v=(e,t)=>[`publish`,`--access`,t,`--registry`,e.kind===`npm`?g:e.registryUrl],y=e=>{let t={};for(let[n,r]of Object.entries(e))r===void 0||_.includes(n)||(t[n]=r);return t},b=e=>e.kind===`npm`?`registry.npmjs.org(公网 npm 轨)`:`${e.registryUrl}(本地私有轨)`,x=async e=>{let t=await D(`lzi-npm-registry`,[`ensure`],e);return t.code===0||(E(`本地私有 registry 未在线且自动拉起失败`),process.stderr.write(`${t.stderr}\n`),process.stderr.write(`指引:发布仓 devDependency 添加 @longzai-intelligence-npm-registry/cli,并在仓库 .env 配置 LZI_NPM_REGISTRY_URL 与 NPM_CONFIG_TOKEN(ADR 0082)
2
- `),!1)},S=c.object({version:c.string().optional()}),C=e=>{process.stdout.write(`${h.BLUE}[INFO]${h.RESET} ${e}\n`)},w=e=>{process.stdout.write(`${h.GREEN}[SUCCESS]${h.RESET} ${e}\n`)},T=e=>{process.stdout.write(`${h.YELLOW}[WARN]${h.RESET} ${e}\n`)},E=e=>{process.stderr.write(`${h.RED}[ERROR]${h.RESET} ${e}\n`)},D=async(e,t,n,r)=>{let i=Bun.spawn([e,...t],{cwd:n,stdout:`pipe`,stderr:`pipe`,env:r}),a=await Bun.readableStreamToText(i.stdout),o=await Bun.readableStreamToText(i.stderr);return{code:await i.exited,stdout:a,stderr:o}},O=e=>{let t=i();for(let n of e)t.addNode(n.name,n);for(let n of e)for(let e of n.workspaceDeps)t.hasNode(e)&&t.addEdge(e,n.name);return s(t).map(e=>e.map(e=>e.data))},k=async e=>{C(`安装依赖...`);let t=await D(`bun`,[`install`],e);return t.code===0?(w(`依赖安装完成`),!0):(E(`安装依赖失败`),process.stdout.write(`${t.stdout}\n`),process.stderr.write(`${t.stderr}\n`),!1)},A=async e=>(C(`更新依赖...`),(await D(`bun`,[`update`],e)).code===0?(w(`依赖更新完成`),!0):(T(`更新依赖失败,继续发布`),!1)),j=async(e,t)=>{C(`构建 ${t}...`);let n=await D(`bun`,[`run`,`build:prod`],e);return n.code===0?(w(`${t} 构建成功`),!0):(E(`${t} 构建失败`),process.stdout.write(`${n.stdout}\n`),process.stderr.write(`${n.stderr}\n`),!1)},M=async(e,t,n=`public`,r={kind:`npm`})=>{if(r.kind===`local`&&!await x(e))return{result:`fail`,error:`本地私有 registry 未在线`};C(`发布 ${t} 到 ${b(r)}...`);let i=await D(`bun`,v(r,n),e,y(process.env)),a=i.stdout+i.stderr;return i.code===0?(w(`${t} 发布成功!`),{result:`success`}):a.includes(`cannot publish over the previously published versions`)?(T(`${t} 版本已存在,跳过`),{result:`skip`}):(E(`${t} 发布失败`),process.stderr.write(`${a}\n`),{result:`fail`,error:`发布失败`})},N=async e=>{try{let t=await Bun.file(n(e,`package.json`)).json(),r=S.safeParse(t);return r.success?r.data.version??`unknown`:`unknown`}catch{return`unknown`}},P=e=>{process.stdout.write(`========================================
3
- `),C(`发布摘要`),process.stdout.write(`========================================
1
+ import"./rolldown-runtime-BqT_7tdF.js";import{loadReleaseConfig as e}from"@longzai-intelligence-release/config";import{spawnSync as t}from"node:child_process";import{join as n,resolve as r}from"node:path";import{createGraph as i,extractWorkspaceDeps as a,scanAllPackages as o,topologicalSort as s}from"@longzai-intelligence-dev-tools/dependency-graph";import*as c from"zod";import{existsSync as l,readFileSync as u}from"node:fs";import{homedir as d}from"node:os";function f(e){return e.map(e=>{if(typeof e==`string`)return e;if(e instanceof Error)return e.message;try{return JSON.stringify(e)}catch{return String(e)}}).join(` `)}function p(...e){process.stdout.write(`${f(e)}\n`)}const m=c.record(c.string(),c.unknown());c.record(c.string(),c.string());const h=c.object({name:c.string()}),g=async e=>{let t=Bun.file(`${e}/package.json`),n=m.safeParse(await t.json());if(!n.success)throw Error(`package.json 格式不合法: ${e}`);return n.data},_=async(e,t,n)=>{let i=`${t}/${e}/package.json`;if(await Bun.file(i).exists()){let n=await g(`${t}/${e}`),i=h.safeParse(n);return i.success?{name:i.data.name,path:r(`${t}/${e}`),workspaceDeps:a(n)}:null}let o=n.get(e);if(o){let e=await g(o),t=h.safeParse(e);return t.success?{name:t.data.name,path:r(o),workspaceDeps:a(e)}:null}return null},v={RED:`\x1B[0;31m`,GREEN:`\x1B[0;32m`,YELLOW:`\x1B[1;33m`,BLUE:`\x1B[0;34m`,RESET:`\x1B[0m`},y=`https://registry.npmjs.org/`,b=[`NPM_CONFIG_REGISTRY`,`npm_config_registry`],x=[`cannot publish over the previously published versions`,`409 Conflict`],S=e=>x.some(t=>e.includes(t)),C=(e,t)=>[`publish`,`--access`,t,`--registry`,e.kind===`npm`?y:e.registryUrl],w=e=>{let t={};for(let[n,r]of Object.entries(e))r===void 0||b.includes(n)||(t[n]=r);return t},T=e=>e.kind===`npm`?`registry.npmjs.org(公网 npm 轨)`:`${e.registryUrl}(本地私有轨)`,E=async e=>{let t=await M(`lzi-npm-registry`,[`ensure`],e);return t.code===0||(j(`本地私有 registry 未在线且自动拉起失败`),process.stderr.write(`${t.stderr}\n`),process.stderr.write(`指引:发布仓 devDependency 添加 @longzai-intelligence-npm-registry/cli,并在仓库 .env 配置 LZI_NPM_REGISTRY_URL 与 NPM_CONFIG_TOKEN(ADR 0082)
2
+ `),!1)},D=c.object({version:c.string().optional()}),O=e=>{process.stdout.write(`${v.BLUE}[INFO]${v.RESET} ${e}\n`)},k=e=>{process.stdout.write(`${v.GREEN}[SUCCESS]${v.RESET} ${e}\n`)},A=e=>{process.stdout.write(`${v.YELLOW}[WARN]${v.RESET} ${e}\n`)},j=e=>{process.stderr.write(`${v.RED}[ERROR]${v.RESET} ${e}\n`)},M=async(e,t,n,r)=>{let i=Bun.spawn([e,...t],{cwd:n,stdout:`pipe`,stderr:`pipe`,env:r}),a=await Bun.readableStreamToText(i.stdout),o=await Bun.readableStreamToText(i.stderr);return{code:await i.exited,stdout:a,stderr:o}},N=e=>{let t=i();for(let n of e)t.addNode(n.name,n);for(let n of e)for(let e of n.workspaceDeps)t.hasNode(e)&&t.addEdge(e,n.name);return s(t).map(e=>e.map(e=>e.data))},P=async e=>{O(`安装依赖...`);let t=await M(`bun`,[`install`],e);return t.code===0?(k(`依赖安装完成`),!0):(j(`安装依赖失败`),process.stdout.write(`${t.stdout}\n`),process.stderr.write(`${t.stderr}\n`),!1)},F=async e=>(O(`更新依赖...`),(await M(`bun`,[`update`],e)).code===0?(k(`依赖更新完成`),!0):(A(`更新依赖失败,继续发布`),!1)),I=async(e,t)=>{O(`构建 ${t}...`);let n=await M(`bun`,[`run`,`build:prod`],e);return n.code===0?(k(`${t} 构建成功`),!0):(j(`${t} 构建失败`),process.stdout.write(`${n.stdout}\n`),process.stderr.write(`${n.stderr}\n`),!1)},L=async(e,t,n=`public`,r={kind:`npm`})=>{if(r.kind===`local`&&!await E(e))return{result:`fail`,error:`本地私有 registry 未在线`};O(`发布 ${t} 到 ${T(r)}...`);let i=await M(`bun`,C(r,n),e,w(process.env)),a=i.stdout+i.stderr;return i.code===0?(k(`${t} 发布成功!`),{result:`success`}):S(a)?(A(`${t} 版本已存在,跳过`),{result:`skip`}):(j(`${t} 发布失败`),process.stderr.write(`${a}\n`),a.includes(`missing authentication`)&&j(`${t} 命中 bun 侧 missing authentication:该文案与真因常脱节——优先排查包 scope 的 registry 映射(bunfig.toml [install.scopes] / .npmrc @scope:registry=,bun 优先级 scope 映射 > --registry 旗标)与本仓 scope 是否已注册,运行 bun run bun-config:generate 重新生成后再试;仍失败再核查目标 registry 凭据`),{result:`fail`,error:`发布失败`})},R=async e=>{try{let t=await Bun.file(n(e,`package.json`)).json(),r=D.safeParse(t);return r.success?r.data.version??`unknown`:`unknown`}catch{return`unknown`}},z=e=>{process.stdout.write(`========================================
3
+ `),O(`发布摘要`),process.stdout.write(`========================================
4
4
  `),process.stdout.write(`
5
- `),process.stdout.write(`总包数: ${e.total}\n`),process.stdout.write(`${h.GREEN}成功: ${e.success}${h.RESET}\n`),process.stdout.write(`${h.YELLOW}跳过: ${e.skip}${h.RESET}\n`),process.stdout.write(`${h.RED}失败: ${e.fail}${h.RESET}\n`),process.stdout.write(`
6
- `);let t=e.results.filter(e=>e.result===`skip`);if(t.length>0){T(`跳过的包:`);for(let e of t)process.stdout.write(` - ${e.name}\n`);process.stdout.write(`
7
- `)}let n=e.results.filter(e=>e.result===`fail`);if(n.length>0){E(`失败的包:`);for(let e of n)process.stderr.write(` - ${e.name}${e.error?`: ${e.error}`:``}\n`);process.stderr.write(`
8
- `)}let r=e.results.filter(e=>e.result===`success`);if(r.length>0){C(`验证发布结果:`);for(let e of r)process.stdout.write(` npm info ${e.name}\n`)}},F=`LZI_NPM_REGISTRY_URL`;function I(e){if(e===void 0)return{npm:[],local:[]};if(Array.isArray(e))return{npm:e,local:[]};if(typeof e!=`object`||!e)throw Error(`publish.packages 必须是字符串数组或 { npm, local } 对象(ADR 0082)`);let t=Object.keys(e),n=t.includes(`npm`),r=t.includes(`local`);if(!n||!r||t.length>2)throw Error(`publish.packages 对象形态必须且只能含 npm/local 两键(实际键:${t.join(`, `)||`空`};ADR 0082)`);let i=e.npm,a=e.local,o=e=>Array.isArray(e)&&e.every(e=>typeof e==`string`);if(!o(i)||!o(a))throw Error(`publish.packages 的 npm/local 键值必须是字符串数组(包名或相对路径)`);return{npm:i,local:a}}const L=e=>{let t;try{t=I(e)}catch(e){E(e instanceof Error?e.message:String(e)),process.exit(1)}t.npm.length===0&&t.local.length===0&&(E(`配置文件中没有定义要发布的包`),process.exit(1));let n=process.env[F]??``;return t.local.length>0&&n===``&&(E(`本地私有轨非空但未设置 ${F}(发布仓 .env 配置,ADR 0082)`),process.exit(1)),{tracks:t,localRegistryUrl:n}},R=async(e,t,n)=>{if(n||t.fail!==0||t.success===0)return;let r=t.results.find(e=>e.result===`success`),i=e.find(e=>e.name===r?.name);if(!i)return;let a=i.name.indexOf(`/`),o=a>=0?i.name.slice(0,a):i.name,s=await N(i.path),c=s.startsWith(`^`)||s.startsWith(`~`)?s:`^${s}`;u(``),u(`下一步:提交版本变更(commit message 参考)`),u(` release: 更新版本至 v${s}`),u(``),u(`然后:升级所有消费者并推送`),u(` bun version:upgrade-across --scope ${o} --target ${c} --dry-run`),u(` bun version:upgrade-across --scope ${o} --target ${c}`),u(` bun push-all`)},z=async e=>{await Bun.spawn([`lzi-dev-cli`,`clean`],{cwd:e,stdout:`inherit`,stderr:`inherit`}).exited!==0&&T(`lzi-dev-cli clean 执行失败(继续发布,请核查清理产物是否完整)`)},B=async n=>{let i;try{i=await e()}catch(e){E(`加载配置文件失败: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}i||(E(`未找到配置文件 lzi-release.config.ts 或 lzi-dev.config.ts`),u(``),u(`请创建配置文件,示例:`),u(`
5
+ `),process.stdout.write(`总包数: ${e.total}\n`),process.stdout.write(`${v.GREEN}成功: ${e.success}${v.RESET}\n`),process.stdout.write(`${v.YELLOW}跳过: ${e.skip}${v.RESET}\n`),process.stdout.write(`${v.RED}失败: ${e.fail}${v.RESET}\n`),process.stdout.write(`
6
+ `);let t=e.results.filter(e=>e.result===`skip`);if(t.length>0){A(`跳过的包:`);for(let e of t)process.stdout.write(` - ${e.name}\n`);process.stdout.write(`
7
+ `)}let n=e.results.filter(e=>e.result===`fail`);if(n.length>0){j(`失败的包:`);for(let e of n)process.stderr.write(` - ${e.name}${e.error?`: ${e.error}`:``}\n`);process.stderr.write(`
8
+ `)}let r=e.results.filter(e=>e.result===`success`);if(r.length>0){O(`验证发布结果:`);for(let e of r)process.stdout.write(` npm info ${e.name}\n`)}},B=e=>{let t=e.indexOf(`/`);if(!(!e.startsWith(`@`)||t<=1))return e.slice(1,t)},V=e=>e.replace(/\/+$/,``),H=e=>typeof e==`object`&&!!e&&!Array.isArray(e),U=e=>{let t;try{t=Bun.TOML.parse(e)}catch{return{}}if(!H(t))return{};let n=`install`in t?t.install:void 0;if(!H(n))return{};let r=`scopes`in n?n.scopes:void 0;if(!H(r))return{};let i={};for(let[e,t]of Object.entries(r))typeof t==`string`&&(i[e.replace(/^@+/,``)]=t);return i},W=e=>{let t=/^@([^:\s]+):registry\s*=\s*(\S+)$/,n={};for(let r of e.split(`
9
+ `)){let e=r.trim(),i=t.exec(e);i?.[1]!==void 0&&i[2]!==void 0&&!i[2].includes("${")&&(n[i[1]]=i[2])}return n},G=async e=>{let t=[{label:`bunfig.toml`,path:n(e,`bunfig.toml`)},{label:`项目 .npmrc`,path:n(e,`.npmrc`)},{label:`用户 ~/.npmrc`,path:n(d(),`.npmrc`)}],r=[];for(let e of t){if(!l(e.path))continue;let t=u(e.path,`utf8`);if(e.label===`bunfig.toml`){let n=U(t);Object.keys(n).length===0&&t.trim()!==``&&A(`bunfig.toml 解析不出 [install.scopes](可能无该段或格式异常),预检按空映射处理`),r.push({label:e.label,scopes:n})}else r.push({label:e.label,scopes:W(t)})}return r},K=e=>{let t=B(e.packageName);if(t===void 0)return[];let n=[];for(let r of e.sources){let e=r.scopes[t];e!==void 0&&n.push({label:r.label,url:e})}let r=V(e.expectedRegistryUrl),i=[];if(e.track===`npm`){if(n.length===0)return i.push(`${e.packageName} 的 scope @${t} 未注册进任何 [install.scopes] / @scope:registry 映射——bun 1.3.x 实测 scope 缺位时 --registry 旗标路径凭据解析不可靠,公网轨发布会以 missing authentication 失败;请运行 bun run bun-config:generate 重新生成(lzi-bun-cli 会兜底注入本仓 scope)`),i;for(let a of n)V(a.url)!==r&&i.push(`${e.packageName} 的 scope @${t} 在 ${a.label} 中映射到 ${a.url},按 bun 优先级(scope 映射 > --registry 旗标)发布将写入该源而非轨道目标 ${e.expectedRegistryUrl},并以 missing authentication 失败;请运行 bun run bun-config:generate 重新生成或修正 ${a.label} 的该条映射`);return i}for(let a of n)V(a.url)!==r&&i.push(`${e.packageName} 的 scope @${t} 在 ${a.label} 中映射到 ${a.url},按 bun 优先级(scope 映射 > --registry 旗标)发布将偏离本地私有轨目标 ${e.expectedRegistryUrl};请在 lzi-bun.config.ts 的 install.privateScopes 排除该 scope(ADR 0082)后运行 bun run bun-config:generate 重新生成`);return i},q=async e=>{O(`发布 registry 预检(scope 映射优先级防线)...`);let t=await G(e.rootDir),n=[];for(let r of e.manifests){let i=e.trackByName.get(r.name)??`npm`;n.push(...K({packageName:r.name,track:i,expectedRegistryUrl:i===`local`?e.localRegistryUrl:y,sources:t}))}if(n.length>0){j(`发布 registry 预检失败(${n.length} 项违规,均为发布目标被静默改写或 scope 缺位的风险):`);for(let e of n)j(` - ${e}`);return!1}return O(`发布 registry 预检通过(scope 映射与轨道声明一致)`),!0},J=`LZI_NPM_REGISTRY_URL`;function Y(e){if(e===void 0)return{npm:[],local:[]};if(Array.isArray(e))return{npm:e,local:[]};if(typeof e!=`object`||!e)throw Error(`publish.packages 必须是字符串数组或 { npm, local } 对象(ADR 0082)`);let t=Object.keys(e),n=t.includes(`npm`),r=t.includes(`local`);if(!n||!r||t.length>2)throw Error(`publish.packages 对象形态必须且只能含 npm/local 两键(实际键:${t.join(`, `)||`空`};ADR 0082)`);let i=e.npm,a=e.local,o=e=>Array.isArray(e)&&e.every(e=>typeof e==`string`);if(!o(i)||!o(a))throw Error(`publish.packages 的 npm/local 键值必须是字符串数组(包名或相对路径)`);return{npm:i,local:a}}const X=e=>{let t;try{t=Y(e)}catch(e){j(e instanceof Error?e.message:String(e)),process.exit(1)}t.npm.length===0&&t.local.length===0&&(j(`配置文件中没有定义要发布的包`),process.exit(1));let n=process.env[J]??``;return t.local.length>0&&n===``&&(j(`本地私有轨非空但未设置 ${J}(发布仓 .env 配置,ADR 0082)`),process.exit(1)),{tracks:t,localRegistryUrl:n}},Z=async(e,t,n)=>{if(n||t.fail!==0||t.success===0)return;let r=t.results.find(e=>e.result===`success`),i=e.find(e=>e.name===r?.name);if(!i)return;let a=i.name.indexOf(`/`),o=a>=0?i.name.slice(0,a):i.name,s=await R(i.path),c=s.startsWith(`^`)||s.startsWith(`~`)?s:`^${s}`;p(``),p(`下一步:提交版本变更(commit message 参考)`),p(` release: 更新版本至 v${s}`),p(``),p(`然后:升级所有消费者并推送`),p(` bun version:upgrade-across --scope ${o} --target ${c} --dry-run`),p(` bun version:upgrade-across --scope ${o} --target ${c}`),p(` bun push-all`)},Q=async e=>{await q(e)||process.exit(1)},$=async e=>{await Bun.spawn([`lzi-dev-cli`,`clean`],{cwd:e,stdout:`inherit`,stderr:`inherit`}).exited!==0&&A(`lzi-dev-cli clean 执行失败(继续发布,请核查清理产物是否完整)`)},ee=async n=>{let i;try{i=await e()}catch(e){j(`加载配置文件失败: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}i||(j(`未找到配置文件 lzi-release.config.ts 或 lzi-dev.config.ts`),p(``),p(`请创建配置文件,示例:`),p(`
9
10
  import { defineConfig } from '@longzai-intelligence-release/config';
10
11
 
11
12
  export default defineConfig({
@@ -13,4 +14,4 @@ export default defineConfig({
13
14
  packages: ['apps/cli'],
14
15
  },
15
16
  });
16
- `),process.exit(1)),i.source===`legacy-dev-tools`&&T(`发布配置读取自 lzi-dev.config.ts 的 publish 字段(发布命令的兼容回退;该字段仍被 release log/version 域消费,请勿删除)。如需发布域专属清单可另建 lzi-release.config.ts,两处声明需保持一致(ADR 0087)`);let{tracks:a,localRegistryUrl:s}=L(i.config.publish?.packages),c=r(i.configPath,`..`),l=i.config.publish;l===void 0&&(E(`配置文件中没有定义要发布的包`),process.exit(1));let d=l.access??`public`,f=l.installDeps??!0,p=l.buildBeforePublish??!0,h=l.cleanBeforePublish??!0,g=n?.dryRun??!1,_=await o(c),v=/* @__PURE__ */ new Map,y=[];for(let[e,t]of[[`npm`,a.npm],[`local`,a.local]])for(let n of t){let t=await m(n,c,_);t||(E(`未找到包: ${n}`),process.exit(1)),v.has(t.name)&&(E(`包 ${t.name} 在 publish.packages 中重复声明`),process.exit(1)),v.set(t.name,e),y.push(t)}let x=e=>v.get(e)===`local`?{kind:`local`,registryUrl:s}:{kind:`npm`},S=e=>b(x(e)),w=O(y);if(g){C(`[dry-run] 预览模式,不会实际安装、构建或发布`),C(`[dry-run] 发布顺序(含目标 registry,发布前核对轨道,ADR 0082):`);for(let e=0;e<w.length;e++){let t=w[e]??[];for(let n of t)C(`[dry-run] 第 ${e+1} 层: ${n.name} → ${S(n.name)}`)}u(``)}let D={total:y.length,success:0,skip:0,fail:0,results:[]};C(`开始发布流程`),C(`基础路径: ${c}`),u(``);let F=n?.cleanRunner??(async()=>z(c));h&&!g?(C(`发布前清理构建产物`),u(``),await F({dryRun:!1}),u(``)):h&&g&&(C(`[dry-run] 跳过发布前清理`),u(``)),f&&!g?(await k(c)||(E(`安装依赖失败,终止发布`),process.exit(1)),u(``)):f&&g&&(C(`[dry-run] 跳过安装依赖`),u(``)),l.preBuildHook&&!g?(C(`执行 preBuildHook`),t(l.preBuildHook,{cwd:c,stdio:`inherit`,shell:!0}).status!==0&&(E(`preBuildHook 执行失败,终止发布`),process.exit(1)),u(``)):l.preBuildHook&&g&&(C(`[dry-run] 跳过 preBuildHook`),u(``)),u(`========================================`),u(``);for(let e=0;e<w.length;e++){let t=w[e]??[];if(t.length!==0){C(`【第 ${e+1} 层】发布 ${t.length} 个包...`),u(``);for(let e of t){let t=x(e.name);C(`开始发布: ${e.name}`),C(`路径: ${e.path}`),C(`目标: ${b(t)}`);let n=await N(e.path);if(C(`版本: ${n}`),p&&!g){if(!await j(e.path,e.name)){D.fail++,D.results.push({name:e.name,result:`fail`,error:`构建失败`}),u(``);continue}}else p&&g&&C(`[dry-run] 跳过构建 ${e.name}`);if(g)C(`[dry-run] 将发布 ${e.name}@${n} → ${b(t)}`),D.results.push({name:e.name,result:`success`}),D.success++;else{let n=await M(e.path,e.name,d,t);switch(D.results.push({name:e.name,result:n.result,error:n.error}),n.result){case`success`:D.success++;break;case`skip`:D.skip++;break;case`fail`:D.fail++}}u(``)}e<w.length-1&&(g?C(`[dry-run] 跳过更新依赖`):await A(c),u(``))}}P(D),await R(y,D,g),D.fail>0&&process.exit(1)};export{g as NPM_OFFICIAL_REGISTRY_URL,j as buildPackage,v as buildPublishArgs,y as buildPublishEnv,b as describePublishTarget,x as ensureLocalRegistry,N as getPackageVersion,k as installDependencies,E as logError,C as logInfo,w as logSuccess,T as logWarn,I as normalizePublishTracks,P as printSummary,B as publishCommand,M as publishPackage,p as readPackageJson,m as resolvePackageManifest,o as scanAllPackages,O as topologicalSort,A as updateDependencies};
17
+ `),process.exit(1)),i.source===`legacy-dev-tools`&&A(`发布配置读取自 lzi-dev.config.ts 的 publish 字段(发布命令的兼容回退;该字段仍被 release log/version 域消费,请勿删除)。如需发布域专属清单可另建 lzi-release.config.ts,两处声明需保持一致(ADR 0087)`);let{tracks:a,localRegistryUrl:s}=X(i.config.publish?.packages),c=r(i.configPath,`..`),l=i.config.publish;l===void 0&&(j(`配置文件中没有定义要发布的包`),process.exit(1));let u=l.access??`public`,d=l.installDeps??!0,f=l.buildBeforePublish??!0,m=l.cleanBeforePublish??!0,h=n?.dryRun??!1,g=await o(c),v=/* @__PURE__ */ new Map,y=[];for(let[e,t]of[[`npm`,a.npm],[`local`,a.local]])for(let n of t){let t=await _(n,c,g);t||(j(`未找到包: ${n}`),process.exit(1)),v.has(t.name)&&(j(`包 ${t.name} 在 publish.packages 中重复声明`),process.exit(1)),v.set(t.name,e),y.push(t)}let b=e=>v.get(e)===`local`?{kind:`local`,registryUrl:s}:{kind:`npm`},x=e=>T(b(e));await Q({manifests:y,trackByName:v,localRegistryUrl:s,rootDir:c});let S=N(y);if(h){O(`[dry-run] 预览模式,不会实际安装、构建或发布`),O(`[dry-run] 发布顺序(含目标 registry,发布前核对轨道,ADR 0082):`);for(let e=0;e<S.length;e++){let t=S[e]??[];for(let n of t)O(`[dry-run] 第 ${e+1} 层: ${n.name} → ${x(n.name)}`)}p(``)}let C={total:y.length,success:0,skip:0,fail:0,results:[]};O(`开始发布流程`),O(`基础路径: ${c}`),p(``);let w=n?.cleanRunner??(async()=>$(c));m&&!h?(O(`发布前清理构建产物`),p(``),await w({dryRun:!1}),p(``)):m&&h&&(O(`[dry-run] 跳过发布前清理`),p(``)),d&&!h?(await P(c)||(j(`安装依赖失败,终止发布`),process.exit(1)),p(``)):d&&h&&(O(`[dry-run] 跳过安装依赖`),p(``)),l.preBuildHook&&!h?(O(`执行 preBuildHook`),t(l.preBuildHook,{cwd:c,stdio:`inherit`,shell:!0}).status!==0&&(j(`preBuildHook 执行失败,终止发布`),process.exit(1)),p(``)):l.preBuildHook&&h&&(O(`[dry-run] 跳过 preBuildHook`),p(``)),p(`========================================`),p(``);for(let e=0;e<S.length;e++){let t=S[e]??[];if(t.length!==0){O(`【第 ${e+1} 层】发布 ${t.length} 个包...`),p(``);for(let e of t){let t=b(e.name);O(`开始发布: ${e.name}`),O(`路径: ${e.path}`),O(`目标: ${T(t)}`);let n=await R(e.path);if(O(`版本: ${n}`),f&&!h){if(!await I(e.path,e.name)){C.fail++,C.results.push({name:e.name,result:`fail`,error:`构建失败`}),p(``);continue}}else f&&h&&O(`[dry-run] 跳过构建 ${e.name}`);if(h)O(`[dry-run] 将发布 ${e.name}@${n} → ${T(t)}`),C.results.push({name:e.name,result:`success`}),C.success++;else{let n=await L(e.path,e.name,u,t);switch(C.results.push({name:e.name,result:n.result,error:n.error}),n.result){case`success`:C.success++;break;case`skip`:C.skip++;break;case`fail`:C.fail++}}p(``)}e<S.length-1&&(h?O(`[dry-run] 跳过更新依赖`):await F(c),p(``))}}z(C),await Z(y,C,h),C.fail>0&&process.exit(1)};export{y as NPM_OFFICIAL_REGISTRY_URL,I as buildPackage,C as buildPublishArgs,w as buildPublishEnv,K as checkScopeRegistryForPublish,T as describePublishTarget,E as ensureLocalRegistry,B as extractPackageScope,R as getPackageVersion,P as installDependencies,G as loadScopeRegistrySources,j as logError,O as logInfo,k as logSuccess,A as logWarn,Y as normalizePublishTracks,V as normalizeRegistryUrl,U as parseBunfigScopeRegistries,W as parseNpmrcScopeRegistries,z as printSummary,ee as publishCommand,L as publishPackage,g as readPackageJson,_ as resolvePackageManifest,q as runPublishRegistryPreflight,o as scanAllPackages,N as topologicalSort,F as updateDependencies};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longzai-intelligence-release/core",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Longzai Intelligence 发布业务核心实现(双轨归一化、依赖拓扑分层、构建与发布管线)",
5
5
  "license": "UNLICENSED",
6
6
  "files": [
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@longzai-intelligence-dev-tools/dependency-graph": "^0.2.29",
35
- "@longzai-intelligence-release/config": "0.0.1",
35
+ "@longzai-intelligence-release/config": "0.0.2",
36
36
  "zod": "^4.4.3"
37
37
  }
38
38
  }