@longzai-intelligence-release/core 0.0.4 → 0.0.5
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 +442 -2
- package/dist/index.js +9 -9
- package/package.json +10 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "./rolldown-runtime-BqT_7tdF.js";
|
|
2
|
-
import "
|
|
2
|
+
import { ContentGateConfig } from "@longzai-intelligence-release/config";
|
|
3
3
|
import { PackageManifest, scanAllPackages } from "@longzai-intelligence-dev-tools/dependency-graph";
|
|
4
|
+
import "zod";
|
|
4
5
|
//#region src/commands/publish.command.d.ts
|
|
5
6
|
/**
|
|
6
7
|
* publish 命令实现
|
|
@@ -76,6 +77,55 @@ declare function normalizePublishTracks(packages: PublishPackagesInput): Normali
|
|
|
76
77
|
*/
|
|
77
78
|
declare const publishCommand: (options?: PublishOptions) => Promise<void>;
|
|
78
79
|
//#endregion
|
|
80
|
+
//#region src/commands/check.command.d.ts
|
|
81
|
+
/**
|
|
82
|
+
* check 命令实现——发布内容门禁巡检(ADR 0114)
|
|
83
|
+
*
|
|
84
|
+
* `lzi-release check` 对当前仓执行 R1 至 R4 静态检查与棘轮基线折叠,`--published`
|
|
85
|
+
* 追加 R5 registry 存量审计,`--write-baseline --issue <指针>` 再生基线。
|
|
86
|
+
* 退出码为 error 级未豁免违例数(`--strict` 计 warn),`--published` 模式下
|
|
87
|
+
* 每个泄露包另计一条
|
|
88
|
+
*/
|
|
89
|
+
/**
|
|
90
|
+
* check 命令选项
|
|
91
|
+
*/
|
|
92
|
+
type CheckCommandOptions = {
|
|
93
|
+
/**
|
|
94
|
+
* 输出格式(text 人读、json 可重入消费)
|
|
95
|
+
*
|
|
96
|
+
* @default 'text'
|
|
97
|
+
*/
|
|
98
|
+
format?: 'text' | 'json';
|
|
99
|
+
/**
|
|
100
|
+
* 是否把 warn 级违例计入退出码(ADR 0112 `--strict` 语义)
|
|
101
|
+
*
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
strict?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* 是否追加 R5 registry 存量审计(拉取已发布 tarball 清单比对白名单)
|
|
107
|
+
*
|
|
108
|
+
* @default false
|
|
109
|
+
*/
|
|
110
|
+
published?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* 是否再生基线(以当前全部违例全量重写,须携带 `issue` 指针)
|
|
113
|
+
*
|
|
114
|
+
* @default false
|
|
115
|
+
*/
|
|
116
|
+
writeBaseline?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* 收编 issue 指针(如 `0001-package-leak`,`writeBaseline` 开启时必填)
|
|
119
|
+
*/
|
|
120
|
+
issue?: string;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* 执行 check 命令
|
|
124
|
+
*
|
|
125
|
+
* @param options - 命令选项
|
|
126
|
+
*/
|
|
127
|
+
declare const checkCommand: (options?: CheckCommandOptions) => Promise<void>;
|
|
128
|
+
//#endregion
|
|
79
129
|
//#region src/utils/package-resolver.utils.d.ts
|
|
80
130
|
/**
|
|
81
131
|
* 读取 package.json
|
|
@@ -440,4 +490,394 @@ type PublishRegistryPreflightInput = {
|
|
|
440
490
|
*/
|
|
441
491
|
declare const runPublishRegistryPreflight: (input: PublishRegistryPreflightInput) => Promise<boolean>;
|
|
442
492
|
//#endregion
|
|
443
|
-
|
|
493
|
+
//#region src/content-gate/types.check.d.ts
|
|
494
|
+
/**
|
|
495
|
+
* 发布内容门禁类型定义(ADR 0114)
|
|
496
|
+
*
|
|
497
|
+
* 违例、报告与基线的共享契约——规则引擎、基线棘轮、registry 审计与报告
|
|
498
|
+
* 输出各模块经本契约衔接,CLI 与发布闸门共用同一套产物
|
|
499
|
+
*/
|
|
500
|
+
/**
|
|
501
|
+
* 门禁规则标识(语义正本归 ADR 0012 与 ADR 0114,工具只做编码)
|
|
502
|
+
*
|
|
503
|
+
* - `files-missing`:非 private 包未声明 `files` 白名单(R1)
|
|
504
|
+
* - `npmignore-present`:包目录存在 `.npmignore`(R1,黑名单模式已被 ADR 0012 备选 A 弃用)
|
|
505
|
+
* - `files-forbidden-entry`:`files` 含禁止目录或未登记的 `src`(R2)
|
|
506
|
+
* - `build-scripts-missing`:构建发布包缺 `build:prod` 或 `prepublishOnly`(R3)
|
|
507
|
+
* - `license-not-unlicensed`:`license` 非 `UNLICENSED`(R4)
|
|
508
|
+
* - `published-tarball-leak`:已发布 tarball 含白名单外内容(R5,仅 `--published` 审计模式)
|
|
509
|
+
*/
|
|
510
|
+
type CheckRule = 'files-missing' | 'npmignore-present' | 'files-forbidden-entry' | 'build-scripts-missing' | 'license-not-unlicensed' | 'published-tarball-leak';
|
|
511
|
+
/**
|
|
512
|
+
* 违例严重级(沿 ADR 0112 语义——error 计入退出码,warn 仅 `--strict` 时计入)
|
|
513
|
+
*/
|
|
514
|
+
type CheckSeverity = 'error' | 'warn';
|
|
515
|
+
/**
|
|
516
|
+
* 单条违例
|
|
517
|
+
*/
|
|
518
|
+
type CheckViolation = {
|
|
519
|
+
/**
|
|
520
|
+
* 违例包全名
|
|
521
|
+
*/
|
|
522
|
+
readonly packageName: string;
|
|
523
|
+
/**
|
|
524
|
+
* 包目录绝对路径
|
|
525
|
+
*/
|
|
526
|
+
readonly packagePath: string;
|
|
527
|
+
/**
|
|
528
|
+
* 命中的规则标识
|
|
529
|
+
*/
|
|
530
|
+
readonly rule: CheckRule;
|
|
531
|
+
/**
|
|
532
|
+
* 违例严重级
|
|
533
|
+
*/
|
|
534
|
+
readonly severity: CheckSeverity;
|
|
535
|
+
/**
|
|
536
|
+
* 违例说明(含修复指引)
|
|
537
|
+
*/
|
|
538
|
+
readonly message: string;
|
|
539
|
+
/**
|
|
540
|
+
* 命中规则的对象明细(如 `files` 违例条目、缺失脚本名、泄露文件样例)
|
|
541
|
+
*/
|
|
542
|
+
readonly details?: readonly string[];
|
|
543
|
+
};
|
|
544
|
+
/**
|
|
545
|
+
* 检查汇总结果
|
|
546
|
+
*/
|
|
547
|
+
type CheckSummary = {
|
|
548
|
+
/**
|
|
549
|
+
* 受检包总数(排除 private 与 `exclude` 登记后)
|
|
550
|
+
*/
|
|
551
|
+
readonly checkedPackages: number;
|
|
552
|
+
/**
|
|
553
|
+
* 受检包名清单(供 R5 registry 审计取同口径范围——含无违例的包)
|
|
554
|
+
*/
|
|
555
|
+
readonly checkedPackageNames: readonly string[];
|
|
556
|
+
/**
|
|
557
|
+
* 全部违例(含被基线豁免的条目,豁免标记见各条目)
|
|
558
|
+
*/
|
|
559
|
+
readonly violations: readonly CheckViolation[];
|
|
560
|
+
/**
|
|
561
|
+
* 被基线豁免的(包名、规则)对集合
|
|
562
|
+
*/
|
|
563
|
+
readonly exemptedKeys: ReadonlySet<string>;
|
|
564
|
+
/**
|
|
565
|
+
* error 级未豁免违例数(退出码口径)
|
|
566
|
+
*/
|
|
567
|
+
readonly errorCount: number;
|
|
568
|
+
/**
|
|
569
|
+
* warn 级未豁免违例数(`--strict` 时计入退出码)
|
|
570
|
+
*/
|
|
571
|
+
readonly warnCount: number;
|
|
572
|
+
};
|
|
573
|
+
/**
|
|
574
|
+
* 基线条目(棘轮语义沿 lzi-issues 基线纪律——只许缩短直至清零)
|
|
575
|
+
*/
|
|
576
|
+
type BaselineEntry = {
|
|
577
|
+
/**
|
|
578
|
+
* 豁免包全名
|
|
579
|
+
*/
|
|
580
|
+
readonly packageName: string;
|
|
581
|
+
/**
|
|
582
|
+
* 豁免规则标识
|
|
583
|
+
*/
|
|
584
|
+
readonly rule: CheckRule;
|
|
585
|
+
/**
|
|
586
|
+
* 收编 issue 指针(如 `0001-package-leak`,缺指针的基线条目视为无效)
|
|
587
|
+
*/
|
|
588
|
+
readonly issue: string;
|
|
589
|
+
/**
|
|
590
|
+
* 登记日期(ISO 日期串)
|
|
591
|
+
*/
|
|
592
|
+
readonly recordedAt: string;
|
|
593
|
+
};
|
|
594
|
+
/**
|
|
595
|
+
* 基线文件形态(`.lzi/release/baseline.json`)
|
|
596
|
+
*/
|
|
597
|
+
type CheckBaseline = {
|
|
598
|
+
/**
|
|
599
|
+
* 基线条目清单
|
|
600
|
+
*/
|
|
601
|
+
readonly entries: readonly BaselineEntry[];
|
|
602
|
+
};
|
|
603
|
+
/**
|
|
604
|
+
* registry 审计的单包泄露报告(R5)
|
|
605
|
+
*/
|
|
606
|
+
type PublishedLeakReport = {
|
|
607
|
+
/**
|
|
608
|
+
* 包全名
|
|
609
|
+
*/
|
|
610
|
+
readonly packageName: string;
|
|
611
|
+
/**
|
|
612
|
+
* 审计的版本号清单(缺省为全部已发布版本)
|
|
613
|
+
*/
|
|
614
|
+
readonly versions: readonly {
|
|
615
|
+
/**
|
|
616
|
+
* 版本号
|
|
617
|
+
*/
|
|
618
|
+
readonly version: string;
|
|
619
|
+
/**
|
|
620
|
+
* tarball 内白名单外文件样例(最多十条)
|
|
621
|
+
*/
|
|
622
|
+
readonly leakedFiles: readonly string[];
|
|
623
|
+
/**
|
|
624
|
+
* 白名单外文件总数
|
|
625
|
+
*/
|
|
626
|
+
readonly leakedCount: number;
|
|
627
|
+
}[];
|
|
628
|
+
};
|
|
629
|
+
/**
|
|
630
|
+
* 基线豁免键的构造规则(包名与规则号的拼接口径)
|
|
631
|
+
*
|
|
632
|
+
* @param packageName - 包全名
|
|
633
|
+
* @param rule - 规则标识
|
|
634
|
+
* @returns 豁免键
|
|
635
|
+
*/
|
|
636
|
+
declare const buildExemptKey: (packageName: string, rule: CheckRule) => string;
|
|
637
|
+
//#endregion
|
|
638
|
+
//#region src/content-gate/rules.check.d.ts
|
|
639
|
+
/**
|
|
640
|
+
* 单包检查入参
|
|
641
|
+
*/
|
|
642
|
+
type CheckPackageInput = {
|
|
643
|
+
/**
|
|
644
|
+
* 包全名
|
|
645
|
+
*/
|
|
646
|
+
readonly packageName: string;
|
|
647
|
+
/**
|
|
648
|
+
* 包目录绝对路径
|
|
649
|
+
*/
|
|
650
|
+
readonly packagePath: string;
|
|
651
|
+
/**
|
|
652
|
+
* 解析后的 package.json 对象
|
|
653
|
+
*/
|
|
654
|
+
readonly pkg: Record<string, unknown>;
|
|
655
|
+
};
|
|
656
|
+
/**
|
|
657
|
+
* 判定包是否为 private 包(private 包不参与发布,跳过全部规则)
|
|
658
|
+
*
|
|
659
|
+
* @param pkg - package.json 对象
|
|
660
|
+
* @returns 是 private 包返回 true
|
|
661
|
+
*/
|
|
662
|
+
declare const isPrivatePackage: (pkg: Record<string, unknown>) => boolean;
|
|
663
|
+
/**
|
|
664
|
+
* 判定包是否经配置登记为源码发布包(ADR 0012 源码发布包判定)
|
|
665
|
+
*
|
|
666
|
+
* @param packageName - 包全名
|
|
667
|
+
* @param gate - 内容门禁配置(`sourcePackages` 登记清单)
|
|
668
|
+
* @returns 已登记返回 true
|
|
669
|
+
*/
|
|
670
|
+
declare const isSourcePackage: (packageName: string, gate: ContentGateConfig | undefined) => boolean;
|
|
671
|
+
/**
|
|
672
|
+
* 提取 `files` 条目的顶层路径段(剥离开头 `!` 否定符与 glob 魔法字符)
|
|
673
|
+
*
|
|
674
|
+
* 形如 src 目录递归 json 的 glob 条目归一到 `src`、否定条目归一到其首段——
|
|
675
|
+
* 白名单判定只关心首段目录归属
|
|
676
|
+
*
|
|
677
|
+
* @param entry - `files` 数组单条
|
|
678
|
+
* @returns 顶层路径段(空串表示无有效段)
|
|
679
|
+
*/
|
|
680
|
+
declare const extractTopSegment: (entry: string) => string;
|
|
681
|
+
/**
|
|
682
|
+
* 对单个包执行 R1 至 R4 静态检查
|
|
683
|
+
*
|
|
684
|
+
* @param input - 单包检查入参
|
|
685
|
+
* @param gate - 内容门禁配置
|
|
686
|
+
* @returns 违例清单(该包无违例时为空数组)
|
|
687
|
+
*/
|
|
688
|
+
declare const checkPackageRules: (input: CheckPackageInput, gate: ContentGateConfig | undefined) => CheckViolation[];
|
|
689
|
+
//#endregion
|
|
690
|
+
//#region src/content-gate/baseline.check.d.ts
|
|
691
|
+
/**
|
|
692
|
+
* 基线读取结果
|
|
693
|
+
*/
|
|
694
|
+
type LoadBaselineResult = {
|
|
695
|
+
/**
|
|
696
|
+
* 基线对象(文件缺失或损坏时为空基线)
|
|
697
|
+
*/
|
|
698
|
+
baseline: CheckBaseline;
|
|
699
|
+
/**
|
|
700
|
+
* 无效条目原文(缺 `issue` 指针等形态不合法的条目,供人工核查)
|
|
701
|
+
*/
|
|
702
|
+
invalidEntries: unknown[];
|
|
703
|
+
/**
|
|
704
|
+
* 基线文件绝对路径
|
|
705
|
+
*/
|
|
706
|
+
path: string;
|
|
707
|
+
};
|
|
708
|
+
/**
|
|
709
|
+
* 读取基线(文件缺失返回空基线,不阻断检查——接入期宽限)
|
|
710
|
+
*
|
|
711
|
+
* @param rootDir - monorepo 根目录路径
|
|
712
|
+
* @returns 基线对象与读取诊断信息
|
|
713
|
+
*/
|
|
714
|
+
declare const loadBaseline: (rootDir: string) => LoadBaselineResult;
|
|
715
|
+
/**
|
|
716
|
+
* 构造基线豁免键集合
|
|
717
|
+
*
|
|
718
|
+
* @param baseline - 基线对象
|
|
719
|
+
* @returns (包名、规则)豁免键集合
|
|
720
|
+
*/
|
|
721
|
+
declare const buildExemptKeySet: (baseline: CheckBaseline) => Set<string>;
|
|
722
|
+
/**
|
|
723
|
+
* 写入基线(`--write-baseline` 再生通道)
|
|
724
|
+
*
|
|
725
|
+
* 以当前违例全量重写基线文件,全部条目挂同一收编 issue 指针;
|
|
726
|
+
* 目录不存在时一并创建
|
|
727
|
+
*
|
|
728
|
+
* @param rootDir - monorepo 根目录路径
|
|
729
|
+
* @param entries - 基线条目清单
|
|
730
|
+
*/
|
|
731
|
+
declare const writeBaseline: (rootDir: string, entries: readonly BaselineEntry[]) => void;
|
|
732
|
+
/**
|
|
733
|
+
* 计算基线棘轮缩短面——已登记豁免但实际违例已消失的条目
|
|
734
|
+
*
|
|
735
|
+
* 棘轮纪律的执法口径——该清单非空时提示手工删除对应条目,基线只许缩短
|
|
736
|
+
*
|
|
737
|
+
* @param baseline - 基线对象
|
|
738
|
+
* @param activeViolationKeys - 当前实际违例的(包名、规则)键集合
|
|
739
|
+
* @returns 可缩短的基线条目清单
|
|
740
|
+
*/
|
|
741
|
+
declare const findShrinkableEntries: (baseline: CheckBaseline, activeViolationKeys: ReadonlySet<string>) => BaselineEntry[];
|
|
742
|
+
//#endregion
|
|
743
|
+
//#region src/content-gate/published.check.d.ts
|
|
744
|
+
/**
|
|
745
|
+
* registry 审计入参
|
|
746
|
+
*/
|
|
747
|
+
type PublishedAuditInput = {
|
|
748
|
+
/**
|
|
749
|
+
* 待审计的包名与目录对(通常为静态检查的全部受检包)
|
|
750
|
+
*/
|
|
751
|
+
readonly packages: readonly {
|
|
752
|
+
/**
|
|
753
|
+
* 包全名
|
|
754
|
+
*/
|
|
755
|
+
readonly name: string;
|
|
756
|
+
}[];
|
|
757
|
+
/**
|
|
758
|
+
* 内容门禁配置(`sourcePackages` 登记)
|
|
759
|
+
*/
|
|
760
|
+
readonly gate: ContentGateConfig | undefined;
|
|
761
|
+
/**
|
|
762
|
+
* registry 基址
|
|
763
|
+
*/
|
|
764
|
+
readonly registryUrl?: string;
|
|
765
|
+
};
|
|
766
|
+
/**
|
|
767
|
+
* 执行 registry 存量审计(R5)
|
|
768
|
+
*
|
|
769
|
+
* 逐包拉取全部已发布版本的 tarball 清单并比对白名单,输出泄露明细——
|
|
770
|
+
* 该模式只报告不拦截,面向存量清查与定期巡检
|
|
771
|
+
*
|
|
772
|
+
* @param input - 审计入参
|
|
773
|
+
* @returns 泄露报告清单(仅含有泄露版本的包)
|
|
774
|
+
*/
|
|
775
|
+
declare const auditPublishedTarballs: (input: PublishedAuditInput) => Promise<PublishedLeakReport[]>;
|
|
776
|
+
//#endregion
|
|
777
|
+
//#region src/content-gate/report.check.d.ts
|
|
778
|
+
/**
|
|
779
|
+
* 违例计数口径的入参形态
|
|
780
|
+
*/
|
|
781
|
+
type CountInput = {
|
|
782
|
+
/**
|
|
783
|
+
* 检查汇总结果
|
|
784
|
+
*/
|
|
785
|
+
readonly summary: CheckSummary;
|
|
786
|
+
/**
|
|
787
|
+
* 是否 `--strict`(warn 计入退出码)
|
|
788
|
+
*/
|
|
789
|
+
readonly strict: boolean;
|
|
790
|
+
};
|
|
791
|
+
/**
|
|
792
|
+
* 计算退出码口径的违例总数
|
|
793
|
+
*
|
|
794
|
+
* @param input - 计数入参
|
|
795
|
+
* @returns 退出码(未豁免违例数,`--strict` 时含 warn)
|
|
796
|
+
*/
|
|
797
|
+
declare const countExitCode: (input: CountInput) => number;
|
|
798
|
+
/**
|
|
799
|
+
* 输出 text 格式报告行
|
|
800
|
+
*
|
|
801
|
+
* 按包分组列出未豁免违例,基线豁免条目单独汇总一行不展开
|
|
802
|
+
*
|
|
803
|
+
* @param summary - 检查汇总结果
|
|
804
|
+
* @returns text 报告行数组(调用方负责落 stdout)
|
|
805
|
+
*/
|
|
806
|
+
declare const renderTextReport: (summary: CheckSummary) => string[];
|
|
807
|
+
/**
|
|
808
|
+
* 渲染 json 格式报告(`--format json` 可重入消费载体)
|
|
809
|
+
*
|
|
810
|
+
* @param summary - 检查汇总结果
|
|
811
|
+
* @returns json 对象(调用方负责序列化落 stdout)
|
|
812
|
+
*/
|
|
813
|
+
declare const renderJsonReport: (summary: CheckSummary) => object;
|
|
814
|
+
//#endregion
|
|
815
|
+
//#region src/content-gate/publish-content-gate.check.d.ts
|
|
816
|
+
/**
|
|
817
|
+
* 内容门禁执行入参
|
|
818
|
+
*/
|
|
819
|
+
type RunContentCheckInput = {
|
|
820
|
+
/**
|
|
821
|
+
* monorepo 根目录路径
|
|
822
|
+
*/
|
|
823
|
+
readonly rootDir: string;
|
|
824
|
+
/**
|
|
825
|
+
* 内容门禁配置(来自 `lzi-release.config.ts` 的 `publish.contentGate` 段)
|
|
826
|
+
*/
|
|
827
|
+
readonly gate: ContentGateConfig | undefined;
|
|
828
|
+
/**
|
|
829
|
+
* 显式受检包清单(发布闸门传 manifests,缺省扫描全 workspace)
|
|
830
|
+
*/
|
|
831
|
+
readonly packages?: readonly {
|
|
832
|
+
/**
|
|
833
|
+
* 包全名
|
|
834
|
+
*/
|
|
835
|
+
readonly name: string;
|
|
836
|
+
/**
|
|
837
|
+
* 包目录绝对路径
|
|
838
|
+
*/
|
|
839
|
+
readonly path: string;
|
|
840
|
+
}[];
|
|
841
|
+
};
|
|
842
|
+
/**
|
|
843
|
+
* 执行发布内容门禁检查(R1 至 R4 + 棘轮基线折叠)
|
|
844
|
+
*
|
|
845
|
+
* @param input - 门禁执行入参
|
|
846
|
+
* @returns 检查汇总结果
|
|
847
|
+
*/
|
|
848
|
+
declare const runPublishContentCheck: (input: RunContentCheckInput) => Promise<CheckSummary>;
|
|
849
|
+
/**
|
|
850
|
+
* 供 `--write-baseline` 通道取当前违例键集合的辅助(棘轮再生与缩短检查共用)
|
|
851
|
+
*
|
|
852
|
+
* @param summary - 检查汇总结果
|
|
853
|
+
* @returns 当前全部违例的(包名、规则)键集合
|
|
854
|
+
*/
|
|
855
|
+
declare const activeViolationKeySet: (summary: CheckSummary) => Set<string>;
|
|
856
|
+
//#endregion
|
|
857
|
+
//#region src/content-gate/preflight.check.d.ts
|
|
858
|
+
/**
|
|
859
|
+
* 发布内容门禁闸门入参
|
|
860
|
+
*/
|
|
861
|
+
type PublishContentPreflightInput = {
|
|
862
|
+
/**
|
|
863
|
+
* 发布清单(manifests)
|
|
864
|
+
*/
|
|
865
|
+
readonly manifests: PackageManifest[];
|
|
866
|
+
/**
|
|
867
|
+
* 内容门禁配置(`publish.contentGate` 段)
|
|
868
|
+
*/
|
|
869
|
+
readonly gate: ContentGateConfig | undefined;
|
|
870
|
+
/**
|
|
871
|
+
* monorepo 根目录路径
|
|
872
|
+
*/
|
|
873
|
+
readonly rootDir: string;
|
|
874
|
+
};
|
|
875
|
+
/**
|
|
876
|
+
* 执行发布内容门禁闸门检查
|
|
877
|
+
*
|
|
878
|
+
* @param input - 闸门入参(发布清单、内容门禁配置、仓根目录)
|
|
879
|
+
* @returns 全部放行返回 true,存在未豁免 error 级违例返回 false(明细已打印)
|
|
880
|
+
*/
|
|
881
|
+
declare const runPublishContentPreflight: (input: PublishContentPreflightInput) => Promise<boolean>;
|
|
882
|
+
//#endregion
|
|
883
|
+
export { type BaselineEntry, type CheckBaseline, type CheckCommandOptions, type CheckPackageInput, type CheckRule, type CheckSeverity, type CheckSummary, type CheckViolation, type CountInput, NPM_OFFICIAL_REGISTRY_URL, type PackageManifest, type PackagePublishResult, type PublishContentPreflightInput, type PublishOptions, type PublishRegistryPreflightInput, type PublishResult, type PublishStats, type PublishTarget, type PublishedAuditInput, type PublishedLeakReport, type RunContentCheckInput, type ScopeRegistryCheckInput, type ScopeRegistrySource, activeViolationKeySet, auditPublishedTarballs, buildExemptKey, buildExemptKeySet, buildPackage, buildPublishArgs, buildPublishEnv, checkCommand, checkPackageRules, checkScopeRegistryForPublish, countExitCode, describePublishTarget, ensureLocalRegistry, extractPackageScope, extractTopSegment, findShrinkableEntries, getPackageVersion, installDependencies, isPrivatePackage, isSourcePackage, loadBaseline, loadScopeRegistrySources, logError, logInfo, logSuccess, logWarn, normalizePublishTracks, normalizeRegistryUrl, parseBunfigScopeRegistries, parseNpmrcScopeRegistries, printSummary, publishCommand, publishPackage, readPackageJson, renderJsonReport, renderTextReport, resolvePackageManifest, runPublishContentCheck, runPublishContentPreflight, runPublishRegistryPreflight, scanAllPackages, topologicalSort, updateDependencies, writeBaseline };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +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
|
|
2
|
-
`),!1)},
|
|
3
|
-
`),
|
|
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,relative as r,resolve as i}from"node:path";import{existsSync as a,mkdirSync as o,readFileSync as s,writeFileSync as c}from"node:fs";import{createGraph as l,extractWorkspaceDeps as u,scanAllPackages as d,topologicalSort as f}from"@longzai-intelligence-dev-tools/dependency-graph";import*as p from"zod";import{homedir as m}from"node:os";import{gunzipSync as h}from"node:zlib";const g=(e,t)=>`${e}|${t}`,_=`.lzi/release/baseline.json`,v=e=>typeof e==`object`&&!!e&&!Array.isArray(e),y=(e,t)=>{if(!v(e)||typeof e[t]!=`string`)return;let n=e[t];return n.trim().length>0?n:void 0},b=e=>y(e,`packageName`)!==void 0&&y(e,`rule`)!==void 0&&y(e,`issue`)!==void 0,x=e=>{let t=n(e,_);if(!a(t))return{baseline:{entries:[]},invalidEntries:[],path:t};let r;try{r=JSON.parse(s(t,`utf8`))}catch{return{baseline:{entries:[]},invalidEntries:[],path:t}}let i=v(r)&&Array.isArray(r.entries)?r.entries:[],o=[],c=[];for(let e of i)b(e)?o.push(e):c.push(e);return{baseline:{entries:o},invalidEntries:c,path:t}},S=e=>new Set(e.entries.map(e=>g(e.packageName,e.rule))),C=(e,t)=>{let r=n(e,_),i=n(r,`..`);a(i)||o(i,{recursive:!0}),c(r,`${JSON.stringify({entries:t},null,2)}\n`,`utf8`)},ee=(e,t)=>e.entries.filter(e=>!t.has(g(e.packageName,e.rule))),te=/* @__PURE__ */ new Set([`docs`,`doc`,`test`,`tests`,`__tests__`,`spec`,`scripts`,`coverage`,`node_modules`]),ne=e=>typeof e==`object`&&!!e&&!Array.isArray(e),re=e=>e.private===!0,w=(e,t)=>(t?.sourcePackages??[]).includes(e),T=e=>(e.replace(/^!/,``).split(/[*?[\]{}]/)[0]??``).split(`/`).filter(e=>e.length>0)[0]??``,E=(e,t)=>{let r=[],i=e.pkg.files;if((!Array.isArray(i)||i.length===0)&&r.push({packageName:e.packageName,packagePath:e.packagePath,rule:`files-missing`,severity:`error`,message:"未声明 `files` 白名单,npm 会打包目录内全部文件(源码、测试、内部配置随 tarball 泄露的根因)",details:['修复指引——构建发布包声明 `files: ["dist"]`;源码即产物的包声明 `files: ["src"]` 并经 `contentGate.sourcePackages` 登记(ADR 0012)']}),a(n(e.packagePath,`.npmignore`))&&r.push({packageName:e.packageName,packagePath:e.packagePath,rule:`npmignore-present`,severity:`error`,message:"包目录存在 `.npmignore`——黑名单模式新增文件时容易遗漏排除,ADR 0012 已裁定统一使用 `files` 白名单",details:["修复指引——删除 `.npmignore` 并以 `files` 白名单声明发布面"]}),Array.isArray(i)){let n=w(e.packageName,t),a=[];for(let e of i){if(typeof e!=`string`)continue;let t=T(e);t!==``&&(t===`src`&&!n||te.has(t))&&a.push(e)}a.length>0&&r.push({packageName:e.packageName,packagePath:e.packagePath,rule:`files-forbidden-entry`,severity:`error`,message:`\`files\` 含禁止目录条目——${a.map(e=>`\`${e}\``).join(`、`)}`,details:["禁止目录——`docs`、`test`、`tests`、`__tests__`、`spec`、`scripts`、`coverage`、`node_modules` 一律不得进入发布面","修复指引——从 `files` 移除上述条目;`src` 仅源码发布包允许且须经 `contentGate.sourcePackages` 登记(ADR 0012)"]})}let o=e.pkg.scripts;if(ne(o)){let t=[];typeof o.build==`string`&&o.build.length>0&&(typeof o[`build:prod`]!=`string`&&t.push(`build:prod`),typeof o.prepublishOnly!=`string`&&t.push(`prepublishOnly`)),t.length>0&&r.push({packageName:e.packageName,packagePath:e.packagePath,rule:`build-scripts-missing`,severity:`error`,message:`构建发布包缺失必需脚本——${t.map(e=>`\`${e}\``).join(`、`)}`,details:["修复指引——`build:prod` 声明为 `NODE_ENV=production bun run build`、`prepublishOnly` 声明为 `bun run build:prod`(ADR 0012 决策 2)"]})}let s=e.pkg.license;return s!==`UNLICENSED`&&r.push({packageName:e.packageName,packagePath:e.packagePath,rule:`license-not-unlicensed`,severity:`warn`,message:`\`license\` 为 ${String(s)}——内部包统一使用 \`UNLICENSED\`(ADR 0012 决策 3)`,details:["修复指引——`package.json` 的 `license` 字段改为 `UNLICENSED`"]}),r},ie=p.record(p.string(),p.unknown());p.record(p.string(),p.string());const D=p.object({name:p.string()}),O=async e=>{let t=Bun.file(`${e}/package.json`),n=ie.safeParse(await t.json());if(!n.success)throw Error(`package.json 格式不合法: ${e}`);return n.data},k=async(e,t,n)=>{let r=`${t}/${e}/package.json`;if(await Bun.file(r).exists()){let n=await O(`${t}/${e}`),r=D.safeParse(n);return r.success?{name:r.data.name,path:i(`${t}/${e}`),workspaceDeps:u(n)}:null}let a=n.get(e);if(a){let e=await O(a),t=D.safeParse(e);return t.success?{name:t.data.name,path:i(a),workspaceDeps:u(e)}:null}return null},ae=(e,t,n,i)=>{let a=i?.exclude??[];if(a.length===0)return!1;let o=r(n,t);return a.includes(e)||a.includes(o)},A=async e=>{let t;t=e.packages===void 0?[...(await d(e.rootDir)).entries()].map(([e,t])=>({name:e,path:t})):e.packages;let n=[],r=[],i=0;for(let a of t){if(ae(a.name,a.path,e.rootDir,e.gate))continue;let t;try{t=await O(a.path)}catch{continue}re(t)||(i++,r.push(a.name),n.push(...E({packageName:a.name,packagePath:a.path,pkg:t},e.gate)))}let{baseline:a}=x(e.rootDir),o=S(a),s=0,c=0;for(let e of n)o.has(g(e.packageName,e.rule))||(e.severity===`error`?s++:c++);return{checkedPackages:i,checkedPackageNames:r,violations:n,exemptedKeys:o,errorCount:s,warnCount:c}},oe=e=>new Set(e.violations.map(e=>`${e.packageName}|${e.rule}`)),j={RED:`\x1B[0;31m`,GREEN:`\x1B[0;32m`,YELLOW:`\x1B[1;33m`,BLUE:`\x1B[0;34m`,RESET:`\x1B[0m`},M=`https://registry.npmjs.org/`,se=[`NPM_CONFIG_REGISTRY`,`npm_config_registry`],ce=[`cannot publish over the previously published versions`,`409 Conflict`],le=e=>ce.some(t=>e.includes(t)),N=(e,t)=>[`publish`,`--access`,t,`--registry`,e.kind===`npm`?M:e.registryUrl],P=e=>{let t={};for(let[n,r]of Object.entries(e))r===void 0||se.includes(n)||(t[n]=r);return t},F=e=>e.kind===`npm`?`registry.npmjs.org(公网 npm 轨)`:`${e.registryUrl}(本地私有轨)`,I=async e=>{let t=await V(`lzi-npm-registry`,[`ensure`],e);return t.code===0||(B(`本地私有 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)},ue=p.object({version:p.string().optional()}),L=e=>{process.stdout.write(`${j.BLUE}[INFO]${j.RESET} ${e}\n`)},R=e=>{process.stdout.write(`${j.GREEN}[SUCCESS]${j.RESET} ${e}\n`)},z=e=>{process.stdout.write(`${j.YELLOW}[WARN]${j.RESET} ${e}\n`)},B=e=>{process.stderr.write(`${j.RED}[ERROR]${j.RESET} ${e}\n`)},V=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}},H=e=>{let t=l();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 f(t).map(e=>e.map(e=>e.data))},de=(e,t)=>{let n={};for(let[t,r]of Object.entries(e))r===void 0||se.includes(t)||(n[t]=r);return n.BUN_CONFIG_REGISTRY=t,n},U=async(e,t)=>{L(`安装依赖...`);let n=await V(`bun`,[`install`],e,t);return n.code===0?(R(`依赖安装完成`),!0):(B(`安装依赖失败`),process.stdout.write(`${n.stdout}\n`),process.stderr.write(`${n.stderr}\n`),!1)},W=async(e,t)=>(L(`更新依赖...`),(await V(`bun`,[`update`],e,t)).code===0?(R(`依赖更新完成`),!0):(z(`更新依赖失败,继续发布`),!1)),G=async(e,t)=>{L(`构建 ${t}...`);let n=await V(`bun`,[`run`,`build:prod`],e);return n.code===0?(R(`${t} 构建成功`),!0):(B(`${t} 构建失败`),process.stdout.write(`${n.stdout}\n`),process.stderr.write(`${n.stderr}\n`),!1)},K=async(e,t,n=`public`,r={kind:`npm`})=>{if(r.kind===`local`&&!await I(e))return{result:`fail`,error:`本地私有 registry 未在线`};L(`发布 ${t} 到 ${F(r)}...`);let i=await V(`bun`,N(r,n),e,P(process.env)),a=i.stdout+i.stderr;return i.code===0?(R(`${t} 发布成功!`),{result:`success`}):le(a)?(z(`${t} 版本已存在,跳过`),{result:`skip`}):(B(`${t} 发布失败`),process.stderr.write(`${a}\n`),a.includes(`missing authentication`)&&B(`${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:`发布失败`})},q=async e=>{try{let t=await Bun.file(n(e,`package.json`)).json(),r=ue.safeParse(t);return r.success?r.data.version??`unknown`:`unknown`}catch{return`unknown`}},J=e=>{process.stdout.write(`========================================
|
|
3
|
+
`),L(`发布摘要`),process.stdout.write(`========================================
|
|
4
4
|
`),process.stdout.write(`
|
|
5
|
-
`),process.stdout.write(`总包数: ${e.total}\n`),process.stdout.write(`${
|
|
6
|
-
`);let t=e.results.filter(e=>e.result===`skip`);if(t.length>0){
|
|
7
|
-
`)}let n=e.results.filter(e=>e.result===`fail`);if(n.length>0){
|
|
8
|
-
`)}},
|
|
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},
|
|
5
|
+
`),process.stdout.write(`总包数: ${e.total}\n`),process.stdout.write(`${j.GREEN}成功: ${e.success}${j.RESET}\n`),process.stdout.write(`${j.YELLOW}跳过: ${e.skip}${j.RESET}\n`),process.stdout.write(`${j.RED}失败: ${e.fail}${j.RESET}\n`),process.stdout.write(`
|
|
6
|
+
`);let t=e.results.filter(e=>e.result===`skip`);if(t.length>0){z(`跳过的包:`);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){B(`失败的包:`);for(let e of n)process.stderr.write(` - ${e.name}${e.error?`: ${e.error}`:``}\n`);process.stderr.write(`
|
|
8
|
+
`)}},fe=async e=>{let t=await A({rootDir:e.rootDir,gate:e.gate,packages:e.manifests.map(e=>({name:e.name,path:e.path}))});if(t.errorCount===0)return!0;B(`发布内容门禁拦截——${t.errorCount} 条 error 级违例(源码与内部文件泄露防线,ADR 0114):`);let n=t.violations.filter(e=>e.severity===`error`&&!t.exemptedKeys.has(`${e.packageName}|${e.rule}`));for(let e of n)B(` [${e.rule}] ${e.packageName}——${e.message}`);return B('修复指引——构建发布包声明 `files: ["dist"]` 与 `build:prod`、`prepublishOnly` 脚本;存量违例可经 `lzi-release check --write-baseline --issue <指针>` 收编进基线'),!1};function pe(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 Y(...e){process.stdout.write(`${pe(e)}\n`)}const me=p.object({workspaces:p.record(p.string(),p.unknown())}),he=p.object({name:p.string(),version:p.string().optional()});var ge=class extends Error{constructor(e){super(e),this.name=`LockfileParseError`}};const _e=async e=>{let t=n(e,`bun.lock`),r=Bun.file(t);if(!await r.exists())return null;let i=(await r.text()).replace(/,(\s*[}\]])/g,`$1`),a;try{a=JSON.parse(i)}catch(e){throw new ge(`bun.lock 解析失败(${t}): ${e instanceof Error?e.message:String(e)}`)}let o=me.safeParse(a);if(!o.success)throw new ge(`bun.lock 缺少 workspaces 段或结构不合法(${t})`);let s=/* @__PURE__ */ new Map;for(let e of Object.values(o.data.workspaces)){let t=he.safeParse(e);t.success&&t.data.version!==void 0&&s.set(t.data.name,t.data.version)}return s},ve=(e,t)=>{let n=[];for(let r of e){let e=t.get(r.dependency)??null;e!==r.manifestVersion&&n.push({dependent:r.dependent,dependency:r.dependency,manifestVersion:r.manifestVersion,lockVersion:e})}return n},ye=async e=>{let t;try{t=await _e(e.rootDir)}catch(e){return B(e instanceof Error?e.message:String(e)),!1}if(t===null)return L(`未找到 bun.lock,跳过 workspace 快照一致性闸门(无 lockfile 即无过期快照面)`),!0;let n=[];for(let t of e.manifests)for(let r of t.workspaceDeps){let i=e.allPackages.get(r);if(i===void 0)return B(`workspace 依赖 ${r}(被 ${t.name} 声明)未能在仓内解析到包目录,终止发布`),!1;n.push({dependent:t.name,dependency:r,manifestVersion:await q(i)})}let r=ve(n,t);if(r.length>0){B(`bun.lock workspace 版本快照与 package.json 不一致,共 ${r.length} 处,终止发布`);for(let e of r)B(` - 依赖方 ${e.dependent} 依赖 ${e.dependency},package.json 为 ${e.manifestVersion},bun.lock 快照为 ${e.lockVersion??`未记录`}`);return B(`修复指引:在仓根重新执行 version sync 刷新快照后重试。`),B(`手工改版本号的场景,将 bun.lock workspaces 段内的版本字段对齐 package.json。`),!1}return L(`bun.lock workspace 版本快照一致性校验通过`),!0},be=e=>{let t=e.indexOf(`/`);if(!(!e.startsWith(`@`)||t<=1))return e.slice(1,t)},X=e=>e.replace(/\/+$/,``),Z=e=>typeof e==`object`&&!!e&&!Array.isArray(e),xe=e=>{let t;try{t=Bun.TOML.parse(e)}catch{return{}}if(!Z(t))return{};let n=`install`in t?t.install:void 0;if(!Z(n))return{};let r=`scopes`in n?n.scopes:void 0;if(!Z(r))return{};let i={};for(let[e,t]of Object.entries(r))typeof t==`string`&&(i[e.replace(/^@+/,``)]=t);return i},Se=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},Ce=async e=>{let t=[{label:`bunfig.toml`,path:n(e,`bunfig.toml`)},{label:`项目 .npmrc`,path:n(e,`.npmrc`)},{label:`用户 ~/.npmrc`,path:n(m(),`.npmrc`)}],r=[];for(let e of t){if(!a(e.path))continue;let t=s(e.path,`utf8`);if(e.label===`bunfig.toml`){let n=xe(t);Object.keys(n).length===0&&t.trim()!==``&&z(`bunfig.toml 解析不出 [install.scopes](可能无该段或格式异常),预检按空映射处理`),r.push({label:e.label,scopes:n})}else r.push({label:e.label,scopes:Se(t)})}return r},we=e=>{let t=be(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=X(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)X(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)X(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},Te=async e=>{L(`发布 registry 预检(scope 映射优先级防线)...`);let t=await Ce(e.rootDir),n=[];for(let r of e.manifests){let i=e.trackByName.get(r.name)??`npm`;n.push(...we({packageName:r.name,track:i,expectedRegistryUrl:i===`local`?e.localRegistryUrl:M,sources:t}))}if(n.length>0){B(`发布 registry 预检失败(${n.length} 项违规,均为发布目标被静默改写或 scope 缺位的风险):`);for(let e of n)B(` - ${e}`);return!1}return L(`发布 registry 预检通过(scope 映射与轨道声明一致)`),!0},Ee=`LZI_NPM_REGISTRY_URL`;function De(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 Oe=e=>{let t;try{t=De(e)}catch(e){B(e instanceof Error?e.message:String(e)),process.exit(1)}t.npm.length===0&&t.local.length===0&&(B(`配置文件中没有定义要发布的包`),process.exit(1));let n=process.env[Ee]??``;return t.local.length>0&&n===``&&(B(`本地私有轨非空但未设置 ${Ee}(发布仓 .env 配置,ADR 0082)`),process.exit(1)),{tracks:t,localRegistryUrl:n}},ke=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 q(i.path),c=s.startsWith(`^`)||s.startsWith(`~`)?s:`^${s}`;Y(``),Y(`下一步:提交版本变更`),Y(` release: 更新版本至 v${s}`),Y(``),Y(`然后:升级所有消费者并推送`),Y(` bun version:upgrade-across --scope ${o} --target ${c} --dry-run`),Y(` bun version:upgrade-across --scope ${o} --target ${c}`),Y(` bun push-all`)},Ae=async e=>{await Te(e)||process.exit(1)},je=async e=>{await fe(e)||process.exit(1)},Me=async e=>{let t=/* @__PURE__ */ new Map,n=[];for(let[r,i]of[[`npm`,e.tracks.npm],[`local`,e.tracks.local]])for(let a of i){let i=await k(a,e.rootDir,e.allPackages);i||(B(`未找到包: ${a}`),process.exit(1)),t.has(i.name)&&(B(`包 ${i.name} 在 publish.packages 中重复声明`),process.exit(1)),t.set(i.name,r),n.push(i)}return{manifests:n,trackByName:t}},Ne=async e=>{await Bun.spawn([`lzi-dev-cli`,`clean`],{cwd:e,stdout:`inherit`,stderr:`inherit`}).exited!==0&&z(`lzi-dev-cli clean 执行失败(继续发布,请核查清理产物是否完整)`)},Pe=async n=>{let r;try{r=await e()}catch(e){B(`加载配置文件失败: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}r||(B(`未找到配置文件 lzi-release.config.ts 或 lzi-dev.config.ts`),Y(``),Y(`请创建配置文件,示例:`),Y(`
|
|
10
10
|
import { defineConfig } from '@longzai-intelligence-release/config';
|
|
11
11
|
|
|
12
12
|
export default defineConfig({
|
|
@@ -14,4 +14,4 @@ export default defineConfig({
|
|
|
14
14
|
packages: ['apps/cli'],
|
|
15
15
|
},
|
|
16
16
|
});
|
|
17
|
-
`),process.exit(1)),
|
|
17
|
+
`),process.exit(1)),r.source===`legacy-dev-tools`&&z(`发布配置读取自 lzi-dev.config.ts 的 publish 字段(发布命令的兼容回退;该字段仍被 release log/version 域消费,请勿删除)。如需发布域专属清单可另建 lzi-release.config.ts,两处声明需保持一致(ADR 0087)`);let{tracks:a,localRegistryUrl:o}=Oe(r.config.publish?.packages),s=i(r.configPath,`..`),c=r.config.publish;c===void 0&&(B(`配置文件中没有定义要发布的包`),process.exit(1));let l=c.access??`public`,u=c.installDeps??!0,f=c.buildBeforePublish??!0,p=c.cleanBeforePublish??!0,m=n?.dryRun??!1,h=await d(s),{manifests:g,trackByName:_}=await Me({tracks:a,rootDir:s,allPackages:h}),v=e=>_.get(e)===`local`?{kind:`local`,registryUrl:o}:{kind:`npm`},y=e=>F(v(e));await Ae({manifests:g,trackByName:_,localRegistryUrl:o,rootDir:s}),await je({manifests:g,gate:c.contentGate,rootDir:s});let b=a.local.length>0?de(process.env,o):void 0,x=H(g);if(m){L(`[dry-run] 预览模式,不会实际安装、构建或发布`),L(`[dry-run] 发布顺序(含目标 registry,发布前核对轨道,ADR 0082):`);for(let e=0;e<x.length;e++){let t=x[e]??[];for(let n of t)L(`[dry-run] 第 ${e+1} 层: ${n.name} → ${y(n.name)}`)}Y(``)}let S={total:g.length,success:0,skip:0,fail:0,results:[]};L(`开始发布流程`),L(`基础路径: ${s}`),Y(``);let C=n?.cleanRunner??(async()=>Ne(s));p&&!m?(L(`发布前清理构建产物`),Y(``),await C({dryRun:!1}),Y(``)):p&&m&&(L(`[dry-run] 跳过发布前清理`),Y(``)),u&&!m?(await U(s,b)||(B(`安装依赖失败,终止发布`),process.exit(1)),Y(``)):u&&m&&(L(`[dry-run] 跳过安装依赖`),Y(``)),await ye({rootDir:s,manifests:g,allPackages:h})||process.exit(1),Y(``),c.preBuildHook&&!m?(L(`执行 preBuildHook`),t(c.preBuildHook,{cwd:s,stdio:`inherit`,shell:!0}).status!==0&&(B(`preBuildHook 执行失败,终止发布`),process.exit(1)),Y(``)):c.preBuildHook&&m&&(L(`[dry-run] 跳过 preBuildHook`),Y(``)),Y(`========================================`),Y(``);for(let e=0;e<x.length;e++){let t=x[e]??[];if(t.length!==0){L(`【第 ${e+1} 层】发布 ${t.length} 个包...`),Y(``);for(let e of t){let t=v(e.name);L(`开始发布: ${e.name}`),L(`路径: ${e.path}`),L(`目标: ${F(t)}`);let n=await q(e.path);if(L(`版本: ${n}`),f&&!m){if(!await G(e.path,e.name)){S.fail++,S.results.push({name:e.name,result:`fail`,error:`构建失败`}),Y(``);continue}}else f&&m&&L(`[dry-run] 跳过构建 ${e.name}`);if(m)L(`[dry-run] 将发布 ${e.name}@${n} → ${F(t)}`),S.results.push({name:e.name,result:`success`}),S.success++;else{let n=await K(e.path,e.name,l,t);switch(S.results.push({name:e.name,result:n.result,error:n.error}),n.result){case`success`:S.success++;break;case`skip`:S.skip++;break;case`fail`:S.fail++}}Y(``)}e<x.length-1&&(m?L(`[dry-run] 跳过更新依赖`):!await W(s,b)&&a.local.length>0&&(B(`更新依赖失败,终止发布(本地私有轨仓:快照刷新失败,继续发布将产出 workspace 依赖版本错位制品)`),process.exit(1)),Y(``))}}J(S),await ke(g,S,m),S.fail>0&&process.exit(1)},Fe=/* @__PURE__ */ new Set([`package.json`,`README.md`,`CHANGELOG.md`,`LICENSE`,`LICENSE.md`,`NOTICE`,`SECURITY.md`]),Ie=e=>{let t=[],n=0;for(;n+512<=e.length;){let r=e.subarray(n,n+512),i=r.subarray(0,100).toString(`utf8`).replace(/\0.*$/s,``);if(i===``)break;let a=r.subarray(124,136).toString(`utf8`).replace(/\0.*$/s,``).trim(),o=a===``?0:Number.parseInt(a,8),s=r.subarray(156,157).toString(`utf8`),c=r.subarray(345,500).toString(`utf8`).replace(/\0.*$/s,``),l=`${c?`${c}/`:``}${i}`;(s===`0`||s===`\0`)&&t.push({path:l,size:o}),n+=512+Math.ceil(o/512)*512}return t},Le=(e,t)=>{let n=e.startsWith(`package/`)?e.slice(8):e;if(n===``||n===`package`)return!1;if(Fe.has(n))return!0;let r=n.split(`/`)[0]??``;return!!(r===`dist`||r===`src`&&t)},Q=e=>typeof e==`object`&&!!e&&!Array.isArray(e),Re=async(e,t)=>{try{let n=await fetch(`${t}/${e}`,{headers:{accept:`application/vnd.npm.install-v1+json`}});if(!n.ok)return null;let r=await n.json();if(!Q(r))return null;let i=r.versions;if(!Q(i))return null;let a=/* @__PURE__ */ new Map;for(let[e,t]of Object.entries(i)){let n=Q(t)?t.dist:void 0;if(!Q(n))continue;let r=n.tarball;typeof r==`string`&&r.length>0&&a.set(e,r)}return a}catch{return null}},ze=async(e,t)=>{try{let n=await fetch(e);if(!n.ok)return null;let r=Buffer.from(await n.arrayBuffer());return Ie(h(r)).filter(e=>!Le(e.path,t)).map(e=>e.path.replace(/^package\//,``))}catch{return null}},Be=async e=>{let t=e.registryUrl??`https://registry.npmjs.org/`,n=[],r=[...e.packages],i=Array.from({length:12},async()=>{for(;r.length>0;){let i=r.shift();if(i===void 0)return;let a=(e.gate?.sourcePackages??[]).includes(i.name),o=await Re(i.name,t);if(o===null)continue;let s=[];for(let[e,t]of o){let n=await ze(t,a);n!==null&&n.length>0&&s.push({version:e,leakedFiles:n.slice(0,10),leakedCount:n.length})}s.length>0&&n.push({packageName:i.name,versions:s})}});return await Promise.all(i),n},Ve=e=>e.strict?e.summary.errorCount+e.summary.warnCount:e.summary.errorCount,He=e=>{let t=[],n=e.violations.filter(t=>!e.exemptedKeys.has(`${t.packageName}|${t.rule}`));if(n.length===0&&e.exemptedKeys.size===0)return t.push(`发布内容门禁通过——${e.checkedPackages} 个受检包零违例`),t;if(n.length>0){t.push(`发布内容门禁违例 ${n.length} 条:`),t.push(``);let e=/* @__PURE__ */ new Map;for(let t of n){let n=e.get(t.packageName)??[];n.push(t),e.set(t.packageName,n)}for(let[n,r]of e){t.push(` ${n}`);for(let e of r){let n=e.severity===`error`?`error`:`warn `;t.push(` [${n}] ${e.rule}`),t.push(` ${e.message}`);for(let n of e.details??[])t.push(` ${n}`)}t.push(``)}}return e.exemptedKeys.size>0&&(t.push(` 基线豁免 ${e.exemptedKeys.size} 条(存量收编中,棘轮只许缩短——见 .lzi/release/baseline.json)`),t.push(``)),t},$=e=>({checkedPackages:e.checkedPackages,errorCount:e.errorCount,warnCount:e.warnCount,exemptedCount:e.exemptedKeys.size,violations:e.violations.map(t=>({packageName:t.packageName,rule:t.rule,severity:t.severity,message:t.message,details:t.details??[],exempted:e.exemptedKeys.has(`${t.packageName}|${t.rule}`)}))}),Ue=async t=>{let n=t?.format===`json`,r=await e(),a=r?i(r.configPath,`..`):process.cwd(),o=r?.config.publish?.contentGate;if(t?.writeBaseline===!0){let e=t.issue?.trim();(e===void 0||e.length===0)&&(B(`--write-baseline 必须同时携带 --issue <收编 issue 指针>(基线豁免必须挂 issue)`),process.exit(1))}let s=await A({rootDir:a,gate:o}),c=t?.published===!0?await Be({packages:s.checkedPackageNames.map(e=>({name:e})),gate:o}):[];if(t?.writeBaseline===!0){let e=t.issue?.trim()??``,r=(/* @__PURE__ */ new Date()).toISOString().slice(0,10),i=s.violations.map(t=>({packageName:t.packageName,rule:t.rule,issue:e,recordedAt:r}));C(a,i),n||L(`基线已再生——${i.length} 条豁免挂 issue 指针 ${e}(.lzi/release/baseline.json)`)}let{baseline:l}=x(a),u=ee(l,oe(s));if(n){let e={...$(s),publishedLeaks:c,shrinkableBaselineEntries:u};process.stdout.write(`${JSON.stringify(e,null,2)}\n`)}else{for(let e of He(s))L(e);if(c.length>0){z(`registry 存量审计——${c.length} 个包存在已发布版本泄露(R5):`);for(let e of c){let t=e.versions.map(e=>`${e.version}(${e.leakedCount} 个文件)`).join(`、`);z(` ${e.packageName}: ${t}`);for(let t of e.versions[0]?.leakedFiles.slice(0,5)??[])z(` ${t}`)}}u.length>0&&z(`基线可缩短——${u.length} 条豁免对应的违例已消失,请手工删除对应条目(棘轮只许缩短)`)}let d=Ve({summary:s,strict:t?.strict===!0})+c.length;process.exit(d)};export{M as NPM_OFFICIAL_REGISTRY_URL,oe as activeViolationKeySet,Be as auditPublishedTarballs,g as buildExemptKey,S as buildExemptKeySet,G as buildPackage,N as buildPublishArgs,P as buildPublishEnv,Ue as checkCommand,E as checkPackageRules,we as checkScopeRegistryForPublish,Ve as countExitCode,F as describePublishTarget,I as ensureLocalRegistry,be as extractPackageScope,T as extractTopSegment,ee as findShrinkableEntries,q as getPackageVersion,U as installDependencies,re as isPrivatePackage,w as isSourcePackage,x as loadBaseline,Ce as loadScopeRegistrySources,B as logError,L as logInfo,R as logSuccess,z as logWarn,De as normalizePublishTracks,X as normalizeRegistryUrl,xe as parseBunfigScopeRegistries,Se as parseNpmrcScopeRegistries,J as printSummary,Pe as publishCommand,K as publishPackage,O as readPackageJson,$ as renderJsonReport,He as renderTextReport,k as resolvePackageManifest,A as runPublishContentCheck,fe as runPublishContentPreflight,Te as runPublishRegistryPreflight,d as scanAllPackages,H as topologicalSort,W as updateDependencies,C as writeBaseline};
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longzai-intelligence-release/core",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Longzai Intelligence
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Longzai Intelligence 发布业务核心实现",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"core",
|
|
7
|
+
"longzai-intelligence",
|
|
8
|
+
"pipeline",
|
|
9
|
+
"publish",
|
|
10
|
+
"release"
|
|
11
|
+
],
|
|
5
12
|
"license": "UNLICENSED",
|
|
6
13
|
"files": [
|
|
7
14
|
"dist"
|
|
@@ -32,7 +39,7 @@
|
|
|
32
39
|
},
|
|
33
40
|
"dependencies": {
|
|
34
41
|
"@longzai-intelligence-dev-tools/dependency-graph": "^0.2.29",
|
|
35
|
-
"@longzai-intelligence-release/config": "0.0.
|
|
42
|
+
"@longzai-intelligence-release/config": "0.0.5",
|
|
36
43
|
"zod": "^4.4.3"
|
|
37
44
|
}
|
|
38
45
|
}
|