@nudojs/cli 0.2.1 → 0.3.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/package.json +12 -7
- package/src/abs-route.ts +273 -0
- package/src/builtins/builtin-prototype.ts +279 -0
- package/src/builtins/builtin-static.ts +260 -0
- package/src/eval-binary.ts +113 -0
- package/src/evaluator-api.ts +3 -0
- package/src/evaluator.ts +174 -633
- package/src/index.ts +373 -270
- package/src/narrowing.ts +7 -0
- package/src/phi-from-test.ts +42 -0
- package/src/run-test.ts +92 -0
- package/src/term-registry.ts +96 -0
- package/CHANGELOG.md +0 -41
- package/dist/chunk-3HCPXPEW.js +0 -5286
- package/dist/evaluator-api.d.ts +0 -151
- package/dist/evaluator-api.js +0 -214
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -4035
- package/examples/real-world.js +0 -66
- package/examples/validate-real-world.ts +0 -160
- package/src/__tests__/builtin-advanced.test.ts +0 -292
- package/src/__tests__/builtin-api.test.ts +0 -308
- package/src/__tests__/builtin-functions.test.ts +0 -313
- package/src/__tests__/builtin-map.test.ts +0 -129
- package/src/__tests__/builtin-promise.test.ts +0 -150
- package/src/__tests__/builtin-regexp.test.ts +0 -133
- package/src/__tests__/builtin-set.test.ts +0 -163
- package/src/__tests__/builtin-url.test.ts +0 -256
- package/src/__tests__/builtin-web.test.ts +0 -340
- package/src/__tests__/combination-scenarios.test.ts +0 -384
- package/src/__tests__/edge-cases.test.ts +0 -749
- package/src/__tests__/evaluator-loops.test.ts +0 -283
- package/src/__tests__/evaluator-nullish-coalescing.test.ts +0 -86
- package/src/__tests__/evaluator-optional-chaining.test.ts +0 -37
- package/src/__tests__/evaluator-phase2.test.ts +0 -564
- package/src/__tests__/evaluator-phase3.test.ts +0 -864
- package/src/__tests__/evaluator-refined.test.ts +0 -121
- package/src/__tests__/evaluator-string-methods.test.ts +0 -151
- package/src/__tests__/evaluator.test.ts +0 -943
- package/src/__tests__/mock-arrow-function.test.ts +0 -169
- package/src/__tests__/mock-comparison.test.ts +0 -221
- package/src/__tests__/mock-comprehensive.test.ts +0 -250
- package/src/__tests__/mock-final-integration.test.ts +0 -263
- package/src/__tests__/mock-helpers.test.ts +0 -174
- package/src/__tests__/mock-realworld.test.ts +0 -191
- package/src/__tests__/mock-rest-debug.test.ts +0 -110
- package/src/__tests__/mock-rest-edge.test.ts +0 -176
- package/src/__tests__/mock-sinon-runtime.test.ts +0 -168
- package/src/__tests__/narrowing-discriminated.test.ts +0 -35
- package/src/__tests__/narrowing-in-operator.test.ts +0 -56
- package/src/__tests__/narrowing-switch.test.ts +0 -56
- package/src/__tests__/narrowing-truthiness.test.ts +0 -52
- package/src/__tests__/narrowing.test.ts +0 -82
- package/src/__tests__/nudo-mock-syntax.test.ts +0 -245
- package/src/__tests__/realworld-patterns.test.ts +0 -430
- package/src/__tests__/syntax-sugar.test.ts +0 -345
- package/src/builtins/index.ts +0 -61
- package/tsconfig.json +0 -9
package/src/narrowing.ts
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
typeValueEquals,
|
|
10
10
|
isSubtypeOf,
|
|
11
11
|
createRange,
|
|
12
|
+
v as termVar,
|
|
12
13
|
} from "@nudojs/core";
|
|
14
|
+
import { attachTerm } from "./term-registry.ts";
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Given a test expression and the current environment, produce two
|
|
@@ -452,6 +454,11 @@ function narrowByComparison(
|
|
|
452
454
|
: op === "<=" ? createRange({ min: value + 1 })
|
|
453
455
|
: createRange({ min: value });
|
|
454
456
|
|
|
457
|
+
// 保留项身份:range 界经 toAbsWithTerms 编码为 Pred,代数继续传播
|
|
458
|
+
const term = termVar(varName);
|
|
459
|
+
attachTerm(trueRange, term);
|
|
460
|
+
attachTerm(falseRange, term);
|
|
461
|
+
|
|
455
462
|
trueEnv.bind(varName, trueRange);
|
|
456
463
|
falseEnv.bind(varName, falseRange);
|
|
457
464
|
return [trueEnv, falseEnv];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 从 Babel 测试表达式提取代数 Φ 约束。
|
|
3
|
+
* 支持 `x > n` / `x >= n` / `x < n` / `x <= n`(Identifier × NumericLiteral)。
|
|
4
|
+
* false 分支用对偶比较(`>` ↔ `≤`),使 else 路径也能携带约束。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Node } from "@babel/types";
|
|
8
|
+
import type { Phi } from "@nudojs/core";
|
|
9
|
+
import { pTrue, and as phiAnd, gt, ge, lt, le, v as termVar, lit } from "@nudojs/core";
|
|
10
|
+
|
|
11
|
+
export function phiFromTest(test: Node): { whenTrue: Phi; whenFalse: Phi } {
|
|
12
|
+
if (test.type !== "BinaryExpression") {
|
|
13
|
+
return { whenTrue: pTrue, whenFalse: pTrue };
|
|
14
|
+
}
|
|
15
|
+
const op = test.operator;
|
|
16
|
+
const left = test.left;
|
|
17
|
+
const right = test.right;
|
|
18
|
+
|
|
19
|
+
// x ≷ n
|
|
20
|
+
if (
|
|
21
|
+
left.type === "Identifier" &&
|
|
22
|
+
right.type === "NumericLiteral" &&
|
|
23
|
+
(op === ">" || op === ">=" || op === "<" || op === "<=")
|
|
24
|
+
) {
|
|
25
|
+
const t = termVar(left.name);
|
|
26
|
+
const n = lit(right.value);
|
|
27
|
+
const pred =
|
|
28
|
+
op === ">" ? gt(t, n) : op === ">=" ? ge(t, n) : op === "<" ? lt(t, n) : le(t, n);
|
|
29
|
+
// 对偶:¬(a≷b) = a≲b(数值全序)
|
|
30
|
+
const dual =
|
|
31
|
+
op === ">" ? le(t, n) : op === ">=" ? lt(t, n) : op === "<" ? ge(t, n) : gt(t, n);
|
|
32
|
+
return { whenTrue: pred, whenFalse: dual };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { whenTrue: pTrue, whenFalse: pTrue };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function combinePhi(base: Phi, extra: Phi): Phi {
|
|
39
|
+
if (extra.op === "true") return base;
|
|
40
|
+
if (base.op === "true") return extra;
|
|
41
|
+
return phiAnd(base, extra);
|
|
42
|
+
}
|
package/src/run-test.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nudo test — @nudo:case 即测试。
|
|
3
|
+
*
|
|
4
|
+
* directive case 带 `=> expected` 时,用 isSubtypeOf 校验推断结果;
|
|
5
|
+
* 失败记 FAIL,进程退出码 1。无期望的 directive case 记 unchecked。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { isSubtypeOf, typeValueToString, type TypeValue } from "@nudojs/core";
|
|
9
|
+
import type { AnalysisResult } from "@nudojs/service";
|
|
10
|
+
|
|
11
|
+
export type CaseTestOutcome = {
|
|
12
|
+
fn: string;
|
|
13
|
+
caseName: string;
|
|
14
|
+
ok: boolean;
|
|
15
|
+
unchecked: boolean;
|
|
16
|
+
expected?: string;
|
|
17
|
+
actual: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type TestReport = {
|
|
21
|
+
file: string;
|
|
22
|
+
passed: number;
|
|
23
|
+
failed: number;
|
|
24
|
+
unchecked: number;
|
|
25
|
+
outcomes: CaseTestOutcome[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function buildTestReport(file: string, result: AnalysisResult): TestReport {
|
|
29
|
+
const outcomes: CaseTestOutcome[] = [];
|
|
30
|
+
for (const fn of result.functions) {
|
|
31
|
+
for (const c of fn.cases) {
|
|
32
|
+
if (c.source !== "directive") continue;
|
|
33
|
+
const actual = typeValueToString(c.result);
|
|
34
|
+
if (!c.expected) {
|
|
35
|
+
outcomes.push({
|
|
36
|
+
fn: fn.name,
|
|
37
|
+
caseName: c.name,
|
|
38
|
+
ok: true,
|
|
39
|
+
unchecked: true,
|
|
40
|
+
actual,
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const expected = typeValueToString(c.expected as TypeValue);
|
|
45
|
+
let ok = false;
|
|
46
|
+
try {
|
|
47
|
+
ok = isSubtypeOf(c.result, c.expected as TypeValue);
|
|
48
|
+
} catch {
|
|
49
|
+
ok = false;
|
|
50
|
+
}
|
|
51
|
+
outcomes.push({
|
|
52
|
+
fn: fn.name,
|
|
53
|
+
caseName: c.name,
|
|
54
|
+
ok,
|
|
55
|
+
unchecked: false,
|
|
56
|
+
expected,
|
|
57
|
+
actual,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
file,
|
|
63
|
+
passed: outcomes.filter((o) => o.ok && !o.unchecked).length,
|
|
64
|
+
failed: outcomes.filter((o) => !o.ok).length,
|
|
65
|
+
unchecked: outcomes.filter((o) => o.unchecked).length,
|
|
66
|
+
outcomes,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function formatTestReport(r: TestReport): string {
|
|
71
|
+
const lines: string[] = [];
|
|
72
|
+
lines.push(`nudo test ${r.file}`);
|
|
73
|
+
if (r.passed + r.failed + r.unchecked === 0) {
|
|
74
|
+
lines.push("No @nudo:case directives found.");
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
77
|
+
lines.push(r.failed === 0 ? "PASS" : "FAILED");
|
|
78
|
+
lines.push(` ${r.passed} passed · ${r.failed} failed · ${r.unchecked} unchecked`);
|
|
79
|
+
lines.push("");
|
|
80
|
+
for (const o of r.outcomes) {
|
|
81
|
+
if (o.unchecked) {
|
|
82
|
+
lines.push(` [unchecked] ${o.fn} case "${o.caseName}" → ${o.actual}`);
|
|
83
|
+
} else if (o.ok) {
|
|
84
|
+
lines.push(` [ok] ${o.fn} case "${o.caseName}" → ${o.actual}`);
|
|
85
|
+
} else {
|
|
86
|
+
lines.push(` [FAIL] ${o.fn} case "${o.caseName}"`);
|
|
87
|
+
lines.push(` expected: ${o.expected}`);
|
|
88
|
+
lines.push(` actual: ${o.actual}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeValue 项(term)身份旁路。
|
|
3
|
+
*
|
|
4
|
+
* core 的 TypeValue 无 term 字段;用 WeakMap 旁路挂载,零侵入。
|
|
5
|
+
* 纪律:
|
|
6
|
+
* - 禁止给共享单例(T.number 等)挂 term——必须先 clone 再 attach
|
|
7
|
+
* - typeValueToAbs 优先读旁路;无旁路时按 kind 推导
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { TypeValue } from "@nudojs/core";
|
|
11
|
+
import { T } from "@nudojs/core";
|
|
12
|
+
import type { Term } from "@nudojs/core";
|
|
13
|
+
import { v as termVar, lit } from "@nudojs/core";
|
|
14
|
+
import type { Pred } from "@nudojs/core";
|
|
15
|
+
|
|
16
|
+
const termByTv = new WeakMap<object, Term>();
|
|
17
|
+
const predByTv = new WeakMap<object, Pred>();
|
|
18
|
+
|
|
19
|
+
export function attachTerm(tv: TypeValue, term: Term): void {
|
|
20
|
+
if (tv && typeof tv === "object") termByTv.set(tv as object, term);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getTerm(tv: TypeValue): Term | undefined {
|
|
24
|
+
if (tv && typeof tv === "object") return termByTv.get(tv as object);
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function attachPred(tv: TypeValue, pred: Pred): void {
|
|
29
|
+
if (tv && typeof tv === "object") predByTv.set(tv as object, pred);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getPred(tv: TypeValue): Pred | undefined {
|
|
33
|
+
if (tv && typeof tv === "object") return predByTv.get(tv as object);
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 为参数绑定克隆 TypeValue 并挂 term。
|
|
39
|
+
* - primitive number → clone + var(name)
|
|
40
|
+
* - literal number → clone + lit(value)(与 kind 推导一致,但显式)
|
|
41
|
+
* - refined → clone + 尽量挂 base term + pred
|
|
42
|
+
*/
|
|
43
|
+
export function tagParamArg(arg: TypeValue, paramName: string): TypeValue {
|
|
44
|
+
if (!paramName || paramName.startsWith("...")) return arg;
|
|
45
|
+
// 函数/实例不 clone:浅拷贝会丢 _signature / 原型表(Promise executor 等)
|
|
46
|
+
if (arg.kind === "function" || arg.kind === "instance") return arg;
|
|
47
|
+
const cloned = cloneTypeValue(arg);
|
|
48
|
+
if (cloned.kind === "primitive" && cloned.type === "number") {
|
|
49
|
+
attachTerm(cloned, termVar(paramName));
|
|
50
|
+
} else if (cloned.kind === "literal" && typeof cloned.value === "number") {
|
|
51
|
+
attachTerm(cloned, lit(cloned.value));
|
|
52
|
+
} else if (cloned.kind === "refined") {
|
|
53
|
+
attachTerm(cloned, termVar(paramName));
|
|
54
|
+
}
|
|
55
|
+
return cloned;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function cloneTypeValue(tv: TypeValue): TypeValue {
|
|
59
|
+
switch (tv.kind) {
|
|
60
|
+
case "literal":
|
|
61
|
+
return { kind: "literal", value: tv.value };
|
|
62
|
+
case "primitive":
|
|
63
|
+
return { kind: "primitive", type: tv.type };
|
|
64
|
+
case "refined":
|
|
65
|
+
return { kind: "refined", base: tv.base, refinement: tv.refinement };
|
|
66
|
+
case "object":
|
|
67
|
+
return { kind: "object", properties: tv.properties, id: tv.id };
|
|
68
|
+
case "array":
|
|
69
|
+
return { kind: "array", element: tv.element };
|
|
70
|
+
case "tuple":
|
|
71
|
+
return { kind: "tuple", elements: tv.elements };
|
|
72
|
+
case "function":
|
|
73
|
+
return {
|
|
74
|
+
kind: "function",
|
|
75
|
+
params: tv.params,
|
|
76
|
+
body: tv.body,
|
|
77
|
+
closure: tv.closure,
|
|
78
|
+
};
|
|
79
|
+
case "promise":
|
|
80
|
+
return { kind: "promise", value: tv.value };
|
|
81
|
+
case "instance":
|
|
82
|
+
return {
|
|
83
|
+
kind: "instance",
|
|
84
|
+
className: tv.className,
|
|
85
|
+
properties: tv.properties,
|
|
86
|
+
};
|
|
87
|
+
case "union":
|
|
88
|
+
return { kind: "union", members: tv.members };
|
|
89
|
+
case "never":
|
|
90
|
+
return { kind: "never" };
|
|
91
|
+
case "unknown":
|
|
92
|
+
return { kind: "unknown" };
|
|
93
|
+
default:
|
|
94
|
+
return tv;
|
|
95
|
+
}
|
|
96
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# nudo
|
|
2
|
-
|
|
3
|
-
## 0.2.1
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- 0fd0718: Merge the three environment packages into one: `@nudojs/env-es`, `@nudojs/env-web`, `@nudojs/env-node` are replaced by a single `@nudojs/env` package with subpath exports `@nudojs/env/es`, `@nudojs/env/web`, `@nudojs/env/node`.
|
|
8
|
-
|
|
9
|
-
Move agent-facing tools from the standalone MCP server into the language server: `@nudojs/mcp` is removed. `@nudojs/lsp` now exposes `nudo.whatIf`, `nudo.suggestCase`, `nudo.trace`, `nudo.selectCase`, and `nudo.getActiveCases` via `workspace/executeCommand` (custom-request aliases `nudo/whatIf` etc. included), adds pull-mode diagnostics, and works on files that are not open in the editor (disk fallback). `nudo.whatIf` now actually applies the given type bindings — previously they were ignored. AI agents connect through any LSP↔MCP bridge (cclsp, mcpls, agent-lsp) or a native LSP client; an installable agent skill ships at `packages/lsp/agent-skill/SKILL.md`.
|
|
10
|
-
|
|
11
|
-
- Updated dependencies [0fd0718]
|
|
12
|
-
- @nudojs/env@0.2.0
|
|
13
|
-
- @nudojs/service@0.2.1
|
|
14
|
-
|
|
15
|
-
## 0.2.0
|
|
16
|
-
|
|
17
|
-
### Minor Changes
|
|
18
|
-
|
|
19
|
-
- 6c38283: docs and ci
|
|
20
|
-
- 9f7f819: fix pkg info
|
|
21
|
-
- c175f71: version
|
|
22
|
-
|
|
23
|
-
### Patch Changes
|
|
24
|
-
|
|
25
|
-
- Updated dependencies [6c38283]
|
|
26
|
-
- Updated dependencies [9f7f819]
|
|
27
|
-
- Updated dependencies [c175f71]
|
|
28
|
-
- @nudojs/core@0.2.0
|
|
29
|
-
- @nudojs/parser@0.2.0
|
|
30
|
-
|
|
31
|
-
## 0.1.0
|
|
32
|
-
|
|
33
|
-
### Minor Changes
|
|
34
|
-
|
|
35
|
-
- Conceptual design and basic implementation.
|
|
36
|
-
|
|
37
|
-
### Patch Changes
|
|
38
|
-
|
|
39
|
-
- Updated dependencies
|
|
40
|
-
- @nudojs/core@0.1.0
|
|
41
|
-
- @nudojs/parser@0.1.0
|