@jayyuen66/dsh-plugin-shared 0.1.6 → 0.1.7

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.
@@ -1,7 +1,8 @@
1
1
  /**
2
- * 产物文本的 canonical 形态:所有 `//#region` 行折成相对包根的形状,其余字节原样保留。
2
+ * 产物文本的 canonical 形态:所有 `//#region` 行折成与 process.cwd() 无关的形状,其余字节原样保留。
3
3
  * @param code - builder 拿到的产物文本
4
4
  * @param pkgDir - 包根绝对路径(builder 的 import.meta.dirname)
5
- * @returns 与 process.cwd() 无关的同一份文本;对 canonical 输入幂等
5
+ * @param cwd - 构建时的 process.cwd()。传入即走精确前缀档;省略走旧签名兼容档
6
+ * @returns 与 process.cwd() 无关的同一份文本;对 canonical 输入幂等(残余局限见文件头)
6
7
  */
7
- export declare function canonicalizeRegionPaths(code: string, pkgDir: string): string;
8
+ export declare function canonicalizeRegionPaths(code: string, pkgDir: string, cwd?: string): string;
@@ -18,11 +18,18 @@ export interface CardApplyCtx {
18
18
  * apply 幂等守卫:已 claim 返回 false(调用方直接 return),否则占位并
19
19
  * 挂 ctx.effect 清理——HMR 重载时 flag 复位,再次 apply 可重新声明。
20
20
  *
21
+ * 占位值是本次 claim 现造的 symbol;守卫对外的判据面是「自有键上的 `true` 或任何 symbol」:
22
+ * `true` 是外部可写的占位协议(判据放宽一分就重新打开 HMR 重复装载),symbol 品牌让
23
+ * 重复加载的另一份模块实例的占位同样被认出——两份实例各持各的 token,靠 typeof 而不是
24
+ * 身份对上。清理只认**自己那枚** token:旧实例的 disposer 迟于新实例 claim 执行(HMR /
25
+ * 重复加载不保证时序,也不保证恰好调一次)时,它认不得新实例的占位,新 claim 得以保留;
26
+ * claim 之前槽上已有的自有值按原样恢复,不抹成 undefined。
27
+ *
21
28
  * ⚠ 标记是**进程级**的(同一枚 flag 在全页面只有一份),不是 per-ctx:同一张卡被第二个
22
29
  * ctx apply 时会被这一枚挡掉。要允许多 ctx 共存就得按 ctx 记账(WeakSet 一类),
23
30
  * 别改成这里"顺手放过第二次"——那会重新打开 HMR 重复装载这条路。
24
31
  * @param ctx - apply 的 ctx(需要 effect)。
25
- * @param flag - globalThis 上的布尔标记键名(如 "__xxxCardApplied")。
32
+ * @param flag - globalThis 上的占位标记键名(如 "__xxxCardApplied"),形态见 {@link isLegalClaimFlag}。
26
33
  * @param label - effect 标签(日志/生命周期可观测)。
27
34
  * @returns true 表示本次拿到声明权(继续执行),false 表示已被占位。
28
35
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * touched 层与快照的差异字段(值语义比较;undefined、null 与缺失三者等价)。
3
3
  *
4
- * 用 `JSON.stringify` 而不是 `===` 比较:设置项的值是表单里的标量与数组,引用相等会把
5
- * 「用户重排了同内容的数组」判成改动,也会把两份等值数组判成没改。
4
+ * 比较用 {@link formValueEquals} 而不是 `===` 或 `JSON.stringify`:引用相等会把「用户重排了
5
+ * 同内容的数组」判成改动,而序列化比较会在 NaN、键序、BigInt 与循环引用上给出错误答案。
6
6
  * @param touched 草稿层里被碰过的字段
7
7
  * @param value 官方快照里的当前值
8
8
  * @returns 真正发生改动的字段名,顺序随 `touched` 的键序
@@ -39,8 +39,9 @@ export declare const OFF_JUSTIFICATIONS: Record<string, OffJustification>;
39
39
  *
40
40
  * 判据是「按文体分别核命中数」。`oxlint --deny=<rule>` 量的是整体,而 `--deny` 盖不住
41
41
  * override 层的 off(这条口径见本文件头),所以一条规则可以同时是「TS 侧零命中、保持开启」
42
- * 与「`scripts/` 的无类型 .mjs 里有命中、按文体收窄」——`eslint/no-continue` 与
43
- * `eslint/no-await-in-loop` 正是这一类,它们的收窄在 SCRIPTS_OVERRIDES 里,不在这里。
42
+ * 与「`scripts/` 的无类型 .mjs 里有命中、按文体收窄」——`eslint/no-continue` 正是这一类,
43
+ * 它的收窄在 SCRIPTS_OVERRIDES 里,不在这里。`eslint/no-await-in-loop` 则两侧都开着
44
+ * (实测探针对 .mjs 照样报),两边都按代码写到位,没有收窄层。
44
45
  */
45
46
  export declare const RETIRED_OFFS: string[];
46
47
  export declare const READONLY_SYNC_ALLOWLIST: string[];
@@ -51,8 +52,13 @@ export declare const READONLY_SYNC_ALLOWLIST: string[];
51
52
  * 那写法无条件先求值,于是「调用方自带入口」这条官方逃生口在缺 sonarjs 时**用不了**——
52
53
  * 抛错发生在调用方的入口被用上之前,报错信息给的补救办法照做必然再报同一条。
53
54
  * 分支口径:
54
- * - 调用方带了入口 → 解析失败就跳过(它显然自备了),成功则去重后前置;
55
- * - 调用方没带 → 解析失败照旧抛那条可操作的信息(缺包是真故障,不该被静默)。
55
+ * - 调用方带了**指向 sonarjs 的**入口 → 解析失败就跳过(它自备的就是 sonarjs),
56
+ * 成功则去重后前置;
57
+ * - 调用方只带了别家插件、或没带 → 解析失败照旧抛那条可操作的信息。SONARJS_RULES
58
+ * 无条件引用 sonarjs,只带别家插件救不了它:那种失败会迟到到 oxlint 装载配置那一刻
59
+ * (`Plugin 'sonarjs' not found`),可操作的成因必须在这里先抛出来,而不是被吞掉。
60
+ * 「指向 sonarjs」按入口路径含 `sonarjs` 判定——调用方传的是自己解析到的插件路径,
61
+ * 这是能落进字符串判据的全部信号。
56
62
  *
57
63
  * 导出且把解析器做成入参,是为了「解析失败」那一支在**本仓**可测:本仓 devDependencies
58
64
  * 装着 sonarjs,真实的解析路径永远成功,那两个分支就只能靠注桩走到(与 `LauncherLocator`、
@@ -64,6 +70,16 @@ export declare const READONLY_SYNC_ALLOWLIST: string[];
64
70
  * @returns 交给 oxlint 的 jsPlugins 数组
65
71
  */
66
72
  export declare function resolveJsPlugins(callerPlugins: NonNullable<OxlintConfig["jsPlugins"]> | undefined, resolve?: () => string): NonNullable<OxlintConfig["jsPlugins"]>;
73
+ /**
74
+ * oxlint 认识的 vitest 规则全集(取自 `oxlint --rules` 的表格,73 条)。整族在基线 off,再由
75
+ * TEST_FILE_OVERRIDES 在用例文件上 on —— 理由见 OFF_JUSTIFICATIONS 里的 `vitest/*`。
76
+ *
77
+ * 这是一张**快照**,不是「上游新增规则自动落在生产文件之外」的机制:`categories` 是全局
78
+ * error 开关,上游在任一 category 里新增 vitest 规则,它就会先作用到每一个 host.ts。
79
+ * 拦这一步的是 test/config-oxlint.test.ts 的一致性用例——它拿 `oxlint --rules` 现量与
80
+ * 本数组逐条对照,上游新增/删除规则即红,由人工同步名单(需要豁免的再补依据)。
81
+ */
82
+ export declare const VITEST_ALL_RULES: string[];
67
83
  export declare const BASE_RULES: RuleTable;
68
84
  /**
69
85
  * 用例文件(含 `test/*.ts` 夹具与 helper)共享的豁免:规模类,以及「桩件要跨官方名义类型」
@@ -90,9 +106,8 @@ export declare const TEST_OVERRIDES: OxlintOverride;
90
106
  export declare const VITEST_ASSERTION_DEFAULTS: string[];
91
107
  export declare const TEST_FILE_OVERRIDES: OxlintOverride;
92
108
  /**
93
- * 生成用例文件的 override(vitest 结构类判据的开与关都在这张表里)。
94
- * @param assertionHelpers - 本包"内部会断言"的 helper 函数名,并入 `expect-expect` 的名单。
95
- * @returns 可直接放进 `overrides` 的配置项。
109
+ * {@link makeTestFileOverrides} 的入参形状。两份名单都并入规则自带的选项,不改任何规则开关;
110
+ * 只收裸标识符(通配或带点形式会在 {@link makeTestFileOverrides} 里直接抛)。
96
111
  */
97
112
  export interface TestFileOverridesOptions {
98
113
  /** 本包"内部会断言"的 helper 函数名,并入 `vitest/expect-expect` 的名单。 */
@@ -6,10 +6,20 @@ import type { ViteUserConfig } from "vitest/config";
6
6
  * 合出来是重载联合(函数类型,取 `.test` 直接落空),`TestUserConfig` 是 test 块本身的类型。
7
7
  */
8
8
  type PackageConfig = ViteUserConfig;
9
+ type TestConfig = NonNullable<PackageConfig["test"]>;
10
+ type CoverageConfig = NonNullable<TestConfig["coverage"]>;
11
+ /**
12
+ * 覆盖率覆盖项的形状。`include` 不在其中:统计面由 `coverageInclude` 单源供给,两处各写一份
13
+ * 必然漂移。覆盖率对象整块替换会让四项 100% 阈值与排除表静默消失,所以覆盖项不走 `test`
14
+ * 展开,而是逐键合并;`thresholds` 之内也逐键合并,单项可改、其余三项删不掉。
15
+ */
16
+ type CoverageOverride = Omit<Partial<CoverageConfig>, "include">;
9
17
  /** 覆盖率面与 test 块里真正因包而异的那一项。 */
10
18
  export interface PackageVitestOptions {
11
- /** `coverage.include` —— 本包该被统计的源文件(各包只有这里不同)。 */
19
+ /** `coverage.include` —— 本包该被统计的源文件(各包只有这里不同);空数组/空串在工厂即抛。 */
12
20
  coverageInclude: string[];
21
+ /** 覆盖率覆盖项:逐键合并到公共面。要例外就在这里写,不要在 `test` 里塞 `coverage`。 */
22
+ coverage?: CoverageOverride;
13
23
  /** 追加/覆盖 test 字段(少用;出现即应在包内配置里写明为什么)。 */
14
24
  test?: PackageConfig["test"];
15
25
  }
@@ -11,7 +11,8 @@ export declare function errorText(error: unknown): string;
11
11
  * 是不是「设置冲突」那一类失败(宿主在两个界面同时改同一份设置时抛的形)。
12
12
  *
13
13
  * 认两枚痕迹而不是 `instanceof`:跨 realm 抛过来的错误类不是同一个构造函数,而
14
- * `code`/`name` 是宿主两边都写的那两个值。经 `fieldOf` 读,免得在敌意形状上抛。
14
+ * `code`/`name` 是宿主两边都写的那两个值。经 `fieldOf` 读——它的探测出口把敌意
15
+ * getter / Proxy 陷阱的抛错塌成「取不到」,判定面因此天然只回答是/否,不外抛。
15
16
  * @param error 捕获到的值
16
17
  * @returns 该失败是否应当按「设置已被别处改动」处理
17
18
  */
@@ -14,18 +14,22 @@ export type SettledProcess = Pick<ShellProcess, "status" | "exitCode" | "signal"
14
14
  */
15
15
  export declare function jobOutcomeOf(proc: SettledProcess): JobOutcome;
16
16
  /**
17
- * 只用得到的那一个成员。**不按 `ShellProcess` 整型收**:本函数丢弃 `kill()` 的返回值,
18
- * 把官方的返回类型写进签名等于要求每个调用方(含测试替身)都得交回一个 boolean,
19
- * 而这里真正需要的只是「有一个可以试着调的 kill」。
17
+ * 只用得到的那一个成员。**不按 `ShellProcess` 整型收**:官方的 `kill` 返回 `Promise<boolean>`,
18
+ * 把那个返回类型写进签名等于要求每个调用方(含测试替身)都得交回一个 Promise,
19
+ * 而这里真正需要的只是「有一个可以试着调的 kill」——返回值要么被丢弃,要么被收掉拒绝。
20
20
  */
21
21
  export interface KillableProcess {
22
22
  kill: () => unknown;
23
23
  }
24
24
  /**
25
- * 收掉子进程,**忽略抛错**:进程可能已自然退出或已被系统回收,此时 kill 失败不是本包的失败。
25
+ * 收掉子进程,**忽略失败**:进程可能已自然退出或已被系统回收,此时 kill 失败不是本包的失败。
26
26
  *
27
27
  * 为什么不向调用方通报:调用点都在清理臂(卸载、被拒、超时)上,那里没有可行动的下一步;
28
28
  * 把异常抛出去只会让清理链半途而废,留下更难的现场。
29
+ *
30
+ * 同步抛错与**异步拒绝**都要收:官方句柄的 `kill` 返回 `Promise<boolean>`,同步 try/catch
31
+ * 只拦得住前者,返回的 Promise 拒绝会一路逃到进程级 unhandledRejection——清理臂不该有能力
32
+ * 把宿主进程的未处理拒绝计数器推高。
29
33
  * @param proc 待收的句柄
30
34
  */
31
35
  export declare function killProc(proc: KillableProcess): void;
@@ -2,5 +2,7 @@
2
2
  * 执行一次 lesson bus 调用,并把两类失败归到同一个出口。
3
3
  * @param call - 真正的调用(同步返回或返回 Promise 都接受)。
4
4
  * @param onFailure - 失败收口:同步抛错与异步拒绝都会到这里,且各自只被调一次。
5
+ * 返回类型写 `unknown` 而不是 `void`:签名不能让"收口是 async 函数"变成类型错误,
6
+ * 而它返回的 Promise 由 report 接管(见该函数的注释),调用方不必也不该自己等它。
5
7
  */
6
- export declare function settleLessonCall(call: () => unknown, onFailure: (reason: unknown) => void): void;
8
+ export declare function settleLessonCall(call: () => unknown, onFailure: (reason: unknown) => unknown): void;
@@ -35,8 +35,9 @@ export type MessagesCatalog<Messages> = Record<Locale, {
35
35
  readonly [Key in keyof Messages]: string;
36
36
  }>;
37
37
  /**
38
- * 把设置里的原始值归一成受支持的 locale:按**主语言子标签**判定(`en-US`、`zh-Hans-CN`
39
- * 都落位),与 client 半「先完整标签、再主语言子标签」的规则同源;判不出来一律退
38
+ * 把设置里的原始值归一成受支持的 locale:只取**主语言子标签**判定(`en-US`、`zh-Hans-CN`
39
+ * 都落位到 `en` / `zh`),没有「先按完整标签匹配」那一层——受支持的语言只有 `zh` 与 `en`
40
+ * 两个主标签,完整标签匹配不会多认出任何一个,反而要多维护一张表。判不出来一律退
40
41
  * DEFAULT_LOCALE——文案语言不该成为报错面。
41
42
  * @param raw - 设置里的原始值,类型不可信(来自用户文档)。
42
43
  * @returns 归一后的 locale。
@@ -5,6 +5,11 @@ export declare function isRecord(value: unknown): value is Record<string, unknow
5
5
  *
6
6
  * 只做 `typeof value["shell"] === "object"` 的话,守卫声称的是「有这个服务」而实际证明的只是
7
7
  * 「有个对象」——运行到一半才炸。逐个点名方法,守卫才名副其实。
8
+ *
9
+ * `Object.prototype` 的默认成员不算服务实现:`hasMethods({}, ["toString"])` 若照单全收,
10
+ * 一个普通对象就能冒充服务面。判据是**身份**——解析到的函数正是 `Object.prototype` 上
11
+ * 那一枚才排除,所以类/中间原型上的真实实现(包括对原型成员的重写)照常可识别,
12
+ * cordis 方法挂在原型上的承重面不受影响。
8
13
  * @param value 待验证的服务候选(来自 `ctx.get(...)` 的 unknown)
9
14
  * @param methods 该服务面必须可调用的方法名
10
15
  * @returns 是否为可用的服务面
@@ -29,5 +34,8 @@ export declare function hasMethods(value: unknown, methods: readonly string[]):
29
34
  * ⚠ 本段刻意不点名兄弟包:**声明上的 JSDoc 会被 tsc 抄进 dist/types/record.d.ts 一起
30
35
  * 发出去**,包名会让装了这个包的消费方以为存在一个并不存在的依赖
31
36
  *(test/publish-manifest.test.ts 的产物面判据会红)。要写具体调用点就写在 test/ 里。
37
+ *
38
+ * 读取走的是探测式出口:被测对象带抛错 getter / Proxy 陷阱时按「取不到」交回
39
+ * `undefined`,异常不冒泡(能力探测不该把流程炸掉)。
32
40
  */
33
41
  export declare function fieldOf(value: unknown, key: string): unknown;
@@ -5,6 +5,7 @@
5
5
  export declare function truncateEnd(text: string, maxChars: number): string;
6
6
  /**
7
7
  * 保留后 `minChars` 个码元;切点切开代理对时丢掉首部那个孤立低代理。
8
- * `minChars <= 0` 与 NaN 与 `truncateEnd(text, 0)` 同口径返回空串,不返回整串。
8
+ * `minChars` 不足一枚码元(含 `<= 0`、NaN、`0 < minChars < 1`)与 `truncateEnd(text, 0)`
9
+ * 同口径返回空串,不返回整串。
9
10
  */
10
11
  export declare function truncateStart(text: string, minChars: number): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayyuen66/dsh-plugin-shared",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "dsh 自研插件的共享工具库:tool 事件流记账扫描 + 卡片 apply/差异骨架 + 官方作业结局映射 + 项目键派生 + 代理对安全截断。作为库包被 import,不由用户启用",
5
5
  "keywords": [
6
6
  "cordis",
@@ -111,12 +111,12 @@
111
111
  "@types/node": "26.6.5",
112
112
  "@vitest/coverage-v8": "5.0.5",
113
113
  "eslint-plugin-sonarjs": "4.2.2",
114
- "oxfmt": "0.73.0",
115
- "oxlint": "1.88.0",
116
- "oxlint-tsgolint": "7.0.2004",
117
- "rolldown": "1.2.14",
114
+ "oxfmt": "0.74.0",
115
+ "oxlint": "1.89.0",
116
+ "oxlint-tsgolint": "7.0.2005",
117
+ "rolldown": "1.2.15",
118
118
  "typescript": "7.0.2",
119
- "vite": "8.3.4",
119
+ "vite": "8.3.5",
120
120
  "vitest": "5.0.5"
121
121
  },
122
122
  "peerDependencies": {
@@ -162,7 +162,7 @@
162
162
  },
163
163
  "scripts": {
164
164
  "prebuild": "node build-clean.mjs",
165
- "build": "node build-host.mjs && node build-config.mjs && tsc -p tsconfig.build.json && tsc -p tsconfig.build.config.json",
165
+ "build": "node build-host.mjs && node build-config.mjs && tsc -p tsconfig.build.json && tsc -p tsconfig.build.config.json && node build-types-check.mjs",
166
166
  "typecheck": "tsc --noEmit -p tsconfig.json",
167
167
  "lint": "oxlint .",
168
168
  "test": "vitest run --coverage",