@nudojs/core 0.3.0 → 0.3.1
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-BU4A6XJ3.js +6856 -0
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +168 -0
- package/dist/index-CVYjXnwU.d.ts +902 -0
- package/dist/index.d.ts +64 -710
- package/dist/index.js +3258 -8426
- package/package.json +18 -5
- package/CHANGELOG.md +0 -34
- package/src/__tests__/environment.test.ts +0 -48
- package/src/__tests__/fn-sig-impl.test.ts +0 -57
- package/src/__tests__/ops-refined.test.ts +0 -196
- package/src/__tests__/ops.test.ts +0 -84
- package/src/__tests__/type-value-phase2.test.ts +0 -117
- package/src/__tests__/type-value-phase3.test.ts +0 -94
- package/src/__tests__/type-value-refined.test.ts +0 -210
- package/src/__tests__/type-value.test.ts +0 -319
- package/src/algebra/__tests__/abs-native.test.ts +0 -176
- package/src/algebra/__tests__/arithmetic.test.ts +0 -170
- package/src/algebra/__tests__/ast-eval.test.ts +0 -154
- package/src/algebra/__tests__/bridge.test.ts +0 -160
- package/src/algebra/__tests__/call-budget.test.ts +0 -117
- package/src/algebra/__tests__/check-case-consistency.test.ts +0 -69
- package/src/algebra/__tests__/check-gold.test.ts +0 -290
- package/src/algebra/__tests__/check-json.test.ts +0 -57
- package/src/algebra/__tests__/check-mini-repo.test.ts +0 -49
- package/src/algebra/__tests__/check-real-commander.test.ts +0 -43
- package/src/algebra/__tests__/check-real-packages.test.ts +0 -165
- package/src/algebra/__tests__/check-recall-gold.test.ts +0 -1024
- package/src/algebra/__tests__/check-require-dep-memo.test.ts +0 -45
- package/src/algebra/__tests__/check-return-contract.test.ts +0 -90
- package/src/algebra/__tests__/check-shape-gold.test.ts +0 -303
- package/src/algebra/__tests__/check-source-memo.test.ts +0 -204
- package/src/algebra/__tests__/check.test.ts +0 -69
- package/src/algebra/__tests__/denote.test.ts +0 -77
- package/src/algebra/__tests__/diagnostics.test.ts +0 -64
- package/src/algebra/__tests__/differential.test.ts +0 -124
- package/src/algebra/__tests__/div-mod.test.ts +0 -38
- package/src/algebra/__tests__/example0.test.ts +0 -103
- package/src/algebra/__tests__/exec-analyze-throws.test.ts +0 -60
- package/src/algebra/__tests__/exec-async.test.ts +0 -46
- package/src/algebra/__tests__/exec-bpath.test.ts +0 -193
- package/src/algebra/__tests__/exec-class.test.ts +0 -56
- package/src/algebra/__tests__/exec-e2e.test.ts +0 -97
- package/src/algebra/__tests__/exec-generator.test.ts +0 -53
- package/src/algebra/__tests__/exec-inherit-default.test.ts +0 -111
- package/src/algebra/__tests__/exec-optional-nested.test.ts +0 -87
- package/src/algebra/__tests__/exec-rest.test.ts +0 -55
- package/src/algebra/__tests__/exec-spread-forof.test.ts +0 -85
- package/src/algebra/__tests__/exec-static-computed.test.ts +0 -61
- package/src/algebra/__tests__/exec-switch.test.ts +0 -74
- package/src/algebra/__tests__/exec-trycatch.test.ts +0 -71
- package/src/algebra/__tests__/fn-fp-memo.test.ts +0 -166
- package/src/algebra/__tests__/fn-fp-multidecl.test.ts +0 -49
- package/src/algebra/__tests__/format.test.ts +0 -34
- package/src/algebra/__tests__/generalize-memo.test.ts +0 -285
- package/src/algebra/__tests__/generalize.test.ts +0 -113
- package/src/algebra/__tests__/hash-source.test.ts +0 -19
- package/src/algebra/__tests__/hof.test.ts +0 -80
- package/src/algebra/__tests__/inlay.test.ts +0 -30
- package/src/algebra/__tests__/language.test.ts +0 -77
- package/src/algebra/__tests__/leq.test.ts +0 -109
- package/src/algebra/__tests__/load-deps-fp-memo.test.ts +0 -132
- package/src/algebra/__tests__/methods.test.ts +0 -37
- package/src/algebra/__tests__/node-types.test.ts +0 -26
- package/src/algebra/__tests__/nudo-constraints.ts +0 -46
- package/src/algebra/__tests__/objects.test.ts +0 -117
- package/src/algebra/__tests__/parse-source-cache.test.ts +0 -42
- package/src/algebra/__tests__/refine-import.test.ts +0 -109
- package/src/algebra/__tests__/stable-source-key-block.test.ts +0 -23
- package/src/algebra/__tests__/stable-source-key.test.ts +0 -31
- package/src/algebra/__tests__/surface.test.ts +0 -41
- package/src/algebra/__tests__/this-inherit.test.ts +0 -130
- package/src/algebra/abs-fn.ts +0 -49
- package/src/algebra/abs-modules.ts +0 -139
- package/src/algebra/abs.ts +0 -206
- package/src/algebra/arithmetic.ts +0 -691
- package/src/algebra/ast-eval.ts +0 -1763
- package/src/algebra/bridge.ts +0 -350
- package/src/algebra/builtins.ts +0 -373
- package/src/algebra/check-report.ts +0 -150
- package/src/algebra/check.ts +0 -1867
- package/src/algebra/constraint.ts +0 -304
- package/src/algebra/denote.ts +0 -166
- package/src/algebra/diagnostics.ts +0 -210
- package/src/algebra/exec/call.ts +0 -29
- package/src/algebra/exec/calls.ts +0 -96
- package/src/algebra/exec/class-registry.ts +0 -29
- package/src/algebra/exec/class.ts +0 -329
- package/src/algebra/exec/index.ts +0 -6
- package/src/algebra/exec/member-diag.ts +0 -141
- package/src/algebra/exec/run.ts +0 -341
- package/src/algebra/exec/runtime.ts +0 -583
- package/src/algebra/exec/transpile.ts +0 -993
- package/src/algebra/fn-fp.ts +0 -294
- package/src/algebra/format.ts +0 -91
- package/src/algebra/generalize.ts +0 -776
- package/src/algebra/hash-source.ts +0 -27
- package/src/algebra/index.ts +0 -34
- package/src/algebra/inlay.ts +0 -198
- package/src/algebra/language.ts +0 -289
- package/src/algebra/leak.ts +0 -68
- package/src/algebra/leq.ts +0 -301
- package/src/algebra/load-deps-fp.ts +0 -97
- package/src/algebra/methods.ts +0 -128
- package/src/algebra/objects.ts +0 -251
- package/src/algebra/parse-source.ts +0 -64
- package/src/algebra/phi.ts +0 -42
- package/src/algebra/pred.ts +0 -395
- package/src/algebra/refine.ts +0 -227
- package/src/algebra/stable-source-key.ts +0 -81
- package/src/algebra/surface.ts +0 -206
- package/src/algebra/template.ts +0 -143
- package/src/algebra/term.ts +0 -119
- package/src/environment.ts +0 -74
- package/src/index.ts +0 -73
- package/src/mock-helpers.ts +0 -166
- package/src/ops.ts +0 -191
- package/src/refinements/range.ts +0 -86
- package/src/refinements/template-predicates.ts +0 -11
- package/src/refinements/template.ts +0 -176
- package/src/strip-types.ts +0 -236
- package/src/type-value.ts +0 -513
- package/tsconfig.json +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Nudo type system: type-as-computation (Abs) with TypeValue evaluation IR",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,11 +11,21 @@
|
|
|
11
11
|
"abstract-interpretation",
|
|
12
12
|
"type-system"
|
|
13
13
|
],
|
|
14
|
-
"main": "
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
15
16
|
"exports": {
|
|
16
|
-
".":
|
|
17
|
-
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./exec": {
|
|
22
|
+
"types": "./dist/exec.d.ts",
|
|
23
|
+
"default": "./dist/exec.js"
|
|
24
|
+
}
|
|
18
25
|
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
19
29
|
"repository": {
|
|
20
30
|
"type": "git",
|
|
21
31
|
"url": "git+https://github.com/nudojs/nudo.git",
|
|
@@ -32,7 +42,10 @@
|
|
|
32
42
|
"@babel/parser": "^7.29.0",
|
|
33
43
|
"@babel/types": "^7.29.0"
|
|
34
44
|
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"commander": "^13.0.0"
|
|
47
|
+
},
|
|
35
48
|
"scripts": {
|
|
36
|
-
"build": "tsup
|
|
49
|
+
"build": "tsup"
|
|
37
50
|
}
|
|
38
51
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# @nudojs/core
|
|
2
|
-
|
|
3
|
-
## 0.3.0
|
|
4
|
-
|
|
5
|
-
### Minor Changes
|
|
6
|
-
|
|
7
|
-
- 5786fa5: Promote the B-path (transpile + in-process Abs runtime) to the primary evaluation path for capable sources.
|
|
8
|
-
|
|
9
|
-
- Module graph now supports relative imports, bare-package harvest, default/namespace imports, re-exports, `export *`, require, cycle/depth/missing guards, and env modules (`path` / `node:path` etc.).
|
|
10
|
-
- Diagnostics, `@nudo:env` / mock injection, `call@` / `entry@` provenance, method-missing, unknown-recv, generators, classes, async/await, optional chaining, and destructuring run through B.
|
|
11
|
-
- When B hosts a file, `evaluateProgram` is skipped so TypeValue no longer double-reports.
|
|
12
|
-
- CLI `check` / `types` / `test` accept directories; check uses an Abs-only gate.
|
|
13
|
-
- LSP hover / `getTypeAtPosition` on capable files read the Abs node table; `*.nudo.js` edits evict L0 and recheck open parents.
|
|
14
|
-
- Public core surface now re-exports the algebra API (`Abs`, check, generalize, exec, bridge, `parseSource`, `stripTypes`).
|
|
15
|
-
|
|
16
|
-
- 5786fa5: Speed up repeated analysis with layered memos and tighter cache contracts.
|
|
17
|
-
|
|
18
|
-
- Shared parse/AST LRU, `generalizeFromAst` L0–L3 (instantiate, α-equivalence, dep fingerprint + LRU), Abs module cache, and session-wide memos with an incremental after-edit path.
|
|
19
|
-
- Host cache-eviction contract, AST/function-fingerprint memory caps, conservative call-scan depth cap, and fail-open truncated dependency fingerprints.
|
|
20
|
-
- Fix session-memo staleness and identity holes so after-edit results stay correct.
|
|
21
|
-
|
|
22
|
-
## 0.2.0
|
|
23
|
-
|
|
24
|
-
### Minor Changes
|
|
25
|
-
|
|
26
|
-
- 6c38283: docs and ci
|
|
27
|
-
- 9f7f819: fix pkg info
|
|
28
|
-
- c175f71: version
|
|
29
|
-
|
|
30
|
-
## 0.1.0
|
|
31
|
-
|
|
32
|
-
### Minor Changes
|
|
33
|
-
|
|
34
|
-
- Conceptual design and basic implementation.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { createEnvironment } from "../environment.ts";
|
|
3
|
-
import { T, typeValueEquals } from "../type-value.ts";
|
|
4
|
-
|
|
5
|
-
describe("Environment", () => {
|
|
6
|
-
it("binds and looks up values", () => {
|
|
7
|
-
const env = createEnvironment();
|
|
8
|
-
env.bind("x", T.literal(42));
|
|
9
|
-
expect(typeValueEquals(env.lookup("x"), T.literal(42))).toBe(true);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("returns undefined for unbound names", () => {
|
|
13
|
-
const env = createEnvironment();
|
|
14
|
-
expect(typeValueEquals(env.lookup("x"), T.undefined)).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("extends with child scope", () => {
|
|
18
|
-
const parent = createEnvironment();
|
|
19
|
-
parent.bind("x", T.literal(1));
|
|
20
|
-
const child = parent.extend({ y: T.literal(2) });
|
|
21
|
-
expect(typeValueEquals(child.lookup("x"), T.literal(1))).toBe(true);
|
|
22
|
-
expect(typeValueEquals(child.lookup("y"), T.literal(2))).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("child binding shadows parent", () => {
|
|
26
|
-
const parent = createEnvironment();
|
|
27
|
-
parent.bind("x", T.literal(1));
|
|
28
|
-
const child = parent.extend({ x: T.literal(99) });
|
|
29
|
-
expect(typeValueEquals(child.lookup("x"), T.literal(99))).toBe(true);
|
|
30
|
-
expect(typeValueEquals(parent.lookup("x"), T.literal(1))).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("has checks existence", () => {
|
|
34
|
-
const env = createEnvironment();
|
|
35
|
-
expect(env.has("x")).toBe(false);
|
|
36
|
-
env.bind("x", T.number);
|
|
37
|
-
expect(env.has("x")).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("snapshot creates independent copy", () => {
|
|
41
|
-
const env = createEnvironment();
|
|
42
|
-
env.bind("x", T.literal(1));
|
|
43
|
-
const snap = env.snapshot();
|
|
44
|
-
env.bind("x", T.literal(2));
|
|
45
|
-
expect(typeValueEquals(snap.lookup("x"), T.literal(1))).toBe(true);
|
|
46
|
-
expect(typeValueEquals(env.lookup("x"), T.literal(2))).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { T, getFnSig, isFnSig, typeValueEquals, typeValueToString } from "../index.ts";
|
|
3
|
-
|
|
4
|
-
describe("T.fnSig with impl", () => {
|
|
5
|
-
it("creates fnSig without impl (backward compatible)", () => {
|
|
6
|
-
const fn = T.fnSig([T.number], T.string);
|
|
7
|
-
expect(isFnSig(fn)).toBe(true);
|
|
8
|
-
const sig = getFnSig(fn)!;
|
|
9
|
-
expect(sig.impl).toBeUndefined();
|
|
10
|
-
expect(typeValueEquals(sig.returnType, T.string)).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("creates fnSig with impl", () => {
|
|
14
|
-
const fn = T.fnSig([T.number], T.number, T.never, (args) => {
|
|
15
|
-
if (args[0].kind === "literal" && typeof args[0].value === "number") {
|
|
16
|
-
return T.literal(args[0].value * 2);
|
|
17
|
-
}
|
|
18
|
-
return undefined;
|
|
19
|
-
});
|
|
20
|
-
expect(isFnSig(fn)).toBe(true);
|
|
21
|
-
const sig = getFnSig(fn)!;
|
|
22
|
-
expect(sig.impl).toBeDefined();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("impl returns concrete value for literal args", () => {
|
|
26
|
-
const fn = T.fnSig([T.number], T.number, T.never, (args) => {
|
|
27
|
-
if (args[0].kind === "literal" && typeof args[0].value === "number") {
|
|
28
|
-
return T.literal(args[0].value * 2);
|
|
29
|
-
}
|
|
30
|
-
return undefined;
|
|
31
|
-
});
|
|
32
|
-
const sig = getFnSig(fn)!;
|
|
33
|
-
const result = sig.impl!([T.literal(5)]);
|
|
34
|
-
expect(result).toBeDefined();
|
|
35
|
-
expect(typeValueEquals(result!, T.literal(10))).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("impl returns undefined for symbolic args (fallback to returnType)", () => {
|
|
39
|
-
const fn = T.fnSig([T.number], T.number, T.never, (args) => {
|
|
40
|
-
if (args[0].kind === "literal" && typeof args[0].value === "number") {
|
|
41
|
-
return T.literal(args[0].value * 2);
|
|
42
|
-
}
|
|
43
|
-
return undefined;
|
|
44
|
-
});
|
|
45
|
-
const sig = getFnSig(fn)!;
|
|
46
|
-
const result = sig.impl!([T.number]);
|
|
47
|
-
expect(result).toBeUndefined();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("typeValueToString still works for fnSig with impl", () => {
|
|
51
|
-
const fn = T.fnSig([T.number, T.string], T.boolean, T.never, () => T.literal(true));
|
|
52
|
-
const str = typeValueToString(fn);
|
|
53
|
-
expect(str).toContain("number");
|
|
54
|
-
expect(str).toContain("string");
|
|
55
|
-
expect(str).toContain("boolean");
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { T, typeValueEquals } from "../type-value.ts";
|
|
3
|
-
import { dispatchBinaryOp, dispatchMethod, dispatchProperty } from "../ops.ts";
|
|
4
|
-
import { createTemplate, isTemplate } from "../refinements/template.ts";
|
|
5
|
-
import { createRange } from "../refinements/range.ts";
|
|
6
|
-
|
|
7
|
-
describe("dispatchBinaryOp", () => {
|
|
8
|
-
describe("refined fallback", () => {
|
|
9
|
-
it("falls back to base type when no ops defined", () => {
|
|
10
|
-
const odd = T.refine(T.number, { name: "odd", meta: {} });
|
|
11
|
-
const result = dispatchBinaryOp("+", odd, T.literal(1));
|
|
12
|
-
expect(result.kind).toBe("primitive");
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("uses custom ops when defined", () => {
|
|
16
|
-
const odd = T.refine(T.number, {
|
|
17
|
-
name: "odd",
|
|
18
|
-
meta: {},
|
|
19
|
-
ops: {
|
|
20
|
-
"%"(self, other) {
|
|
21
|
-
if (other.kind === "literal" && other.value === 2) return T.literal(1);
|
|
22
|
-
return undefined;
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
const result = dispatchBinaryOp("%", odd, T.literal(2));
|
|
27
|
-
expect(typeValueEquals(result, T.literal(1))).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("falls back when custom op returns undefined", () => {
|
|
31
|
-
const odd = T.refine(T.number, {
|
|
32
|
-
name: "odd",
|
|
33
|
-
meta: {},
|
|
34
|
-
ops: {
|
|
35
|
-
"%"(self, other) {
|
|
36
|
-
if (other.kind === "literal" && other.value === 2) return T.literal(1);
|
|
37
|
-
return undefined;
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
// Ops.mod 已删:dispatchBinaryOp 的 % 最终落到 unknown;
|
|
42
|
-
// evaluator 侧由 evalBinaryValue 的 numberish 兜底补 number。
|
|
43
|
-
const result = dispatchBinaryOp("%", odd, T.literal(3));
|
|
44
|
-
expect(result.kind).toBe("unknown");
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("template string via add", () => {
|
|
49
|
-
it("literal string + T.string produces template", () => {
|
|
50
|
-
const result = dispatchBinaryOp("+", T.literal("xy"), T.string);
|
|
51
|
-
expect(isTemplate(result)).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("T.string + literal string produces template", () => {
|
|
55
|
-
const result = dispatchBinaryOp("+", T.string, T.literal("!"));
|
|
56
|
-
expect(isTemplate(result)).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("template + template concatenates", () => {
|
|
60
|
-
const left = createTemplate([T.literal("a"), T.string]);
|
|
61
|
-
const right = createTemplate([T.literal("b"), T.string]);
|
|
62
|
-
const result = dispatchBinaryOp("+", left, right);
|
|
63
|
-
expect(isTemplate(result)).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("two literal strings still produce literal", () => {
|
|
67
|
-
const result = dispatchBinaryOp("+", T.literal("a"), T.literal("b"));
|
|
68
|
-
expect(typeValueEquals(result, T.literal("ab"))).toBe(true);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("T.string + T.string stays T.string", () => {
|
|
72
|
-
const result = dispatchBinaryOp("+", T.string, T.string);
|
|
73
|
-
expect(typeValueEquals(result, T.string)).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe("range comparison ops", () => {
|
|
78
|
-
it("range(min:0) >= 0 is true", () => {
|
|
79
|
-
const r = createRange({ min: 0 });
|
|
80
|
-
const result = dispatchBinaryOp(">=", r, T.literal(0));
|
|
81
|
-
expect(typeValueEquals(result, T.literal(true))).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("range(min:0) >= -1 is true", () => {
|
|
85
|
-
const r = createRange({ min: 0 });
|
|
86
|
-
const result = dispatchBinaryOp(">=", r, T.literal(-1));
|
|
87
|
-
expect(typeValueEquals(result, T.literal(true))).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("range(max:100) > 200 is false", () => {
|
|
91
|
-
const r = createRange({ max: 100 });
|
|
92
|
-
const result = dispatchBinaryOp(">", r, T.literal(200));
|
|
93
|
-
expect(typeValueEquals(result, T.literal(false))).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("range(min:0, max:100) > 50 is uncertain (boolean)", () => {
|
|
97
|
-
const r = createRange({ min: 0, max: 100 });
|
|
98
|
-
const result = dispatchBinaryOp(">", r, T.literal(50));
|
|
99
|
-
expect(typeValueEquals(result, T.boolean)).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("range(max:10) <= 10 is true", () => {
|
|
103
|
-
const r = createRange({ max: 10 });
|
|
104
|
-
const result = dispatchBinaryOp("<=", r, T.literal(10));
|
|
105
|
-
expect(typeValueEquals(result, T.literal(true))).toBe(true);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("range(min:5) < 3 is false", () => {
|
|
109
|
-
const r = createRange({ min: 5 });
|
|
110
|
-
const result = dispatchBinaryOp("<", r, T.literal(3));
|
|
111
|
-
expect(typeValueEquals(result, T.literal(false))).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe("dispatchMethod", () => {
|
|
117
|
-
describe("template startsWith", () => {
|
|
118
|
-
it("known prefix matches", () => {
|
|
119
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
120
|
-
const result = dispatchMethod(tmpl, "startsWith", [T.literal("x")]);
|
|
121
|
-
expect(result).toBeDefined();
|
|
122
|
-
expect(typeValueEquals(result!, T.literal(true))).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("known prefix full match", () => {
|
|
126
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
127
|
-
const result = dispatchMethod(tmpl, "startsWith", [T.literal("xy")]);
|
|
128
|
-
expect(typeValueEquals(result!, T.literal(true))).toBe(true);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("known prefix mismatch", () => {
|
|
132
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
133
|
-
const result = dispatchMethod(tmpl, "startsWith", [T.literal("a")]);
|
|
134
|
-
expect(typeValueEquals(result!, T.literal(false))).toBe(true);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("uncertain when search extends beyond prefix", () => {
|
|
138
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
139
|
-
const result = dispatchMethod(tmpl, "startsWith", [T.literal("xyz")]);
|
|
140
|
-
expect(result).toBeUndefined();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("template endsWith", () => {
|
|
145
|
-
it("known suffix matches", () => {
|
|
146
|
-
const tmpl = createTemplate([T.string, T.literal("!")]);
|
|
147
|
-
const result = dispatchMethod(tmpl, "endsWith", [T.literal("!")]);
|
|
148
|
-
expect(typeValueEquals(result!, T.literal(true))).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("known suffix mismatch", () => {
|
|
152
|
-
const tmpl = createTemplate([T.string, T.literal("!")]);
|
|
153
|
-
const result = dispatchMethod(tmpl, "endsWith", [T.literal("?")]);
|
|
154
|
-
expect(typeValueEquals(result!, T.literal(false))).toBe(true);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe("template includes", () => {
|
|
159
|
-
it("known fixed text includes substring", () => {
|
|
160
|
-
const tmpl = createTemplate([T.literal("hello"), T.string, T.literal("world")]);
|
|
161
|
-
const result = dispatchMethod(tmpl, "includes", [T.literal("hello")]);
|
|
162
|
-
expect(typeValueEquals(result!, T.literal(true))).toBe(true);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("uncertain when not in fixed text", () => {
|
|
166
|
-
const tmpl = createTemplate([T.literal("hello"), T.string]);
|
|
167
|
-
const result = dispatchMethod(tmpl, "includes", [T.literal("xyz")]);
|
|
168
|
-
expect(result).toBeUndefined();
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("returns undefined for unknown methods", () => {
|
|
173
|
-
const tmpl = createTemplate([T.literal("x"), T.string]);
|
|
174
|
-
const result = dispatchMethod(tmpl, "unknownMethod", []);
|
|
175
|
-
expect(result).toBeUndefined();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("returns undefined for non-refined types", () => {
|
|
179
|
-
const result = dispatchMethod(T.string, "startsWith", [T.literal("x")]);
|
|
180
|
-
expect(result).toBeUndefined();
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe("dispatchProperty", () => {
|
|
185
|
-
it("template length returns range", () => {
|
|
186
|
-
const tmpl = createTemplate([T.literal("xy"), T.string]);
|
|
187
|
-
const result = dispatchProperty(tmpl, "length");
|
|
188
|
-
expect(result).toBeDefined();
|
|
189
|
-
expect(result!.kind).toBe("refined");
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("returns undefined for non-refined types", () => {
|
|
193
|
-
const result = dispatchProperty(T.string, "length");
|
|
194
|
-
expect(result).toBeUndefined();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Ops, applyBinaryOp } from "../ops.ts";
|
|
3
|
-
import { T, typeValueEquals } from "../type-value.ts";
|
|
4
|
-
|
|
5
|
-
describe("Ops.add", () => {
|
|
6
|
-
it("adds two literal numbers", () => {
|
|
7
|
-
expect(typeValueEquals(Ops.add(T.literal(1), T.literal(2)), T.literal(3))).toBe(true);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("concatenates two literal strings", () => {
|
|
11
|
-
expect(typeValueEquals(Ops.add(T.literal("a"), T.literal("b")), T.literal("ab"))).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("returns number for abstract numbers", () => {
|
|
15
|
-
expect(typeValueEquals(Ops.add(T.number, T.number), T.number)).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("returns string when one side is string", () => {
|
|
19
|
-
expect(typeValueEquals(Ops.add(T.number, T.string), T.string)).toBe(true);
|
|
20
|
-
expect(typeValueEquals(Ops.add(T.string, T.literal(1)), T.string)).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// sub/mul/div/mod 已删除:数值路径唯一真理在 algebra;非数应得 unknown。
|
|
25
|
-
|
|
26
|
-
describe("Ops.strictEq / strictNeq", () => {
|
|
27
|
-
it("compares literals", () => {
|
|
28
|
-
expect(typeValueEquals(Ops.strictEq(T.literal(1), T.literal(1)), T.literal(true))).toBe(true);
|
|
29
|
-
expect(typeValueEquals(Ops.strictEq(T.literal(1), T.literal(2)), T.literal(false))).toBe(true);
|
|
30
|
-
expect(typeValueEquals(Ops.strictNeq(T.literal(1), T.literal(2)), T.literal(true))).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("returns boolean for abstract operands", () => {
|
|
34
|
-
expect(typeValueEquals(Ops.strictEq(T.number, T.number), T.boolean)).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe("Ops.gt / lt / gte / lte", () => {
|
|
39
|
-
it("compares literal numbers", () => {
|
|
40
|
-
expect(typeValueEquals(Ops.gt(T.literal(3), T.literal(2)), T.literal(true))).toBe(true);
|
|
41
|
-
expect(typeValueEquals(Ops.lt(T.literal(1), T.literal(2)), T.literal(true))).toBe(true);
|
|
42
|
-
expect(typeValueEquals(Ops.gte(T.literal(2), T.literal(2)), T.literal(true))).toBe(true);
|
|
43
|
-
expect(typeValueEquals(Ops.lte(T.literal(3), T.literal(2)), T.literal(false))).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("returns boolean for abstract operands", () => {
|
|
47
|
-
expect(typeValueEquals(Ops.gt(T.number, T.number), T.boolean)).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe("Ops.not", () => {
|
|
52
|
-
it("negates literal booleans", () => {
|
|
53
|
-
expect(typeValueEquals(Ops.not(T.literal(true)), T.literal(false))).toBe(true);
|
|
54
|
-
expect(typeValueEquals(Ops.not(T.literal(false)), T.literal(true))).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("negates truthy/falsy literals", () => {
|
|
58
|
-
expect(typeValueEquals(Ops.not(T.literal(0)), T.literal(true))).toBe(true);
|
|
59
|
-
expect(typeValueEquals(Ops.not(T.literal("")), T.literal(true))).toBe(true);
|
|
60
|
-
expect(typeValueEquals(Ops.not(T.literal(1)), T.literal(false))).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("returns boolean for abstract types", () => {
|
|
64
|
-
expect(typeValueEquals(Ops.not(T.number), T.boolean)).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// typeof_ / neg 已迁入 algebra surface(tryAbsUnary)
|
|
69
|
-
|
|
70
|
-
describe("applyBinaryOp", () => {
|
|
71
|
-
it("dispatches to correct op", () => {
|
|
72
|
-
expect(typeValueEquals(applyBinaryOp("+", T.literal(1), T.literal(2)), T.literal(3))).toBe(true);
|
|
73
|
-
expect(typeValueEquals(applyBinaryOp("===", T.literal(1), T.literal(1)), T.literal(true))).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("returns unknown for deleted / unsupported ops", () => {
|
|
77
|
-
expect(applyBinaryOp("**", T.number, T.number)).toEqual(T.unknown);
|
|
78
|
-
// sub/mul/div/mod 已从 Ops 删除,代数是唯一路径
|
|
79
|
-
expect(applyBinaryOp("-", T.literal(5), T.literal(3))).toEqual(T.unknown);
|
|
80
|
-
expect(applyBinaryOp("*", T.number, T.number)).toEqual(T.unknown);
|
|
81
|
-
expect(applyBinaryOp("/", T.literal(10), T.literal(2))).toEqual(T.unknown);
|
|
82
|
-
expect(applyBinaryOp("%", T.literal(7), T.literal(3))).toEqual(T.unknown);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
T,
|
|
4
|
-
isSubtypeOf,
|
|
5
|
-
deepCloneTypeValue,
|
|
6
|
-
mergeObjectProperties,
|
|
7
|
-
typeValueEquals,
|
|
8
|
-
} from "../type-value.ts";
|
|
9
|
-
|
|
10
|
-
describe("isSubtypeOf: structural subtyping", () => {
|
|
11
|
-
it("object subtype: a has all properties of b with subtypes", () => {
|
|
12
|
-
const a = T.object({ x: T.literal(1), y: T.string, z: T.boolean });
|
|
13
|
-
const b = T.object({ x: T.number, y: T.string });
|
|
14
|
-
expect(isSubtypeOf(a, b)).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("object not subtype: missing property", () => {
|
|
18
|
-
const a = T.object({ x: T.number });
|
|
19
|
-
const b = T.object({ x: T.number, y: T.string });
|
|
20
|
-
expect(isSubtypeOf(a, b)).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("object not subtype: property type mismatch", () => {
|
|
24
|
-
const a = T.object({ x: T.string });
|
|
25
|
-
const b = T.object({ x: T.number });
|
|
26
|
-
expect(isSubtypeOf(a, b)).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("array subtype: element is subtype", () => {
|
|
30
|
-
const a = T.array(T.literal(1));
|
|
31
|
-
const b = T.array(T.number);
|
|
32
|
-
expect(isSubtypeOf(a, b)).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("array not subtype: element mismatch", () => {
|
|
36
|
-
const a = T.array(T.string);
|
|
37
|
-
const b = T.array(T.number);
|
|
38
|
-
expect(isSubtypeOf(a, b)).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("tuple subtype: element-wise", () => {
|
|
42
|
-
const a = T.tuple([T.literal(1), T.literal("hi")]);
|
|
43
|
-
const b = T.tuple([T.number, T.string]);
|
|
44
|
-
expect(isSubtypeOf(a, b)).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("tuple not subtype: length mismatch", () => {
|
|
48
|
-
const a = T.tuple([T.number]);
|
|
49
|
-
const b = T.tuple([T.number, T.string]);
|
|
50
|
-
expect(isSubtypeOf(a, b)).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("tuple is subtype of array if all elements match", () => {
|
|
54
|
-
const a = T.tuple([T.literal(1), T.literal(2)]);
|
|
55
|
-
const b = T.array(T.number);
|
|
56
|
-
expect(isSubtypeOf(a, b)).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("tuple not subtype of array if element mismatch", () => {
|
|
60
|
-
const a = T.tuple([T.literal(1), T.literal("hi")]);
|
|
61
|
-
const b = T.array(T.number);
|
|
62
|
-
expect(isSubtypeOf(a, b)).toBe(false);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe("deepCloneTypeValue", () => {
|
|
67
|
-
it("clones object with new id", () => {
|
|
68
|
-
const obj = T.object({ x: T.number, y: T.string });
|
|
69
|
-
const cloned = deepCloneTypeValue(obj);
|
|
70
|
-
expect(cloned.kind).toBe("object");
|
|
71
|
-
if (cloned.kind === "object" && obj.kind === "object") {
|
|
72
|
-
expect(cloned.id).not.toBe(obj.id);
|
|
73
|
-
expect(cloned.properties.x).toEqual(obj.properties.x);
|
|
74
|
-
expect(cloned.properties.y).toEqual(obj.properties.y);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("clones nested objects", () => {
|
|
79
|
-
const inner = T.object({ a: T.literal(1) });
|
|
80
|
-
const outer = T.object({ nested: inner });
|
|
81
|
-
const cloned = deepCloneTypeValue(outer);
|
|
82
|
-
if (cloned.kind === "object" && outer.kind === "object") {
|
|
83
|
-
const clonedInner = cloned.properties.nested;
|
|
84
|
-
expect(clonedInner.kind).toBe("object");
|
|
85
|
-
if (clonedInner.kind === "object" && inner.kind === "object") {
|
|
86
|
-
expect(clonedInner.id).not.toBe(inner.id);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("returns primitives unchanged", () => {
|
|
92
|
-
expect(deepCloneTypeValue(T.number)).toBe(T.number);
|
|
93
|
-
expect(typeValueEquals(deepCloneTypeValue(T.literal(1)), T.literal(1))).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe("mergeObjectProperties", () => {
|
|
98
|
-
it("merges two objects with overlapping keys", () => {
|
|
99
|
-
const a = T.object({ x: T.literal(1), y: T.literal("a") }) as any;
|
|
100
|
-
const b = T.object({ x: T.literal(2), z: T.boolean }) as any;
|
|
101
|
-
const merged = mergeObjectProperties(a, b);
|
|
102
|
-
if (merged.kind === "object") {
|
|
103
|
-
expect(Object.keys(merged.properties).sort()).toEqual(["x", "y", "z"]);
|
|
104
|
-
expect(merged.properties.y).toEqual(T.literal("a"));
|
|
105
|
-
expect(merged.properties.z).toEqual(T.boolean);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("creates union for overlapping keys with different values", () => {
|
|
110
|
-
const a = T.object({ x: T.literal(1) }) as any;
|
|
111
|
-
const b = T.object({ x: T.literal(2) }) as any;
|
|
112
|
-
const merged = mergeObjectProperties(a, b);
|
|
113
|
-
if (merged.kind === "object") {
|
|
114
|
-
expect(merged.properties.x.kind).toBe("union");
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
T,
|
|
4
|
-
typeValueToString,
|
|
5
|
-
typeValueEquals,
|
|
6
|
-
isSubtypeOf,
|
|
7
|
-
getPrimitiveTypeOf,
|
|
8
|
-
deepCloneTypeValue,
|
|
9
|
-
} from "../type-value.ts";
|
|
10
|
-
|
|
11
|
-
describe("PromiseType", () => {
|
|
12
|
-
it("creates a promise type", () => {
|
|
13
|
-
const p = T.promise(T.number);
|
|
14
|
-
expect(p.kind).toBe("promise");
|
|
15
|
-
if (p.kind === "promise") {
|
|
16
|
-
expect(p.value).toBe(T.number);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("toString formats correctly", () => {
|
|
21
|
-
expect(typeValueToString(T.promise(T.number))).toBe("Promise<number>");
|
|
22
|
-
expect(typeValueToString(T.promise(T.string))).toBe("Promise<string>");
|
|
23
|
-
expect(typeValueToString(T.promise(T.union(T.number, T.string)))).toBe(
|
|
24
|
-
"Promise<number | string>",
|
|
25
|
-
);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("equals works", () => {
|
|
29
|
-
expect(typeValueEquals(T.promise(T.number), T.promise(T.number))).toBe(true);
|
|
30
|
-
expect(typeValueEquals(T.promise(T.number), T.promise(T.string))).toBe(false);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("isSubtypeOf works", () => {
|
|
34
|
-
expect(isSubtypeOf(T.promise(T.literal(1)), T.promise(T.number))).toBe(true);
|
|
35
|
-
expect(isSubtypeOf(T.promise(T.number), T.promise(T.string))).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("getPrimitiveTypeOf returns object", () => {
|
|
39
|
-
expect(getPrimitiveTypeOf(T.promise(T.number))).toBe("object");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("deepClone works", () => {
|
|
43
|
-
const p = T.promise(T.object({ x: T.number }));
|
|
44
|
-
const cloned = deepCloneTypeValue(p);
|
|
45
|
-
expect(typeValueEquals(cloned, p)).toBe(false);
|
|
46
|
-
expect(cloned.kind).toBe("promise");
|
|
47
|
-
if (cloned.kind === "promise" && cloned.value.kind === "object") {
|
|
48
|
-
expect(cloned.value.properties.x).toBe(T.number);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe("InstanceType", () => {
|
|
54
|
-
it("creates an instance type", () => {
|
|
55
|
-
const inst = T.instanceOf("Error", { message: T.literal("oops") });
|
|
56
|
-
expect(inst.kind).toBe("instance");
|
|
57
|
-
if (inst.kind === "instance") {
|
|
58
|
-
expect(inst.className).toBe("Error");
|
|
59
|
-
expect(inst.properties.message).toEqual(T.literal("oops"));
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("toString formats correctly", () => {
|
|
64
|
-
expect(typeValueToString(T.instanceOf("Error"))).toBe("Error");
|
|
65
|
-
expect(typeValueToString(T.instanceOf("Error", { message: T.literal("oops") }))).toBe(
|
|
66
|
-
'Error { message: "oops" }',
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("equals checks className", () => {
|
|
71
|
-
expect(typeValueEquals(T.instanceOf("Error"), T.instanceOf("Error"))).toBe(true);
|
|
72
|
-
expect(typeValueEquals(T.instanceOf("Error"), T.instanceOf("TypeError"))).toBe(false);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("isSubtypeOf handles error hierarchy", () => {
|
|
76
|
-
expect(isSubtypeOf(T.instanceOf("TypeError"), T.instanceOf("Error"))).toBe(true);
|
|
77
|
-
expect(isSubtypeOf(T.instanceOf("SyntaxError"), T.instanceOf("Error"))).toBe(true);
|
|
78
|
-
expect(isSubtypeOf(T.instanceOf("Error"), T.instanceOf("TypeError"))).toBe(false);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("getPrimitiveTypeOf returns object", () => {
|
|
82
|
-
expect(getPrimitiveTypeOf(T.instanceOf("Error"))).toBe("object");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("deepClone works", () => {
|
|
86
|
-
const inst = T.instanceOf("Error", { message: T.string });
|
|
87
|
-
const cloned = deepCloneTypeValue(inst);
|
|
88
|
-
expect(cloned.kind).toBe("instance");
|
|
89
|
-
if (cloned.kind === "instance") {
|
|
90
|
-
expect(cloned.className).toBe("Error");
|
|
91
|
-
expect(cloned.properties.message).toBe(T.string);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
});
|