@nudojs/core 1.15.2 → 1.16.0
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/{chunk-WKIRPUHZ.js → chunk-OM6SAY2M.js} +29846 -20390
- package/dist/{chunk-NXZSRLCT.js → chunk-POGFCYV2.js} +3999 -999
- package/dist/exec.d.ts +384 -38
- package/dist/exec.js +3 -1
- package/dist/index.d.ts +389 -23
- package/dist/index.js +1250 -336
- package/dist/internal.d.ts +72 -8
- package/dist/internal.js +147 -115
- package/dist/{projection-budget-07VnMktV.d.ts → projection-budget-DW-svmIf.d.ts} +178 -42
- package/dist/{run-DfloWX6i.d.ts → run-T9HWTziZ.d.ts} +65 -3
- package/package.json +1 -1
package/dist/internal.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { I as InterfaceSource, a as InterfaceTierOpts, p as parseSource, C as CheckIssue } from './projection-budget-
|
|
2
|
-
export { L as LeakBudget, b as LoadDepsFingerprint, M as MAX_PROMISE_MICROS, P as PROJECTION_MAX_DEPTH, c as ProjectionBudget, d as ProjectionStopReason, e as budgetAttributionSegs, f as defaultLeakBudget, g as exceedsBudget, h as exportedNameOfLocal, i as extractAllLoadSpecs, j as getPromiseMicrosLength, l as leakIfNeeded, k as loadModuleDepsFingerprint, m as maybeLeak, n as normPath, r as resetLeakCounter, o as resolveDepPath, s as sidecarSpecsOf, q as stablePathKey, t as stablePathKeyGraph, u as termDepth, v as termNodes } from './projection-budget-
|
|
3
|
-
import { A as Abs, T as Term, c as AbsModuleExports, d as AstEnv } from './run-
|
|
4
|
-
export { $ as $tryDigestSoft, e as $tryMarkSoft, f as $tryReleaseSoft, E as ERROR_FAMILY, g as EvalFallbackStats, M as MayThrowEffect, h as absLit, i as absNumLit, j as absStrLit, k as allAbsStr, l as clearPureCallMemo, m as clearPureMemo, n as enterEvalCallBudgetSession, o as errorTypeAbs, p as exitEvalCallBudgetSession, q as filterDeclaredThrows, r as filterGateThrows, s as filterIgnoredThrows, t as flushMayThrowEffects, u as formatThrowsAbs, v as getEvalCallBudgetState, w as getEvalFallbackStats, x as getMayThrowCollector, y as isThrowsIgnored, z as mayThrowEffectsToAbs, B as orphanMayThrowEffects, D as popMayThrowFrame, F as pushMayThrowFrame, G as recordMayThrow, H as resetEvalFallbackStats, I as runWithMayThrowSession, J as setMayThrowCollector, K as throwAbsToKinds, N as throwsKindCovered, O as withEvalBudgetOverride } from './run-
|
|
1
|
+
import { I as InterfaceSource, a as InterfaceTierOpts, p as parseSource, C as CheckIssue } from './projection-budget-DW-svmIf.js';
|
|
2
|
+
export { L as LeakBudget, b as LoadDepsFingerprint, M as MAX_PROMISE_MICROS, P as PROJECTION_MAX_DEPTH, c as ProjectionBudget, d as ProjectionStopReason, e as budgetAttributionSegs, f as defaultLeakBudget, g as exceedsBudget, h as exportedNameOfLocal, i as extractAllLoadSpecs, j as getPromiseMicrosLength, l as leakIfNeeded, k as loadModuleDepsFingerprint, m as maybeLeak, n as normPath, r as resetLeakCounter, o as resolveDepPath, s as sidecarSpecsOf, q as stablePathKey, t as stablePathKeyGraph, u as termDepth, v as termNodes } from './projection-budget-DW-svmIf.js';
|
|
3
|
+
import { A as Abs, T as Term, c as AbsModuleExports, R as RunTranspiledOptions, d as AstEnv } from './run-T9HWTziZ.js';
|
|
4
|
+
export { $ as $tryDigestSoft, e as $tryMarkSoft, f as $tryReleaseSoft, E as ERROR_FAMILY, g as EvalFallbackStats, M as MayThrowEffect, h as absLit, i as absNumLit, j as absStrLit, k as allAbsStr, l as clearPureCallMemo, m as clearPureMemo, n as enterEvalCallBudgetSession, o as errorTypeAbs, p as exitEvalCallBudgetSession, q as filterDeclaredThrows, r as filterGateThrows, s as filterIgnoredThrows, t as flushMayThrowEffects, u as formatThrowsAbs, v as getEvalCallBudgetState, w as getEvalFallbackStats, x as getMayThrowCollector, y as isThrowsIgnored, z as mayThrowEffectsToAbs, B as orphanMayThrowEffects, D as popMayThrowFrame, F as pushMayThrowFrame, G as recordMayThrow, H as resetEvalFallbackStats, I as runWithMayThrowSession, J as setMayThrowCollector, K as throwAbsToKinds, N as throwsKindCovered, O as withEvalBudgetOverride } from './run-T9HWTziZ.js';
|
|
5
5
|
import { File, Node } from '@babel/types';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -41,6 +41,14 @@ declare const DIAG: {
|
|
|
41
41
|
readonly dualEntry: "nudo:dual-entry";
|
|
42
42
|
readonly entryMayThrow: "nudo:entry-may-throw";
|
|
43
43
|
readonly mayThrow: "nudo:may-throw";
|
|
44
|
+
/**
|
|
45
|
+
* 模块**装载期必然抛**(顶层无条件 `throw`)⇒ 模块根本无法被导入 ⇒ 导出
|
|
46
|
+
* 全丢、L2 无证据。fail-closed:禁用句法证据(`throw` 语句)判「必抛」,
|
|
47
|
+
* **不**采信引擎的抽象域结论(宿主 API 的近似也会让引擎抛,那是引擎债,
|
|
48
|
+
* 程序本身能正常加载)。详见 `check-may-throw.ts` 的
|
|
49
|
+
* `definiteModuleThrowOf`。
|
|
50
|
+
*/
|
|
51
|
+
readonly moduleThrow: "nudo:module-throw";
|
|
44
52
|
readonly noSignature: "nudo:no-signature";
|
|
45
53
|
readonly builtinUnknown: "nudo:builtin-unknown";
|
|
46
54
|
readonly forkTruncated: "nudo:fork-truncated";
|
|
@@ -74,7 +82,27 @@ declare const DIAG: {
|
|
|
74
82
|
readonly moduleCycle: "nudo:module-cycle";
|
|
75
83
|
readonly moduleDepth: "nudo:module-depth";
|
|
76
84
|
readonly moduleMissing: "nudo:module-missing";
|
|
85
|
+
/**
|
|
86
|
+
* 模块**存在但不可读**(EACCES / EIO / EMFILE…)——与 moduleMissing
|
|
87
|
+
* (无此文件)严格区分(Bug 499):文件就在那儿,只是读不了;折叠成
|
|
88
|
+
* 「不存在」会让诊断与事实相反。service 面(analyzer-orchestrate-uncached)
|
|
89
|
+
* 已用同一码。
|
|
90
|
+
*/
|
|
91
|
+
readonly moduleUnreadable: "nudo:module-unreadable";
|
|
92
|
+
/**
|
|
93
|
+
* `.json` 依赖**存在但 JSON 语法非法**(Bug 457)——与 moduleMissing
|
|
94
|
+
* (无此文件)、moduleUnreadable(读不了)都不同:文件在、也读得到,
|
|
95
|
+
* 只是内容不是合法 JSON ⇒ 取不到任何值。
|
|
96
|
+
*/
|
|
97
|
+
readonly jsonInvalid: "nudo:json-invalid";
|
|
77
98
|
readonly internal: "nudo:internal";
|
|
99
|
+
/**
|
|
100
|
+
* `nudo migrate strip` 的**逐文件**失败(语法错 / TS-only 残留)——
|
|
101
|
+
* 机器面 `pathErrors[].code`。此前 CLI 层直写字面量(绕过注册表),
|
|
102
|
+
* 术语表反向对账(docs-coverage rule 19)只能靠 src 字面量扫描兜住;
|
|
103
|
+
* 进注册表后本码的「可发射」由 DIAG 单点保证。
|
|
104
|
+
*/
|
|
105
|
+
readonly migrateStripFailed: "nudo:migrate-strip-failed";
|
|
78
106
|
readonly pathEmptyDir: "nudo:path-empty-dir";
|
|
79
107
|
readonly pathIo: "nudo:path-io";
|
|
80
108
|
readonly pathMissingCallsite: "nudo:path-missing-callsite";
|
|
@@ -202,7 +230,20 @@ declare function stableCallId(obj: object): string;
|
|
|
202
230
|
*/
|
|
203
231
|
declare function truncatedAbs(): Abs;
|
|
204
232
|
/** 调用指纹:命名函数用 name+arg shapes;一等函数用对象身份。
|
|
205
|
-
* 实参可能是裸 JS 值(B run 里模块函数作实参)——不得裸读 shape。
|
|
233
|
+
* 实参可能是裸 JS 值(B run 里模块函数作实参)——不得裸读 shape。
|
|
234
|
+
*
|
|
235
|
+
* ⚠ **两种口径**(勿混用,勿互相替代):
|
|
236
|
+
* - 本函数 = **调用身份**键(shape 种类 + term 的**变量身份**)⇒ 服务
|
|
237
|
+
* **调用预算 / 环检测**:同一递归点必须撞出同键(term 相同 ⇒ 同键)。
|
|
238
|
+
* - pureValueKey = **值**键(形状内容 + 字面量值)⇒ 服务 **@nudo:pure
|
|
239
|
+
* 记忆化**:按**值**承诺 ⇒ 同值必须同键(变量身份不同但值相同 ⇒ 同键)。
|
|
240
|
+
*
|
|
241
|
+
* @nudo:pure 记忆化通道(kind = "pure" 且 id 为空 —— 两个 pure memo 站点
|
|
242
|
+
* 「$call 路径」与「$callNamed 宿主路径」的唯一调用形态)在此**分流**到值
|
|
243
|
+
* 基键:记忆化键若带**变量身份**,同一变量重绑定为不同值后仍命中旧缓存
|
|
244
|
+
* ⇒ **陈旧结果**(Bug 281)。预算/环检测通道(kind = absfn / absbody /
|
|
245
|
+
* coerce-hook / 命名调用名,且 id 非空)**口径不变**。
|
|
246
|
+
*/
|
|
206
247
|
declare function callBudgetKey(kind: string, id: string, args: unknown[]): string;
|
|
207
248
|
/** 记录被截断的递归(service 可映射为 nudo:recursion-truncated)。
|
|
208
249
|
* 返回先前 collector,便于嵌套 save/restore。 */
|
|
@@ -231,7 +272,7 @@ declare const MAX_EVAL_TOTAL_FORKS = 5000;
|
|
|
231
272
|
/**
|
|
232
273
|
* 专用标签:fork 截断不是「某个递归函数被截断」,不能复用函数名 label
|
|
233
274
|
* (否则 check 会误报 nudo:recursion-truncated)。service/check 按此标签
|
|
234
|
-
* 映射为 `nudo:fork-truncated`(
|
|
275
|
+
* 映射为 `nudo:fork-truncated`(info —— 预算观测,非质量失败)。
|
|
235
276
|
*/
|
|
236
277
|
declare const FORK_TRUNCATION_LABEL = "#fork-budget";
|
|
237
278
|
/**
|
|
@@ -262,8 +303,8 @@ declare function resetEvalForkBudget(): void;
|
|
|
262
303
|
/** $fork 入口:超限放弃该分支(调用方返回 unknown = 最保守,安全)。 */
|
|
263
304
|
declare function bumpEvalForkBudget(): boolean;
|
|
264
305
|
|
|
265
|
-
/**
|
|
266
|
-
*
|
|
306
|
+
/** 会话缓存全清时显式清空注册表(模块域随之死;弱引用本会自动回收,
|
|
307
|
+
* 这里是让「下一轮」的注册表从空开始,避免残留弱引用条目)。 */
|
|
267
308
|
declare function clearLiveImportRefreshers(): void;
|
|
268
309
|
|
|
269
310
|
/**
|
|
@@ -513,6 +554,17 @@ type CollectAbsInlaysOpts = InterfaceTierOpts & {
|
|
|
513
554
|
* generalizeFromAst 看不到跨模块 import → 调用塌缩 unknown(inlay 假空)。
|
|
514
555
|
*/
|
|
515
556
|
modules?: Record<string, AbsModuleExports | Record<string, unknown>>;
|
|
557
|
+
/**
|
|
558
|
+
* B run 注入包(modules / mocks / envGlobals / replacements / as)——
|
|
559
|
+
* 透传给 generalizeFromAst(与 CLI `buildCheckInjection` 产出的 inject
|
|
560
|
+
* 同型)。
|
|
561
|
+
*
|
|
562
|
+
* **缺省 = 行内指令全部缺席**(Bug 786):`@nudo:mock` 的依赖变成未绑定名、
|
|
563
|
+
* `@nudo:as` / `@nudo:replace` 的覆盖表为空、env 全局未绑定 ⇒ 推导链塌缩成
|
|
564
|
+
* `unknown`(Nudo 词汇里 = 「推断失败」)⇒ IDE 显示 unknown 而 CLI 门禁
|
|
565
|
+
* 显示 number(跨产品面自相矛盾)。调用方(LSP)须与 CLI 同源装配 inject。
|
|
566
|
+
*/
|
|
567
|
+
inject?: RunTranspiledOptions;
|
|
516
568
|
};
|
|
517
569
|
/**
|
|
518
570
|
* 收集源码中函数签名的 Abs inlay:
|
|
@@ -636,6 +688,11 @@ declare function toJsBindingIdent(name: string, used?: Set<string>): string;
|
|
|
636
688
|
* Object-literal / type-member key: bare ident or canonical integer,
|
|
637
689
|
* otherwise JSON-quoted. Leading-zero digit keys (`01`) stay quoted — bare
|
|
638
690
|
* `01` is a SyntaxError in modules/TS.
|
|
691
|
+
*
|
|
692
|
+
* `__proto__` 是**特例**(族 17 Bug 606):它虽然长得像普通标识符,但在
|
|
693
|
+
* **JS 对象字面量**里 `__proto__: v` 是**原型 setter**(不是自有属性)⇒
|
|
694
|
+
* 运行时该键根本不存在(zod 形状丢键)。⇒ 一律写成计算键 `["__proto__"]`
|
|
695
|
+
* —— 计算键是普通自有属性。
|
|
639
696
|
*/
|
|
640
697
|
declare function formatObjectKey(k: string): string;
|
|
641
698
|
/**
|
|
@@ -715,6 +772,10 @@ declare function classFromMethods(name: string, methods: MethodDef[], superClass
|
|
|
715
772
|
* P1:本地 `class Foo` 经 `export { Foo }` / `export { Foo as default }` /
|
|
716
773
|
* `export default Foo` 导出时同样登记实例方法。
|
|
717
774
|
*
|
|
775
|
+
* ⚠ 本清单 = **函数**普查面(与 service 面 `collectTopLevelFunctions` 逐字
|
|
776
|
+
* 对齐 ⇒ 改这里必须同步改 service 面)。**类导出入口**(`new C()`)是另一
|
|
777
|
+
* 个面 ⇒ 见 {@link listClassEntryNames}(check 面专用,不进函数普查)。
|
|
778
|
+
*
|
|
718
779
|
* 从 scan.ts 拆出:纯 AST 清单,无调用图状态。
|
|
719
780
|
*/
|
|
720
781
|
|
|
@@ -855,6 +916,9 @@ declare const OBJECT_PROTO_NAMES: Set<string>;
|
|
|
855
916
|
* - 闭 exact 对象:slots 是精确键集,非 OP 名缺失即确定缺失(OP 名经
|
|
856
917
|
* Object.prototype 存在,未建模 → 保守不抛)
|
|
857
918
|
* - 槽存在但值是字面量(prim/null/undefined 非函数)→ 确定抛
|
|
919
|
+
* - brand 接收者:内层闭合 obj 的槽表是实例面,表外名确定缺失
|
|
920
|
+
* (Bug 1044:env 声明的 brand 之前一律不判 ⇒ `Response.json(x).then(cb)`
|
|
921
|
+
* 假绿;原型面已建模的 brand 走 `brandProtoSurfaceModelled` 排除)
|
|
858
922
|
* 保守边界:open 对象(spread/assign 产物)、非 exact conf(create(proto)
|
|
859
923
|
* 的 path 产物)、OP 原型名一律不判抛——避免假抛 false positive。
|
|
860
924
|
*/
|
package/dist/internal.js
CHANGED
|
@@ -4,25 +4,33 @@ import {
|
|
|
4
4
|
checkInjectedDomainEvidence,
|
|
5
5
|
createDiagChannel,
|
|
6
6
|
createScopedDiagChannel,
|
|
7
|
+
escapeTemplateTypeFixed,
|
|
7
8
|
exportedNameOfLocal,
|
|
8
9
|
extractAllLoadSpecs,
|
|
9
10
|
fnFingerprints,
|
|
11
|
+
formatObjectKey,
|
|
10
12
|
generalizeFromAst,
|
|
11
13
|
generalizeSourceKeyPart,
|
|
12
14
|
getFnNameAndBodies,
|
|
13
15
|
hashSource,
|
|
14
16
|
interfaceTierOf,
|
|
17
|
+
isJsBindingIdent,
|
|
18
|
+
isJsIdent,
|
|
15
19
|
listTopFunctions,
|
|
16
20
|
loadModuleDepsFingerprint,
|
|
21
|
+
localNamedExports,
|
|
17
22
|
normPath,
|
|
18
23
|
resetFnFpCache,
|
|
19
24
|
resetHashSourceCache,
|
|
20
25
|
resolveDepPath,
|
|
26
|
+
safeMemberAccess,
|
|
27
|
+
sanitizeCommentText,
|
|
21
28
|
sidecarSpecsOf,
|
|
22
29
|
stableAnalyzeKeySource,
|
|
23
30
|
stablePathKey,
|
|
24
|
-
stablePathKeyGraph
|
|
25
|
-
|
|
31
|
+
stablePathKeyGraph,
|
|
32
|
+
toJsBindingIdent
|
|
33
|
+
} from "./chunk-POGFCYV2.js";
|
|
26
34
|
import {
|
|
27
35
|
$tryDigestSoft,
|
|
28
36
|
$tryMarkSoft,
|
|
@@ -40,11 +48,13 @@ import {
|
|
|
40
48
|
PROMISE_MICRO_ERROR_LABEL,
|
|
41
49
|
PROMISE_MICRO_OVERFLOW_LABEL,
|
|
42
50
|
ProjectionBudget,
|
|
51
|
+
SELF,
|
|
43
52
|
abortDerivationSession,
|
|
44
53
|
absLit,
|
|
45
54
|
absNumLit,
|
|
46
55
|
absStrLit,
|
|
47
56
|
absTemplateViews,
|
|
57
|
+
accessorReadDomain,
|
|
48
58
|
allAbsStr,
|
|
49
59
|
allFixedTextOfViews,
|
|
50
60
|
anyMemberResult,
|
|
@@ -52,6 +62,7 @@ import {
|
|
|
52
62
|
beginDerivationSession,
|
|
53
63
|
bumpEvalForkBudget,
|
|
54
64
|
callBudgetKey,
|
|
65
|
+
canonicalArrayIndex,
|
|
55
66
|
classChainNames,
|
|
56
67
|
classFromMethods,
|
|
57
68
|
clearLiveImportRefreshers,
|
|
@@ -104,6 +115,7 @@ import {
|
|
|
104
115
|
knownPrefixOfViews,
|
|
105
116
|
knownSuffixOfViews,
|
|
106
117
|
leakIfNeeded,
|
|
118
|
+
litKeyString,
|
|
107
119
|
litValue,
|
|
108
120
|
lookupMethod,
|
|
109
121
|
lookupMethodWithOwner,
|
|
@@ -162,6 +174,7 @@ import {
|
|
|
162
174
|
templateMatchesValue,
|
|
163
175
|
templatePartsOf,
|
|
164
176
|
termDepth,
|
|
177
|
+
termEquals,
|
|
165
178
|
termKey,
|
|
166
179
|
termNodes,
|
|
167
180
|
termToString,
|
|
@@ -172,17 +185,33 @@ import {
|
|
|
172
185
|
withEvalBudgetOverride,
|
|
173
186
|
withFnBudgetOverride,
|
|
174
187
|
wrapPromise
|
|
175
|
-
} from "./chunk-
|
|
188
|
+
} from "./chunk-OM6SAY2M.js";
|
|
176
189
|
|
|
177
190
|
// src/algebra/inlay.ts
|
|
178
191
|
function listFunctions(source) {
|
|
179
192
|
const file = parseSource(source);
|
|
180
193
|
const out = [];
|
|
194
|
+
const exported = localNamedExports(source);
|
|
181
195
|
const visitDecl = (decl) => {
|
|
182
196
|
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
183
197
|
const fd = decl;
|
|
184
198
|
out.push({ name: fd.id.name, node: fd });
|
|
185
199
|
}
|
|
200
|
+
if (decl.type === "ClassDeclaration") {
|
|
201
|
+
const cd = decl;
|
|
202
|
+
const clsName = cd.id?.name;
|
|
203
|
+
if (clsName) {
|
|
204
|
+
for (const m of cd.body?.body ?? []) {
|
|
205
|
+
if (m.type !== "ClassMethod") continue;
|
|
206
|
+
if (m.kind && m.kind !== "method") continue;
|
|
207
|
+
const keyName = m.key?.type === "Identifier" ? m.key.name : void 0;
|
|
208
|
+
if (!keyName) continue;
|
|
209
|
+
const fullName = `${clsName}.${keyName}`;
|
|
210
|
+
if (!exported.has(fullName)) continue;
|
|
211
|
+
out.push({ name: fullName, node: m });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
186
215
|
if (decl.type === "VariableDeclaration") {
|
|
187
216
|
const dcls = decl.declarations ?? [];
|
|
188
217
|
for (const d of dcls) {
|
|
@@ -194,11 +223,40 @@ function listFunctions(source) {
|
|
|
194
223
|
}
|
|
195
224
|
}
|
|
196
225
|
};
|
|
226
|
+
const isExportsTarget = (n) => {
|
|
227
|
+
if (!n) return false;
|
|
228
|
+
if (n.type === "Identifier") return n.name === "exports";
|
|
229
|
+
if (n.type === "MemberExpression") {
|
|
230
|
+
const me = n;
|
|
231
|
+
return me.object?.type === "Identifier" && me.object.name === "module" && me.property?.type === "Identifier" && me.property.name === "exports";
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
};
|
|
235
|
+
const visitCjsAssign = (stmt) => {
|
|
236
|
+
const expr = stmt.expression;
|
|
237
|
+
if (!expr || expr.type !== "AssignmentExpression") return;
|
|
238
|
+
const assign = expr;
|
|
239
|
+
if (assign.operator !== "=") return;
|
|
240
|
+
const left = assign.left;
|
|
241
|
+
const right = assign.right;
|
|
242
|
+
if (!left || !right || left.type !== "MemberExpression") return;
|
|
243
|
+
const me = left;
|
|
244
|
+
if (me.computed === true) return;
|
|
245
|
+
if (!isExportsTarget(me.object)) return;
|
|
246
|
+
const prop = me.property;
|
|
247
|
+
const propName = prop?.type === "Identifier" ? prop.name : void 0;
|
|
248
|
+
if (!propName) return;
|
|
249
|
+
if (!exported.has(propName)) return;
|
|
250
|
+
if (right.type !== "FunctionExpression" && right.type !== "ArrowFunctionExpression") return;
|
|
251
|
+
out.push({ name: propName, node: right });
|
|
252
|
+
};
|
|
197
253
|
for (const stmt of file.program.body) {
|
|
198
254
|
if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
|
|
199
255
|
visitDecl(stmt.declaration);
|
|
200
256
|
} else if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration) {
|
|
201
257
|
visitDecl(stmt.declaration);
|
|
258
|
+
} else if (stmt.type === "ExpressionStatement") {
|
|
259
|
+
visitCjsAssign(stmt);
|
|
202
260
|
} else {
|
|
203
261
|
visitDecl(stmt);
|
|
204
262
|
}
|
|
@@ -213,6 +271,17 @@ function renameTypeVars(s, varMap) {
|
|
|
213
271
|
}
|
|
214
272
|
return out;
|
|
215
273
|
}
|
|
274
|
+
var INTERNAL_LEN_ID_RE = /^len#\d+$/;
|
|
275
|
+
var READABLE_LEN_ID = "len";
|
|
276
|
+
function readableTerm(t) {
|
|
277
|
+
if (t.op === "var") {
|
|
278
|
+
return INTERNAL_LEN_ID_RE.test(t.id) ? { op: "var", id: READABLE_LEN_ID } : t;
|
|
279
|
+
}
|
|
280
|
+
if (t.op === "app") {
|
|
281
|
+
return { op: "app", fn: t.fn, args: t.args.map(readableTerm) };
|
|
282
|
+
}
|
|
283
|
+
return t;
|
|
284
|
+
}
|
|
216
285
|
function formatReturnDisplay(g) {
|
|
217
286
|
const varMap = /* @__PURE__ */ new Map();
|
|
218
287
|
for (let i = 0; i < (g.typeParams?.length ?? 0); i++) {
|
|
@@ -221,28 +290,28 @@ function formatReturnDisplay(g) {
|
|
|
221
290
|
if (pname) varMap.set(id, pname);
|
|
222
291
|
}
|
|
223
292
|
const stripParens = (t) => t.startsWith("(") && t.endsWith(")") ? t.slice(1, -1) : t;
|
|
293
|
+
const termText = (t) => stripParens(renameTypeVars(termToString(readableTerm(t)), varMap));
|
|
224
294
|
const memberPath = (m) => {
|
|
225
295
|
if (m.term?.op === "var") {
|
|
226
|
-
const name = renameTypeVars(m.term.id, varMap);
|
|
296
|
+
const name = INTERNAL_LEN_ID_RE.test(m.term.id) ? READABLE_LEN_ID : renameTypeVars(m.term.id, varMap);
|
|
227
297
|
return { key: `var:${name}`, text: name };
|
|
228
298
|
}
|
|
229
299
|
if (m.term?.op === "app") {
|
|
230
|
-
const t =
|
|
300
|
+
const t = termText(m.term);
|
|
231
301
|
return { key: `app:${t}`, text: t };
|
|
232
302
|
}
|
|
233
303
|
const lvR = litValue(m);
|
|
234
304
|
if (lvR.ok) {
|
|
235
305
|
const lv = lvR.value;
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
return { key: `lit:${String(lv)}`, text: JSON.stringify(lv) ?? "undefined" };
|
|
306
|
+
const text = termToString({ op: "lit", value: lv });
|
|
307
|
+
const key = `lit:${typeof lv}:${litKeyString(lv)}`;
|
|
308
|
+
return { key, text };
|
|
240
309
|
}
|
|
241
310
|
return { key: `shape:${formatShape(m)}`, text: formatShape(m) };
|
|
242
311
|
};
|
|
243
312
|
const abs = g.symbolic;
|
|
244
313
|
if (abs.term && abs.term.op !== "lit") {
|
|
245
|
-
return
|
|
314
|
+
return termText(abs.term);
|
|
246
315
|
}
|
|
247
316
|
if (abs.shape.k === "sum") {
|
|
248
317
|
const parts = [];
|
|
@@ -269,7 +338,10 @@ function collectAbsInlays(source, opts) {
|
|
|
269
338
|
try {
|
|
270
339
|
g = generalizeFromAst(name, source, {
|
|
271
340
|
...refineOpts ? { refine: refineOpts } : {},
|
|
272
|
-
...opts?.modules ? { modules: opts.modules } : {}
|
|
341
|
+
...opts?.modules ? { modules: opts.modules } : {},
|
|
342
|
+
// 注入包透传(Bug 786):@nudo:mock / @nudo:as / @nudo:replace /
|
|
343
|
+
// env 全局缺席 ⇒ 推导链塌缩 unknown(与 CLI 面分叉)。
|
|
344
|
+
...opts?.inject ? { inject: opts.inject } : {}
|
|
273
345
|
});
|
|
274
346
|
} catch {
|
|
275
347
|
continue;
|
|
@@ -311,8 +383,10 @@ function collectAbsInlays(source, opts) {
|
|
|
311
383
|
});
|
|
312
384
|
}
|
|
313
385
|
}
|
|
314
|
-
const
|
|
315
|
-
|
|
386
|
+
const bodyNode = fnNode.body;
|
|
387
|
+
const bodyLoc = bodyNode?.loc;
|
|
388
|
+
const anchor = bodyNode?.type === "BlockStatement" ? bodyLoc?.start : bodyLoc?.end ?? bodyLoc?.start;
|
|
389
|
+
if (anchor) {
|
|
316
390
|
let label;
|
|
317
391
|
try {
|
|
318
392
|
label = `: ${formatReturnDisplay(g)}`;
|
|
@@ -321,8 +395,8 @@ function collectAbsInlays(source, opts) {
|
|
|
321
395
|
}
|
|
322
396
|
if (isImplicitExport) label += " \xB7 derived";
|
|
323
397
|
inlays.push({
|
|
324
|
-
line:
|
|
325
|
-
character: Math.max(0,
|
|
398
|
+
line: anchor.line,
|
|
399
|
+
character: Math.max(0, anchor.column),
|
|
326
400
|
label: `${label} `,
|
|
327
401
|
kind: "type",
|
|
328
402
|
...tierSrc ? { interfaceSource: tierSrc } : {},
|
|
@@ -333,103 +407,35 @@ function collectAbsInlays(source, opts) {
|
|
|
333
407
|
return inlays;
|
|
334
408
|
}
|
|
335
409
|
|
|
336
|
-
// src/algebra/
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
"
|
|
343
|
-
"
|
|
344
|
-
"
|
|
345
|
-
"
|
|
346
|
-
"
|
|
347
|
-
"
|
|
348
|
-
"
|
|
349
|
-
"
|
|
350
|
-
"
|
|
351
|
-
"else",
|
|
352
|
-
"enum",
|
|
353
|
-
"export",
|
|
354
|
-
"extends",
|
|
355
|
-
"false",
|
|
356
|
-
"finally",
|
|
357
|
-
"for",
|
|
358
|
-
"function",
|
|
359
|
-
"if",
|
|
360
|
-
"import",
|
|
361
|
-
"in",
|
|
362
|
-
"instanceof",
|
|
363
|
-
"new",
|
|
364
|
-
"null",
|
|
365
|
-
"return",
|
|
366
|
-
"super",
|
|
367
|
-
"switch",
|
|
368
|
-
"this",
|
|
369
|
-
"throw",
|
|
370
|
-
"true",
|
|
371
|
-
"try",
|
|
372
|
-
"typeof",
|
|
373
|
-
"var",
|
|
374
|
-
"void",
|
|
375
|
-
"while",
|
|
376
|
-
"with",
|
|
377
|
-
"yield",
|
|
378
|
-
// strict mode / module-code reserved
|
|
379
|
-
"let",
|
|
380
|
-
"static",
|
|
381
|
-
"await",
|
|
382
|
-
"implements",
|
|
383
|
-
"interface",
|
|
384
|
-
"package",
|
|
385
|
-
"private",
|
|
386
|
-
"protected",
|
|
387
|
-
"public",
|
|
388
|
-
// never legal bindings in strict / module code
|
|
410
|
+
// src/algebra/denote.ts
|
|
411
|
+
function isHiddenInternalSlot(k, slot) {
|
|
412
|
+
return slot.internal === true && canonicalArrayIndex(k) === void 0;
|
|
413
|
+
}
|
|
414
|
+
var PROTOTYPE_LESS_GLOBALS = /* @__PURE__ */ new Set([
|
|
415
|
+
// 可调用但没有对象 prototype
|
|
416
|
+
"Proxy",
|
|
417
|
+
"parseFloat",
|
|
418
|
+
"parseInt",
|
|
419
|
+
"decodeURI",
|
|
420
|
+
"decodeURIComponent",
|
|
421
|
+
"encodeURI",
|
|
422
|
+
"encodeURIComponent",
|
|
423
|
+
"escape",
|
|
424
|
+
"unescape",
|
|
389
425
|
"eval",
|
|
390
|
-
"
|
|
426
|
+
"isFinite",
|
|
427
|
+
"isNaN",
|
|
428
|
+
// 不可调用(非构造器)
|
|
429
|
+
"Math",
|
|
430
|
+
"JSON",
|
|
431
|
+
"Intl",
|
|
432
|
+
"Reflect",
|
|
433
|
+
"Atomics",
|
|
434
|
+
"globalThis"
|
|
391
435
|
]);
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return JS_IDENT.test(name);
|
|
395
|
-
}
|
|
396
|
-
function isJsBindingIdent(name) {
|
|
397
|
-
return JS_BINDING_IDENT.test(name) && !JS_BINDING_RESERVED.has(name);
|
|
398
|
-
}
|
|
399
|
-
function toJsBindingIdent(name, used) {
|
|
400
|
-
let base = name;
|
|
401
|
-
if (!JS_BINDING_IDENT.test(base)) {
|
|
402
|
-
base = base.replace(/[^\p{ID_Continue}$\u200C\u200D]/gu, "_");
|
|
403
|
-
if (base.length === 0) base = "_";
|
|
404
|
-
else if (!/^[$_\p{ID_Start}]/u.test(base)) base = `_${base}`;
|
|
405
|
-
}
|
|
406
|
-
if (JS_BINDING_RESERVED.has(base)) base = `_${base}`;
|
|
407
|
-
if (!used) return base;
|
|
408
|
-
let n = base;
|
|
409
|
-
let i = 2;
|
|
410
|
-
while (used.has(n)) {
|
|
411
|
-
n = `${base}_${i}`;
|
|
412
|
-
i++;
|
|
413
|
-
}
|
|
414
|
-
used.add(n);
|
|
415
|
-
return n;
|
|
416
|
-
}
|
|
417
|
-
function formatObjectKey(k) {
|
|
418
|
-
if (JS_IDENT.test(k)) return k;
|
|
419
|
-
if (JS_CANONICAL_INT.test(k)) return k;
|
|
420
|
-
return JSON.stringify(k);
|
|
421
|
-
}
|
|
422
|
-
function safeMemberAccess(recv, key) {
|
|
423
|
-
return JS_IDENT.test(key) ? `${recv}.${key}` : `${recv}[${JSON.stringify(key)}]`;
|
|
424
|
-
}
|
|
425
|
-
function escapeTemplateTypeFixed(text) {
|
|
426
|
-
return text.replace(/[\\`$]/g, "\\$&");
|
|
436
|
+
function hasUnusablePrototype(name) {
|
|
437
|
+
return PROTOTYPE_LESS_GLOBALS.has(name);
|
|
427
438
|
}
|
|
428
|
-
function sanitizeCommentText(text) {
|
|
429
|
-
return text.replace(/\*\//g, "*\\/").replace(/[\r\n\u2028\u2029\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, " ");
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// src/algebra/denote.ts
|
|
433
439
|
function denoteGuard(a, v) {
|
|
434
440
|
return denoteGuardB(a, v, new ProjectionBudget());
|
|
435
441
|
}
|
|
@@ -440,7 +446,7 @@ function denoteGuardB(a, v, budget) {
|
|
|
440
446
|
if (a.term?.op === "lit" && (!a.pred || a.pred.op === "true")) {
|
|
441
447
|
return eqGuard(v, a.term.value);
|
|
442
448
|
}
|
|
443
|
-
const shape = denoteShape(a
|
|
449
|
+
const shape = denoteShape(a, v, budget);
|
|
444
450
|
const pred = denotePred(a, v);
|
|
445
451
|
if (shape === "true") return pred;
|
|
446
452
|
if (pred === "true") return shape;
|
|
@@ -449,7 +455,8 @@ function denoteGuardB(a, v, budget) {
|
|
|
449
455
|
budget.exit();
|
|
450
456
|
}
|
|
451
457
|
}
|
|
452
|
-
function denoteShape(
|
|
458
|
+
function denoteShape(a, v, budget) {
|
|
459
|
+
const s = a.shape;
|
|
453
460
|
switch (s.k) {
|
|
454
461
|
case "never":
|
|
455
462
|
return "false";
|
|
@@ -472,13 +479,16 @@ function denoteShape(s, v, budget) {
|
|
|
472
479
|
return "true";
|
|
473
480
|
}
|
|
474
481
|
case "obj": {
|
|
475
|
-
const checks = [`typeof ${v} === "object"`, `${v} !== null`];
|
|
482
|
+
const checks = [`typeof ${v} === "object"`, `${v} !== null`, `!Array.isArray(${v})`];
|
|
476
483
|
for (const [key, slot] of Object.entries(s.slots)) {
|
|
484
|
+
if (isHiddenInternalSlot(key, slot)) continue;
|
|
477
485
|
const access = safeMemberAccess(v, key);
|
|
478
|
-
const
|
|
486
|
+
const slotAbs = accessorReadDomain(a, key) ?? slot.value;
|
|
487
|
+
const inner = denoteGuardB(slotAbs, access, budget);
|
|
479
488
|
if (slot.optional) {
|
|
480
489
|
if (inner !== "true") checks.push(`(${access} === undefined || ${inner})`);
|
|
481
490
|
} else {
|
|
491
|
+
checks.push(hasPropertyClause(v, key));
|
|
482
492
|
if (inner !== "true") checks.push(inner);
|
|
483
493
|
}
|
|
484
494
|
}
|
|
@@ -515,8 +525,13 @@ function denoteShape(s, v, budget) {
|
|
|
515
525
|
case "eff":
|
|
516
526
|
if (s.eff === "promise") return `${v} instanceof Promise`;
|
|
517
527
|
return "true";
|
|
518
|
-
case "brand":
|
|
519
|
-
|
|
528
|
+
case "brand": {
|
|
529
|
+
const inner = denoteShape(s.shape, v, budget);
|
|
530
|
+
if (hasUnusablePrototype(s.name)) return inner;
|
|
531
|
+
const identity = brandIdentityClause(s.name, v);
|
|
532
|
+
if (inner === "true") return identity;
|
|
533
|
+
return `(${identity}) && (${inner})`;
|
|
534
|
+
}
|
|
520
535
|
case "sum": {
|
|
521
536
|
if (s.members.length === 0) return "false";
|
|
522
537
|
const parts = s.members.map((m) => denoteGuardB(m, v, budget));
|
|
@@ -566,12 +581,29 @@ function predAsJs(p, v, a) {
|
|
|
566
581
|
if (leftLitTerm && rightLitTerm) {
|
|
567
582
|
return `${jsLit(leftLitTerm.value)} ${op} ${jsLit(rightLitTerm.value)}`;
|
|
568
583
|
}
|
|
584
|
+
if (rightLitTerm && typeof rightLitTerm.value === "number" && isLengthSelf(p.a, a)) {
|
|
585
|
+
return `${v}.length ${op} ${rightLitTerm.value}`;
|
|
586
|
+
}
|
|
569
587
|
return "true";
|
|
570
588
|
}
|
|
571
589
|
default:
|
|
572
590
|
return "true";
|
|
573
591
|
}
|
|
574
592
|
}
|
|
593
|
+
function hasPropertyClause(v, key) {
|
|
594
|
+
const k = JSON.stringify(key);
|
|
595
|
+
return `(Object.hasOwn(${v}, ${k}) || (${k} in ${v} && !Object.hasOwn(Object.prototype, ${k})))`;
|
|
596
|
+
}
|
|
597
|
+
function brandIdentityClause(name, v) {
|
|
598
|
+
const ctor = `Reflect.get(globalThis, ${JSON.stringify(name)})`;
|
|
599
|
+
return `(typeof ${ctor} !== "function" || ${v} instanceof ${ctor})`;
|
|
600
|
+
}
|
|
601
|
+
function isLengthSelf(t, a) {
|
|
602
|
+
if (t.op !== "app" || t.fn !== "length" || t.args.length !== 1) return false;
|
|
603
|
+
const arg = t.args[0];
|
|
604
|
+
if (a.term && termEquals(arg, a.term)) return true;
|
|
605
|
+
return arg.op === "var" && arg.id === SELF;
|
|
606
|
+
}
|
|
575
607
|
function jsLit(v) {
|
|
576
608
|
if (v === void 0) return "undefined";
|
|
577
609
|
if (v === null) return "null";
|