@jayyuen66/dsh-plugin-shared 0.1.6 → 0.1.8
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/LICENSE +1 -1
- package/README.md +184 -91
- package/README.zh-CN.md +186 -93
- package/config/tsconfig.client.base.json +1 -1
- package/dist/build/build-artifacts.js +1 -0
- package/dist/build/build-client.js +2 -0
- package/dist/build/build-host.js +1 -0
- package/dist/build/build-types.js +2 -0
- package/dist/build/canonicalize-region-paths-BxladpX3.js +1 -0
- package/dist/canonicalize-region-paths.js +1 -1
- package/dist/card-apply.js +1 -1
- package/dist/card-diff.js +1 -1
- package/dist/config/oxlint.base.js +1 -1
- package/dist/config/vitest.base.js +1 -1
- package/dist/errors.js +1 -1
- package/dist/job-outcome.js +1 -1
- package/dist/jsonl.js +2 -3
- package/dist/lesson-bus.js +1 -1
- package/dist/project-key.js +1 -1
- package/dist/record.js +1 -1
- package/dist/text.js +1 -1
- package/dist/tool-events.js +1 -1
- package/dist/types/build-artifacts.d.ts +116 -0
- package/dist/types/build-client.d.ts +79 -0
- package/dist/types/build-host.d.ts +54 -0
- package/dist/types/build-types.d.ts +61 -0
- package/dist/types/canonicalize-region-paths.d.ts +4 -3
- package/dist/types/card-apply.d.ts +8 -1
- package/dist/types/card-diff.d.ts +2 -2
- package/dist/types/config/oxlint.base.d.ts +22 -7
- package/dist/types/config/vitest.base.d.ts +11 -1
- package/dist/types/errors.d.ts +2 -1
- package/dist/types/job-outcome.d.ts +8 -4
- package/dist/types/lesson-bus.d.ts +3 -1
- package/dist/types/locale.d.ts +3 -2
- package/dist/types/record.d.ts +8 -0
- package/dist/types/text.d.ts +2 -1
- package/package.json +40 -24
package/dist/lesson-bus.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return e===null||typeof e!=`object`&&typeof e!=`function`?!1:typeof Reflect.get(e,`then`)==`function`}function t(e,
|
|
1
|
+
function e(e){return e===null||typeof e!=`object`&&typeof e!=`function`?!1:typeof Reflect.get(e,`then`)==`function`}async function t(e){try{await e}catch(e){console.error(`[shared/lesson-bus] onFailure threw:`,e)}}function n(n,r){try{let i=r(n);e(i)&&t(i)}catch(e){console.error(`[shared/lesson-bus] onFailure threw:`,e)}}async function r(e,t){try{await e}catch(e){n(e,t)}}function i(t,i){try{let n=t();e(n)&&r(n,i)}catch(e){n(e,i)}}export{i as settleLessonCall};
|
package/dist/project-key.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{realpathSync as e}from"node:fs";import{createHash as t}from"node:crypto";import n from"node:path";const r=/[/\\]+/gu,i=/\/{2,}/gu;function a(e){return e===`\\`?r:i}const o=/^[A-Za-z]:$/u
|
|
1
|
+
import{realpathSync as e}from"node:fs";import{createHash as t}from"node:crypto";import n from"node:path";const r=/[/\\]+/gu,i=/\/{2,}/gu;function a(e){return e===`\\`?r:i}const o=/^[A-Za-z]:$/u;function s(e){if(!e.startsWith(String.raw`\\`))return!1;let t=e.replace(/^\\\\[?.]\\(?:UNC\\)?/u,``).replace(/^\\\\/u,``);return/^[^\\]+\\[^\\]+$/u.test(t)}function c(r,i){if(typeof r!=`string`)return`default`;let c=r.trim();if(c===``)return`default`;let l=i?.realpath??e,u=i?.sep??n.sep,d=(u===`\\`?n.win32:n.posix).resolve(c);try{d=l(d)}catch{}let f=d.replace(/[/\\]+$/u,``),p=d.replaceAll(a(u),`/`).replace(/\/+$/u,``);return p===``||o.test(p)||s(f)?`default`:`${p.slice(p.lastIndexOf(`/`)+1).replaceAll(/\s+/gu,`_`)}-${t(`sha256`).update(p,`utf8`).digest(`hex`).slice(0,8)}`}export{c as deriveProjectKey};
|
package/dist/record.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function t(t,
|
|
1
|
+
function e(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const t={absent:!0};function n(e,n){try{return{absent:!1,value:e[n]}}catch{return t}}function r(t,r){if(!e(t))return!1;for(let e of r){let r=n(t,e);if(r.absent||typeof r.value!=`function`||r.value===Object.getOwnPropertyDescriptor(Object.prototype,e)?.value)return!1}return!0}function i(r,i){let a=e(r)?n(r,i):t;return a.absent?void 0:a.value}export{i as fieldOf,r as hasMethods,e as isRecord};
|
package/dist/text.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{truncateWithoutSplittingSurrogatePair as e}from"@deepseek-ai/dsh-output-retention";function t(t,n){return e(t,Math.max(0,n))}function n(e,t){if(
|
|
1
|
+
import{truncateWithoutSplittingSurrogatePair as e}from"@deepseek-ai/dsh-output-retention";function t(t,n){return e(t,Math.max(0,n))}function n(e,t){if(Number.isNaN(t)||t<1)return``;if(e.length<=t)return e;let n=e.slice(-t),r=n.codePointAt(0);return r!==void 0&&r>=56320&&r<=57343?n.slice(1):n}export{t as truncateEnd,n as truncateStart};
|
package/dist/tool-events.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isRecord as e}from"./record.js";function t(t){return e(t)&&t.isError===!0}function n(e){return e===!0}function r(t){return e(t.data)}function i(e){return typeof e==`string`&&e.length>0?e:void 0}function a(e){return typeof e==`string`&&e.length>0?e:void 0}function o(t){if(typeof t==`string`)try{let n=JSON.parse(t);return
|
|
1
|
+
import{isRecord as e}from"./record.js";function t(t){return e(t)&&t.isError===!0}function n(e){return e===!0}function r(t){return e(t.data)}function i(e){return typeof e==`string`&&e.length>0?e:void 0}function a(e){return typeof e==`string`&&e.length>0?e:void 0}function o(t){if(typeof t==`string`)try{let n=JSON.parse(t);return{arguments:e(n)?n:{},badArguments:!1}}catch{return{arguments:{},badArguments:!0}}return{arguments:e(t)?t:{},badArguments:!1}}function s(e,t){let n;if(!r(e))return n;if(e.type===`tool/call`){let r=a(e.data.name);if(r!==void 0){let a=o(e.data.arguments);n={name:r,callId:i(e.data.callId),arguments:a.arguments,badArguments:a.badArguments,seq:t}}}else if(e.type===`tool/ptc-dispatch-start`){let r=i(e.data.subCallId),s=a(e.data.name);if(r!==void 0&&s!==void 0){let i=o(e.data.arguments);n={name:s,callId:r,arguments:i.arguments,badArguments:i.badArguments,seq:t}}}return n}function c(a,o){let s;if(!r(a))return s;if(a.type===`tool/result`){let n=a.data.message,r=e(n)?n.source:void 0;s={callId:e(r)?i(r.callId):void 0,isError:t(n),seq:o}}else if(a.type===`tool/ptc-dispatch`){let e=i(a.data.subCallId);e!==void 0&&(s={callId:e,isError:n(a.data.isError),seq:o})}return s}function l(e,t){return{call:s(e,t),result:c(e,t)}}function u(t){let n=[],r=[];for(let[i,a]of t.entries())if(e(a)){let{call:e,result:t}=l(a,i);e!==void 0&&n.push(e),t!==void 0&&r.push(t)}return{calls:n,results:r}}function d(e,t){let n=e[t];return typeof n==`string`?n:void 0}function f(e){let t=e.arguments;return e.name===`str_replace_editor`?t.command===`view`?{kind:`read-view`,path:d(t,`path`)}:{kind:`write`,path:d(t,`path`)}:{kind:`write`,path:d(t,`file_path`)}}export{f as editPathOf,u as scanToolEvents,l as toolEventRowsOf};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { InputOptions } from "rolldown";
|
|
2
|
+
/**
|
|
3
|
+
* 对象守卫:数组与非对象都不算 Record。
|
|
4
|
+
* @param raw - JSON.parse 之后的任意值
|
|
5
|
+
* @returns 是否为普通对象
|
|
6
|
+
*/
|
|
7
|
+
export declare function isRecord(raw: unknown): raw is Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* 从 JSON.parse 的 unknown 投影为 Record(非对象 → 空对象兜底),避免 any 索引。
|
|
10
|
+
* @param raw - JSON.parse 之后的任意值
|
|
11
|
+
* @returns 普通对象;输入不是对象时为空对象
|
|
12
|
+
*/
|
|
13
|
+
export declare function asRecord(raw: unknown): Record<string, unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* 读包根的 package.json,带上下文的失败信息。
|
|
16
|
+
*
|
|
17
|
+
* 惰性(调用时才读)而不是在消费方模块顶层读:顶层一次 JSON 语法错误会以未捕获的顶层
|
|
18
|
+
* await 错误 + 裸 SyntaxError 收场,读者看不出是哪个文件坏了;而测试 import 构建模块时
|
|
19
|
+
* 只为拿构建函数,不该连带付一次磁盘 I/O。
|
|
20
|
+
* @param root - 包根绝对路径
|
|
21
|
+
* @returns 解析后的 manifest
|
|
22
|
+
*/
|
|
23
|
+
export declare function readManifest(root: string): Promise<unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* 说明符的包名段:`@scope/pkg/sub` → `@scope/pkg`,`pkg/sub` → `pkg`。
|
|
26
|
+
*
|
|
27
|
+
* external 的字符串项是精确匹配,所以按「包名段」判定而不是枚举子路径。
|
|
28
|
+
* @param id - 说明符
|
|
29
|
+
* @returns 包名段
|
|
30
|
+
*/
|
|
31
|
+
export declare function packageNameOf(id: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* rolldown 输出项的最小投影:单文件产物只关心这几项。
|
|
34
|
+
* `moduleIds` 之类不进投影——本类型只判形状,多一层声明就多一处与 rolldown 版本耦合的面,
|
|
35
|
+
* 而调用方拿到的仍是原始输出项,不缺字段。
|
|
36
|
+
*/
|
|
37
|
+
export interface OutputItem {
|
|
38
|
+
readonly type: string;
|
|
39
|
+
readonly fileName: string;
|
|
40
|
+
readonly code?: string | undefined;
|
|
41
|
+
readonly isEntry?: boolean | undefined;
|
|
42
|
+
readonly moduleIds?: readonly string[] | undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 单文件产物只允许一个 entry chunk:多余 chunk / asset 不落盘,产物要到运行时才因缺模块而炸。
|
|
46
|
+
* 这是「静默不渲染」同一类难查故障,所以在构建期就失败。
|
|
47
|
+
* @param output - rolldown 的 generate 输出清单
|
|
48
|
+
* @param source - 入口源码名(错误信息点名用)
|
|
49
|
+
* @returns 唯一的那个 entry chunk
|
|
50
|
+
*/
|
|
51
|
+
export declare function singleEntryChunk(output: readonly OutputItem[], source: string): OutputItem & {
|
|
52
|
+
code: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* 原子落盘:目标目录下 `.tmp/` 草稿 + rename;失败时清掉草稿再抛。
|
|
56
|
+
*
|
|
57
|
+
* 草稿落在**目标文件所在目录**下的 `.tmp/`,不是 `os.tmpdir()`,也不是产物旁边的裸名:
|
|
58
|
+
* - 不用 `os.tmpdir()`:rename 只在同一文件系统内原子,跨设备会以 EXDEV 失败,而系统临时
|
|
59
|
+
* 目录常被挂到另一个分区/卷;
|
|
60
|
+
* - 跟随目标目录而不是写死包根:本函数住在公共库里,产物落点由调用方决定;
|
|
61
|
+
* - 不用「产物名 + 后缀」直接落在产物旁边:那个名字通常不在 `.gitignore` 里,构建被中断
|
|
62
|
+
* 时半份产物会以未跟踪文件出现在 `git status`,被误当成源码改动提交。`.tmp/` 是各包
|
|
63
|
+
* 统一忽略的目录,不需要为构建残留单独扩忽略规则。
|
|
64
|
+
*
|
|
65
|
+
* 失败信息带上两个路径并保留 cause:裸 `ENOENT … open '…/<uuid>.tmp'` 把注意力引到草稿名上,
|
|
66
|
+
* 读者还得自己剥出真正的产物路径。
|
|
67
|
+
* @param target - 目标文件绝对路径
|
|
68
|
+
* @param text - 产物正文
|
|
69
|
+
*/
|
|
70
|
+
export declare function writeArtifact(target: string, text: string): Promise<void>;
|
|
71
|
+
/** 异常路径上释放 rolldown bundle 的最小形状。 */
|
|
72
|
+
export interface CloseableBundle {
|
|
73
|
+
readonly close: () => Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
/** generate 交给调用方的最小输出形状(属性式签名,真实 bundle 结构满足)。 */
|
|
76
|
+
export interface RolldownBundle extends CloseableBundle {
|
|
77
|
+
readonly generate: (options: {
|
|
78
|
+
format: "esm" | "cjs";
|
|
79
|
+
minify?: boolean;
|
|
80
|
+
comments?: boolean;
|
|
81
|
+
}) => Promise<{
|
|
82
|
+
output: readonly OutputItem[];
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 调用方注入的 rolldown 构造器。
|
|
87
|
+
*
|
|
88
|
+
* 注入而不是由本包声明依赖:rolldown 是各包构建期自己的 devDependency,本包作为库
|
|
89
|
+
* 不该为构建工具声明运行期依赖面。参数类型借 rolldown 自己的 InputOptions(仅类型
|
|
90
|
+
* 导入,产物里没有它)。
|
|
91
|
+
*/
|
|
92
|
+
export type RolldownFactory = (options: InputOptions) => Promise<RolldownBundle>;
|
|
93
|
+
/**
|
|
94
|
+
* 异常路径上释放 rolldown bundle:close 失败只记日志,不覆盖调用方正在抛的原始错因。
|
|
95
|
+
*
|
|
96
|
+
* bundle 持有原生资源,generate 或输出守卫抛错时同样要释放;但那时唯一有价值的日志是
|
|
97
|
+
* 「构建为什么失败」,让一条关于资源释放的噪音顶掉它,排障就从「读一行」变成「猜」。
|
|
98
|
+
* 成功路径不要用本函数:那里 close 失败仍应判红,资源没释放是构建器的问题。
|
|
99
|
+
* @param bundle - 待释放的 bundle
|
|
100
|
+
*/
|
|
101
|
+
export declare function closeQuietly(bundle: CloseableBundle): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* 直跑守卫:本脚本是否被当作入口直接执行(而不是被测试 import)。
|
|
104
|
+
*
|
|
105
|
+
* argv[1] 可能缺席或是软链路径(pnpm 场景、`/tmp` → `/private/tmp`),两边折成 realpath
|
|
106
|
+
* 再比;裸字符串比较会让直跑分支被静默跳过,脚本挂在 prepare / prepack 上时等于发布
|
|
107
|
+
* 过期产物而不报错。
|
|
108
|
+
*
|
|
109
|
+
* 两种失败必须分开:argv[1] 不是本文件(被 import、或被别的脚本调用)→ 安静返回 false;
|
|
110
|
+
* 基名就是本文件却解不出 realpath(软链断了、父目录 EACCES、argv[1] 已被删除)→ 直接抛。
|
|
111
|
+
* 后者若也返回 false,脚本会既不构建也不报错、以 0 退出,发布链路认为构建成功而磁盘
|
|
112
|
+
* 产物仍是旧的。
|
|
113
|
+
* @param moduleFile - 调用方的 `import.meta.filename`
|
|
114
|
+
* @returns 是否直接运行
|
|
115
|
+
*/
|
|
116
|
+
export declare function invokedDirectly(moduleFile: string): boolean;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { RolldownFactory } from "./build-artifacts.js";
|
|
2
|
+
/**
|
|
3
|
+
* 宿主 seed 表里 react 家族的全部说明符(客户端产物只外置宿主真给得出的模块)。
|
|
4
|
+
* 与宿主客户端自己的模块表同源:那张表里 react 家族只有这四条。外置面更大的包
|
|
5
|
+
* (宿主外壳基座扩容后)由调用方在 `externals` 里给全量清单。
|
|
6
|
+
*/
|
|
7
|
+
export declare const REACT_RUNTIME_MODULES: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* 把 `//#region` 行里的 pnpm 内容寻址段(store 目录里带「名@版_哈希」的那一层)折成
|
|
10
|
+
* 普通的 `node_modules/` 一层。
|
|
11
|
+
*
|
|
12
|
+
* 为什么 client 侧需要而 host 侧不需要:host 把依赖全外部化,产物里没有依赖的模块标记;
|
|
13
|
+
* client 必须把共享库的模块**内联**(浏览器解析不了裸说明符),于是 rolldown 写下的标记里
|
|
14
|
+
* 烙着依赖版本与本机 store 哈希。带着它发布,同一份源码在不同机器上产出不同字节(跨机器
|
|
15
|
+
* 不可复现),还把安装布局公开进公共 tarball。
|
|
16
|
+
*
|
|
17
|
+
* 折叠必须覆盖整段而不是只换中段:store 段之上还可能带着 `../` 前缀(依赖落在工作区根
|
|
18
|
+
* 一侧时就是这样),只换中段会留下 `../../node_modules/<包名>/…`,产物字节仍然随检出
|
|
19
|
+
* 深度变,等于没折。所以判据锚在 `//#region ` 之后、允许任意个相对段。
|
|
20
|
+
*
|
|
21
|
+
* 反斜杠先归一:Windows 上 rolldown 可能写 `\`,而这行是注释、整行只有路径,归一不会
|
|
22
|
+
* 误伤代码。折叠完再验一遍「整份产物里不剩 `.pnpm` 段」——静默未命中是这条折法最坏的
|
|
23
|
+
* 失效形态:判据锚在 region 行的形状上,而 rolldown 一旦改写成别的行形态,折叠会一处都
|
|
24
|
+
* 不命中,兜底若也跟着只用同一套形状假设,就会一起静默失明。`node_modules/.pnpm/`
|
|
25
|
+
* 这个子串与行形态无关。
|
|
26
|
+
* @param code - chunk 正文
|
|
27
|
+
* @returns 折叠后的正文
|
|
28
|
+
*/
|
|
29
|
+
export declare function collapseStorePaths(code: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* 包名同源校验的上下文:校验逻辑在各包(它知道自己哪几个源文件带着包名字面量),
|
|
32
|
+
* 判据材料在这里。
|
|
33
|
+
*/
|
|
34
|
+
export interface ClientNameContext {
|
|
35
|
+
/** package.json 的 name(外壳 id 的真源)。 */
|
|
36
|
+
readonly pkgName: string;
|
|
37
|
+
/** 折叠后的 chunk 正文(未包外壳)。 */
|
|
38
|
+
readonly body: string;
|
|
39
|
+
/** 读包根下一份源码文件。 */
|
|
40
|
+
readonly readSource: (relative: string) => Promise<string>;
|
|
41
|
+
}
|
|
42
|
+
/** client 侧构建的完整配置。 */
|
|
43
|
+
export interface ClientBuildOptions {
|
|
44
|
+
/** 包根绝对路径(调用方的 `import.meta.dirname`)。 */
|
|
45
|
+
readonly root: string;
|
|
46
|
+
/** 调用方注入的 rolldown。 */
|
|
47
|
+
readonly rolldown: RolldownFactory;
|
|
48
|
+
/** 客户端入口(相对包根)。缺省 `src/client-entry.ts`。 */
|
|
49
|
+
readonly entry?: string;
|
|
50
|
+
/** 产物首行的 GENERATED 头(不含换行)。它是产物字节的一部分,逐字由调用方给定。 */
|
|
51
|
+
readonly header: string;
|
|
52
|
+
/** 外置说明符全量清单(精确匹配)。缺省 {@link REACT_RUNTIME_MODULES}。 */
|
|
53
|
+
readonly externals?: readonly string[];
|
|
54
|
+
/** 压缩产物。缺省不压。 */
|
|
55
|
+
readonly minify?: boolean;
|
|
56
|
+
/** 保留注释。缺省跟随 minify(压则剥,不压则留)。 */
|
|
57
|
+
readonly comments?: boolean;
|
|
58
|
+
/** 包名同源校验。槽位 key、`$mount` 的 package 位之类各包自带的字面量声明在这里核对;
|
|
59
|
+
* 缺省不校验。抛错即构建失败。 */
|
|
60
|
+
readonly verifyName?: (context: ClientNameContext) => Promise<void> | void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 客户端外置判据:登记过的精确说明符外置,react 家族里**没登记**的说明符当场失败。
|
|
64
|
+
*
|
|
65
|
+
* 为什么既不静默内联也不前缀放行:外壳注入的 require 只在宿主模块表里查键。前缀放行会
|
|
66
|
+
* 得到一条表里没有的 require(加载期 not in the module table,整张卡片静默不渲染);而
|
|
67
|
+
* 静默内联会把 node_modules 里那份 React 打进产物、与宿主那份并存,页面里两个 React
|
|
68
|
+
* 实例, hooks 直接报错。两种失败都不在构建期报错,所以这里没有可观察的中间态。
|
|
69
|
+
* 导出给各包的构建脚本复用与测试直接驱动,与构建走同一份判据。
|
|
70
|
+
* @param externals - 外置说明符全量清单(精确匹配)
|
|
71
|
+
* @returns rolldown 的 external 判据
|
|
72
|
+
*/
|
|
73
|
+
export declare function clientExternalPredicate(externals: readonly string[]): (id: string) => boolean;
|
|
74
|
+
/**
|
|
75
|
+
* 构建客户端产物的完整文本(含 ModuleLoader 外壳)。
|
|
76
|
+
* @param options - 见 {@link ClientBuildOptions}
|
|
77
|
+
* @returns 产物正文
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildClientBundle(options: ClientBuildOptions): Promise<string>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { RolldownPluginOption } from "rolldown";
|
|
2
|
+
import type { RolldownFactory } from "./build-artifacts.js";
|
|
3
|
+
/** 一个 host 入口:源码、产物与生成头。多个入口各自单独构建,互不共享 chunk。 */
|
|
4
|
+
export interface HostEntry {
|
|
5
|
+
/** 入口源码(相对包根,如 `host.ts`)。 */
|
|
6
|
+
readonly source: string;
|
|
7
|
+
/** 产物文件名(相对包根,如 `host.js`)。 */
|
|
8
|
+
readonly output: string;
|
|
9
|
+
/** 产物首行的 GENERATED 头(含行尾换行)。它是产物字节的一部分,逐字由调用方给定。 */
|
|
10
|
+
readonly header: string;
|
|
11
|
+
}
|
|
12
|
+
/** host 侧构建的完整配置。 */
|
|
13
|
+
export interface HostBuildOptions {
|
|
14
|
+
/** 包根绝对路径(调用方的 `import.meta.dirname`)。 */
|
|
15
|
+
readonly root: string;
|
|
16
|
+
/** 调用方注入的 rolldown。 */
|
|
17
|
+
readonly rolldown: RolldownFactory;
|
|
18
|
+
/** 入口表。每项单独一次构建:多输入会把公共依赖提成共享 chunk,这里要的是自包含单文件。 */
|
|
19
|
+
readonly entries: readonly HostEntry[];
|
|
20
|
+
/** 压缩产物。缺省不压;装载器只 `import()` 文本,注释与空白是真实装载成本。 */
|
|
21
|
+
readonly minify?: boolean;
|
|
22
|
+
/** 保留注释。缺省跟随 minify(压则剥,不压则留)。 */
|
|
23
|
+
readonly comments?: boolean;
|
|
24
|
+
/** rolldown 插件(如装饰器预转换)。缺省无。类型借 rolldown 自己的插件形状(仅类型导入)。 */
|
|
25
|
+
readonly plugins?: RolldownPluginOption;
|
|
26
|
+
/** 解析扩展名表,**替换** rolldown 默认值而非追加。缺省 `[".ts", ".mjs", ".js"]`;
|
|
27
|
+
* 入口会导入 `.json` 数据的包要显式带上 `.json`。 */
|
|
28
|
+
readonly resolveExtensions?: readonly string[];
|
|
29
|
+
/** 是否外部化 optionalDependencies。缺省外部化:「装上就用、没装也能跑」的语义由
|
|
30
|
+
* 运行期 require 决定,内联会把可选实现烤进产物、抹掉「可选」这层语义。
|
|
31
|
+
* 个别包刻意反向(可选依赖内联成「缺包时功能照旧」),传 false。 */
|
|
32
|
+
readonly externalizeOptional?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** 一个 host 产物的构建结果。`inlinedModules` 是结构性外部化门禁的判据面。 */
|
|
35
|
+
export interface HostArtifact {
|
|
36
|
+
readonly source: string;
|
|
37
|
+
readonly output: string;
|
|
38
|
+
/** 产物绝对路径。 */
|
|
39
|
+
readonly file: string;
|
|
40
|
+
/** 产物完整正文(含生成头)。 */
|
|
41
|
+
readonly code: string;
|
|
42
|
+
/** 被打进 chunk 的模块 id 清单。外部化的依赖不出现在这里——依赖表里的包名一旦
|
|
43
|
+
* 出现,就是有人把它从外部化名单里挪走了(内联)。 */
|
|
44
|
+
readonly inlinedModules: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 构建全部 host 入口。
|
|
48
|
+
*
|
|
49
|
+
* 各入口单独一次 rolldown 并发跑(它们互不依赖,串行只是在等第二次构建);产物正文
|
|
50
|
+
* 先按 `process.cwd()` 无关的 canonical 形态正典化,再交给调用方落盘。
|
|
51
|
+
* @param options - 见 {@link HostBuildOptions}
|
|
52
|
+
* @returns 与入口表同序的产物清单
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildHostEntries(options: HostBuildOptions): Promise<HostArtifact[]>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** 声明构建的配置。 */
|
|
2
|
+
export interface TypesBuildOptions {
|
|
3
|
+
/** 包根绝对路径(调用方的 `import.meta.dirname`)。 */
|
|
4
|
+
readonly root: string;
|
|
5
|
+
/** tsc 入口脚本的绝对路径(用 `process.execPath` 跑它,跨平台且不经 PATH / npx)。 */
|
|
6
|
+
readonly tscEntry: string;
|
|
7
|
+
/** tsconfig 项目名(相对包根)。缺省 `tsconfig.types.json`。 */
|
|
8
|
+
readonly tsconfig?: string;
|
|
9
|
+
/** 草稿目录绝对路径。缺省 `<root>/.tmp/types`(该目录在各包统一被 git 忽略,
|
|
10
|
+
* 且与 types/ 同一文件系统,最后一步 rename 原子且不跨设备)。 */
|
|
11
|
+
readonly scratch?: string;
|
|
12
|
+
/** 产物目录绝对路径。缺省 `<root>/types`。 */
|
|
13
|
+
readonly outDir?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 把一段声明正文里的相对源码后缀说明符改写成对侧产物后缀。
|
|
17
|
+
*
|
|
18
|
+
* 三处 replace 各自收一种句法位置,互不依赖;同一个文件里三种都出现时不漏也不重。
|
|
19
|
+
* @param text - 声明文件正文
|
|
20
|
+
* @returns 改写后的正文
|
|
21
|
+
*/
|
|
22
|
+
export declare function canonicalizeDeclarationSpecifiers(text: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* 把子进程的一路输出读成文本。
|
|
25
|
+
*
|
|
26
|
+
* `execFileSync` 不给 `encoding` 时两路都是 Buffer;按形态显式分支:文本原样用、Buffer
|
|
27
|
+
* 按 UTF-8 解、其余形态返回空串。写成 `String(stream)` 会把 Buffer 送进默认字符串化,
|
|
28
|
+
* 类型检查也会因为「可能是任意对象」而判红。
|
|
29
|
+
* @param stream - 子进程的一路输出
|
|
30
|
+
* @returns 文本
|
|
31
|
+
*/
|
|
32
|
+
export declare function textOf(stream: unknown): string;
|
|
33
|
+
/**
|
|
34
|
+
* 跑一条命令;失败时抛出的信息取子进程自己的诊断文本,而不是包装层的壳。
|
|
35
|
+
*
|
|
36
|
+
* 用同步版:这是构建步骤,阻塞等 tsc 跑完完全够用,异步包装在这里只会为了一个用不上
|
|
37
|
+
* 的并发度引入回调与 Promise 两种形态。两路都得收:tsc 把编译诊断写 **stdout**,工具
|
|
38
|
+
* 自身崩溃与 node 的用法错误写在 stderr,任取一路都会丢一半。
|
|
39
|
+
* @param file - 可执行文件(用 `process.execPath` 跑入口 JS,不经 npx:Windows 上 `npx`
|
|
40
|
+
* 实际是 `npx.cmd`,`execFileSync` 不带 `shell` 不按 PATHEXT 解析,一律 ENOENT)
|
|
41
|
+
* @param args - 参数
|
|
42
|
+
* @param cwd - 工作目录
|
|
43
|
+
*/
|
|
44
|
+
export declare function runTool(file: string, args: readonly string[], cwd: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* 递归列出目录下的声明文件(`/` 分隔,路径相对 `dir`)。
|
|
47
|
+
*
|
|
48
|
+
* 文件与目录分成两支再汇总:混在一个 map 里时,只有文件的那一支会变成一个没有 `await`
|
|
49
|
+
* 的 `async` 回调;拆开后两支各自理由完整。导出而非私藏:调用方的声明门禁要按同一口径
|
|
50
|
+
* 列 types/,在调用方重写一份遍历逻辑只会让两边的目录形状悄悄分叉。
|
|
51
|
+
* @param dir - 当前目录绝对路径
|
|
52
|
+
* @param prefix - 已走过的相对前缀(顶层调用传空串)
|
|
53
|
+
* @returns 命中的文件清单
|
|
54
|
+
*/
|
|
55
|
+
export declare function listDeclarations(dir: string, prefix: string): Promise<string[]>;
|
|
56
|
+
/**
|
|
57
|
+
* 产出 types/:跑 tsc 出声明,再逐个改写相对说明符,成功后整体换上。
|
|
58
|
+
* @param options - 见 {@link TypesBuildOptions}
|
|
59
|
+
* @returns 改写过的声明文件清单(相对 outDir、`/` 分隔)
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildDeclarations(options: TypesBuildOptions): Promise<string[]>;
|
|
@@ -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
|
-
* @
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
94
|
-
* @
|
|
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
|
}
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -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`
|
|
18
|
-
*
|
|
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
|
-
*
|
|
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) =>
|
|
8
|
+
export declare function settleLessonCall(call: () => unknown, onFailure: (reason: unknown) => unknown): void;
|
package/dist/types/locale.d.ts
CHANGED
|
@@ -35,8 +35,9 @@ export type MessagesCatalog<Messages> = Record<Locale, {
|
|
|
35
35
|
readonly [Key in keyof Messages]: string;
|
|
36
36
|
}>;
|
|
37
37
|
/**
|
|
38
|
-
* 把设置里的原始值归一成受支持的 locale
|
|
39
|
-
*
|
|
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。
|
package/dist/types/record.d.ts
CHANGED
|
@@ -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;
|
package/dist/types/text.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
export declare function truncateEnd(text: string, maxChars: number): string;
|
|
6
6
|
/**
|
|
7
7
|
* 保留后 `minChars` 个码元;切点切开代理对时丢掉首部那个孤立低代理。
|
|
8
|
-
* `minChars
|
|
8
|
+
* `minChars` 不足一枚码元(含 `<= 0`、NaN、`0 < minChars < 1`)与 `truncateEnd(text, 0)`
|
|
9
|
+
* 同口径返回空串,不返回整串。
|
|
9
10
|
*/
|
|
10
11
|
export declare function truncateStart(text: string, minChars: number): string;
|