@nudojs/core 0.3.0 → 0.4.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.
Files changed (123) hide show
  1. package/dist/chunk-N7E2A7IK.js +7216 -0
  2. package/dist/exec.d.ts +2 -0
  3. package/dist/exec.js +174 -0
  4. package/dist/index-DgyudAkw.d.ts +911 -0
  5. package/dist/index.d.ts +64 -710
  6. package/dist/index.js +3269 -8426
  7. package/package.json +18 -5
  8. package/CHANGELOG.md +0 -34
  9. package/src/__tests__/environment.test.ts +0 -48
  10. package/src/__tests__/fn-sig-impl.test.ts +0 -57
  11. package/src/__tests__/ops-refined.test.ts +0 -196
  12. package/src/__tests__/ops.test.ts +0 -84
  13. package/src/__tests__/type-value-phase2.test.ts +0 -117
  14. package/src/__tests__/type-value-phase3.test.ts +0 -94
  15. package/src/__tests__/type-value-refined.test.ts +0 -210
  16. package/src/__tests__/type-value.test.ts +0 -319
  17. package/src/algebra/__tests__/abs-native.test.ts +0 -176
  18. package/src/algebra/__tests__/arithmetic.test.ts +0 -170
  19. package/src/algebra/__tests__/ast-eval.test.ts +0 -154
  20. package/src/algebra/__tests__/bridge.test.ts +0 -160
  21. package/src/algebra/__tests__/call-budget.test.ts +0 -117
  22. package/src/algebra/__tests__/check-case-consistency.test.ts +0 -69
  23. package/src/algebra/__tests__/check-gold.test.ts +0 -290
  24. package/src/algebra/__tests__/check-json.test.ts +0 -57
  25. package/src/algebra/__tests__/check-mini-repo.test.ts +0 -49
  26. package/src/algebra/__tests__/check-real-commander.test.ts +0 -43
  27. package/src/algebra/__tests__/check-real-packages.test.ts +0 -165
  28. package/src/algebra/__tests__/check-recall-gold.test.ts +0 -1024
  29. package/src/algebra/__tests__/check-require-dep-memo.test.ts +0 -45
  30. package/src/algebra/__tests__/check-return-contract.test.ts +0 -90
  31. package/src/algebra/__tests__/check-shape-gold.test.ts +0 -303
  32. package/src/algebra/__tests__/check-source-memo.test.ts +0 -204
  33. package/src/algebra/__tests__/check.test.ts +0 -69
  34. package/src/algebra/__tests__/denote.test.ts +0 -77
  35. package/src/algebra/__tests__/diagnostics.test.ts +0 -64
  36. package/src/algebra/__tests__/differential.test.ts +0 -124
  37. package/src/algebra/__tests__/div-mod.test.ts +0 -38
  38. package/src/algebra/__tests__/example0.test.ts +0 -103
  39. package/src/algebra/__tests__/exec-analyze-throws.test.ts +0 -60
  40. package/src/algebra/__tests__/exec-async.test.ts +0 -46
  41. package/src/algebra/__tests__/exec-bpath.test.ts +0 -193
  42. package/src/algebra/__tests__/exec-class.test.ts +0 -56
  43. package/src/algebra/__tests__/exec-e2e.test.ts +0 -97
  44. package/src/algebra/__tests__/exec-generator.test.ts +0 -53
  45. package/src/algebra/__tests__/exec-inherit-default.test.ts +0 -111
  46. package/src/algebra/__tests__/exec-optional-nested.test.ts +0 -87
  47. package/src/algebra/__tests__/exec-rest.test.ts +0 -55
  48. package/src/algebra/__tests__/exec-spread-forof.test.ts +0 -85
  49. package/src/algebra/__tests__/exec-static-computed.test.ts +0 -61
  50. package/src/algebra/__tests__/exec-switch.test.ts +0 -74
  51. package/src/algebra/__tests__/exec-trycatch.test.ts +0 -71
  52. package/src/algebra/__tests__/fn-fp-memo.test.ts +0 -166
  53. package/src/algebra/__tests__/fn-fp-multidecl.test.ts +0 -49
  54. package/src/algebra/__tests__/format.test.ts +0 -34
  55. package/src/algebra/__tests__/generalize-memo.test.ts +0 -285
  56. package/src/algebra/__tests__/generalize.test.ts +0 -113
  57. package/src/algebra/__tests__/hash-source.test.ts +0 -19
  58. package/src/algebra/__tests__/hof.test.ts +0 -80
  59. package/src/algebra/__tests__/inlay.test.ts +0 -30
  60. package/src/algebra/__tests__/language.test.ts +0 -77
  61. package/src/algebra/__tests__/leq.test.ts +0 -109
  62. package/src/algebra/__tests__/load-deps-fp-memo.test.ts +0 -132
  63. package/src/algebra/__tests__/methods.test.ts +0 -37
  64. package/src/algebra/__tests__/node-types.test.ts +0 -26
  65. package/src/algebra/__tests__/nudo-constraints.ts +0 -46
  66. package/src/algebra/__tests__/objects.test.ts +0 -117
  67. package/src/algebra/__tests__/parse-source-cache.test.ts +0 -42
  68. package/src/algebra/__tests__/refine-import.test.ts +0 -109
  69. package/src/algebra/__tests__/stable-source-key-block.test.ts +0 -23
  70. package/src/algebra/__tests__/stable-source-key.test.ts +0 -31
  71. package/src/algebra/__tests__/surface.test.ts +0 -41
  72. package/src/algebra/__tests__/this-inherit.test.ts +0 -130
  73. package/src/algebra/abs-fn.ts +0 -49
  74. package/src/algebra/abs-modules.ts +0 -139
  75. package/src/algebra/abs.ts +0 -206
  76. package/src/algebra/arithmetic.ts +0 -691
  77. package/src/algebra/ast-eval.ts +0 -1763
  78. package/src/algebra/bridge.ts +0 -350
  79. package/src/algebra/builtins.ts +0 -373
  80. package/src/algebra/check-report.ts +0 -150
  81. package/src/algebra/check.ts +0 -1867
  82. package/src/algebra/constraint.ts +0 -304
  83. package/src/algebra/denote.ts +0 -166
  84. package/src/algebra/diagnostics.ts +0 -210
  85. package/src/algebra/exec/call.ts +0 -29
  86. package/src/algebra/exec/calls.ts +0 -96
  87. package/src/algebra/exec/class-registry.ts +0 -29
  88. package/src/algebra/exec/class.ts +0 -329
  89. package/src/algebra/exec/index.ts +0 -6
  90. package/src/algebra/exec/member-diag.ts +0 -141
  91. package/src/algebra/exec/run.ts +0 -341
  92. package/src/algebra/exec/runtime.ts +0 -583
  93. package/src/algebra/exec/transpile.ts +0 -993
  94. package/src/algebra/fn-fp.ts +0 -294
  95. package/src/algebra/format.ts +0 -91
  96. package/src/algebra/generalize.ts +0 -776
  97. package/src/algebra/hash-source.ts +0 -27
  98. package/src/algebra/index.ts +0 -34
  99. package/src/algebra/inlay.ts +0 -198
  100. package/src/algebra/language.ts +0 -289
  101. package/src/algebra/leak.ts +0 -68
  102. package/src/algebra/leq.ts +0 -301
  103. package/src/algebra/load-deps-fp.ts +0 -97
  104. package/src/algebra/methods.ts +0 -128
  105. package/src/algebra/objects.ts +0 -251
  106. package/src/algebra/parse-source.ts +0 -64
  107. package/src/algebra/phi.ts +0 -42
  108. package/src/algebra/pred.ts +0 -395
  109. package/src/algebra/refine.ts +0 -227
  110. package/src/algebra/stable-source-key.ts +0 -81
  111. package/src/algebra/surface.ts +0 -206
  112. package/src/algebra/template.ts +0 -143
  113. package/src/algebra/term.ts +0 -119
  114. package/src/environment.ts +0 -74
  115. package/src/index.ts +0 -73
  116. package/src/mock-helpers.ts +0 -166
  117. package/src/ops.ts +0 -191
  118. package/src/refinements/range.ts +0 -86
  119. package/src/refinements/template-predicates.ts +0 -11
  120. package/src/refinements/template.ts +0 -176
  121. package/src/strip-types.ts +0 -236
  122. package/src/type-value.ts +0 -513
  123. package/tsconfig.json +0 -8
@@ -1,45 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import { checkSource, resetCheckSourceMemo, pTrue } from "../index.ts";
3
- import { STD_NUDO_SRC } from "./nudo-constraints.ts";
4
-
5
- beforeEach(() => {
6
- resetCheckSourceMemo();
7
- });
8
-
9
- describe("check memo covers regular require deps", () => {
10
- it("misses when require target refine changes (parent source unchanged)", () => {
11
- let dep = `
12
- /// @nudo:import { positive } from "./std.nudo.js"
13
- /**
14
- * @nudo:refine x positive
15
- */
16
- function needsPositive(x) {
17
- if (x > 0) return x;
18
- return 0;
19
- }
20
- module.exports = { needsPositive };
21
- `;
22
- const loadModule = (spec: string) => {
23
- if (spec.includes("std.nudo")) return STD_NUDO_SRC;
24
- return dep;
25
- };
26
- const opts = { loadModule, fromFile: "/t/a.js" };
27
- const src = `
28
- const { needsPositive } = require("./v.js");
29
- needsPositive(-1);
30
- `;
31
- const r1 = checkSource("/t/a.js", src, pTrue, opts);
32
- expect(r1.ok).toBe(false);
33
- expect(r1.issues.some((i) => i.code === "nudo:constraint-violated")).toBe(true);
34
-
35
- dep = `
36
- function needsPositive(x) {
37
- if (x > 0) return x;
38
- return 0;
39
- }
40
- module.exports = { needsPositive };
41
- `;
42
- const r2 = checkSource("/t/a.js", src, pTrue, opts);
43
- expect(r2.ok).toBe(true);
44
- });
45
- });
@@ -1,90 +0,0 @@
1
- /**
2
- * @nudo:refine return 后置契约:推断返回值 ⊭ 声明。
3
- * 与 @nudo:refine(前置)对偶;契约仍来自 *.nudo.js 模板。
4
- */
5
- import { describe, it, expect } from "vitest";
6
- import { checkSource, pTrue } from "../index.ts";
7
- import { withStdImport, stdOpts } from "./nudo-constraints.ts";
8
-
9
- function issuesOf(src: string) {
10
- return checkSource("/t/ret.js", withStdImport(src), pTrue, stdOpts);
11
- }
12
-
13
- describe("@nudo:refine return", () => {
14
- it("ok: 返回值满足 positive", () => {
15
- const r = issuesOf(`
16
- /**
17
- * @nudo:refine x positive
18
- * @nudo:refine return positive
19
- */
20
- function inc(x) {
21
- return x + 1;
22
- }
23
- `);
24
- expect(r.issues.filter((i) => i.severity === "error")).toEqual([]);
25
- });
26
-
27
- it("error: 字面量返回 ⊭ positive", () => {
28
- const r = issuesOf(`
29
- /**
30
- * @nudo:refine return positive
31
- */
32
- function bad() {
33
- return 0;
34
- }
35
- `);
36
- expect(r.ok).toBe(false);
37
- const err = r.issues.find((i) => i.message.includes("@nudo:refine return"));
38
- expect(err).toBeDefined();
39
- expect(err!.expected).toContain(">");
40
- });
41
-
42
- it("error: 返回类型 ⊭ string", () => {
43
- const r = issuesOf(`
44
- /**
45
- * @nudo:refine return notAString
46
- */
47
- function n() {
48
- return 1;
49
- }
50
- `);
51
- // notAString 不在 std 里 → 无契约可查,不报
52
- expect(r.issues.filter((i) => i.message.includes("@nudo:refine return"))).toEqual([]);
53
- });
54
-
55
- it("ok: 无 @nudo:refine return 不猜后置", () => {
56
- const r = issuesOf(`
57
- function free() {
58
- return -1;
59
- }
60
- `);
61
- expect(r.issues.filter((i) => i.message.includes("@nudo:refine return"))).toEqual([]);
62
- });
63
-
64
- it("error: percent 上界", () => {
65
- const r = issuesOf(`
66
- /**
67
- * @nudo:refine return percent
68
- */
69
- function big() {
70
- return 150;
71
- }
72
- `);
73
- expect(r.ok).toBe(false);
74
- expect(r.issues.some((i) => i.message.includes("@nudo:refine return"))).toBe(true);
75
- });
76
-
77
- it("ok: any/符号返回不误报", () => {
78
- const r = issuesOf(`
79
- /**
80
- * @nudo:refine x positive
81
- * @nudo:refine return positive
82
- */
83
- function keep(x) {
84
- return x;
85
- }
86
- `);
87
- // 符号返回带 pred x>0,应满足
88
- expect(r.issues.filter((i) => i.severity === "error")).toEqual([]);
89
- });
90
- });
@@ -1,303 +0,0 @@
1
- /**
2
- * shape 约束金标:契约规范 object 形状,无需 interface/type。
3
- * 唯一 refine 形态:@nudo:refine <param> <shapeName>
4
- */
5
- import { describe, it, expect } from "vitest";
6
- import { checkSource, pTrue } from "../index.ts";
7
- import {
8
- shape,
9
- number,
10
- string,
11
- isNudoConstraint,
12
- instantiateConstraint,
13
- } from "../constraint.ts";
14
- import { predToString } from "../pred.ts";
15
- import { execNudoModule } from "../refine.ts";
16
- import { withStdImport, stdOpts } from "./nudo-constraints.ts";
17
-
18
- function issuesOf(src: string) {
19
- return checkSource("/t/shape.js", withStdImport(src), pTrue, stdOpts);
20
- }
21
-
22
- function errorCodes(src: string): string[] {
23
- return issuesOf(src)
24
- .issues.filter((i) => i.severity === "error")
25
- .map((i) => i.code);
26
- }
27
-
28
- describe("shape() constraint builder", () => {
29
- it("builds field map from number()/string()", () => {
30
- const c = shape({ id: number().gt(0), name: string() });
31
- expect(isNudoConstraint(c)).toBe(true);
32
- expect(Object.keys(c.fields ?? {}).sort()).toEqual(["id", "name"]);
33
- expect(c.fields!.id!.constraint.prim).toBe("number");
34
- expect(c.fields!.name!.constraint.prim).toBe("string");
35
- });
36
-
37
- it("instantiates to field-access Preds", () => {
38
- const c = shape({ id: number().gt(0), name: string() });
39
- const p = instantiateConstraint(c, "u");
40
- const s = predToString(p);
41
- expect(s).toContain("u.id");
42
- expect(s).toContain(">");
43
- expect(s).toContain("typeof u.name");
44
- });
45
-
46
- it("optional() marks field", () => {
47
- const c = shape({ label: string().optional() });
48
- expect(c.fields!.label!.optional).toBe(true);
49
- expect(c.fields!.label!.constraint.isOptional).toBe(true);
50
- });
51
-
52
- it("executes shape in .nudo.js", () => {
53
- const src = `
54
- export const user = shape({ id: number().gt(0), name: string() });
55
- `;
56
- const exp = execNudoModule(src);
57
- expect(isNudoConstraint(exp.user)).toBe(true);
58
- expect(Object.keys((exp.user as { fields: object }).fields).sort()).toEqual([
59
- "id",
60
- "name",
61
- ]);
62
- });
63
- });
64
-
65
- describe("check: object literal ⊭ shape", () => {
66
- it("ok: valid object", () => {
67
- const r = issuesOf(`
68
- /**
69
- * @nudo:refine u userShape
70
- */
71
- function register(u) {
72
- return u.name;
73
- }
74
- register({ id: 1, name: "a" });
75
- `);
76
- expect(r.ok).toBe(true);
77
- expect(r.issues.filter((i) => i.severity === "error")).toEqual([]);
78
- });
79
-
80
- it("error: field bound violated (id > 0)", () => {
81
- const r = issuesOf(`
82
- /**
83
- * @nudo:refine u userShape
84
- */
85
- function register(u) {
86
- return u.name;
87
- }
88
- register({ id: -1, name: "a" });
89
- `);
90
- expect(r.ok).toBe(false);
91
- const err = r.issues.find((i) => i.code === "nudo:constraint-violated");
92
- expect(err).toBeDefined();
93
- expect(err!.expected).toContain("u.id");
94
- expect(err!.expected).toContain(">");
95
- });
96
-
97
- it("error: missing required field", () => {
98
- // body 不访问 name,仅契约声明要求该字段
99
- const r = issuesOf(`
100
- /**
101
- * @nudo:refine u userShape
102
- */
103
- function register(u) {
104
- return u.id;
105
- }
106
- register({ id: 1 });
107
- `);
108
- expect(r.ok).toBe(false);
109
- const err = r.issues.find((i) => i.code === "nudo:constraint-violated");
110
- expect(err).toBeDefined();
111
- expect(err!.expected).toContain("missing");
112
- expect(err!.expected).toContain("name");
113
- });
114
-
115
- it("error: field prim mismatch (name is number)", () => {
116
- const r = issuesOf(`
117
- /**
118
- * @nudo:refine u userShape
119
- */
120
- function register(u) {
121
- return u.name;
122
- }
123
- register({ id: 1, name: 2 });
124
- `);
125
- expect(r.ok).toBe(false);
126
- const err = r.issues.find((i) => i.code === "nudo:constraint-violated");
127
- expect(err).toBeDefined();
128
- expect(err!.expected).toContain("string");
129
- });
130
-
131
- it("ok: optional field omitted", () => {
132
- const r = issuesOf(`
133
- /**
134
- * @nudo:refine c configShape
135
- */
136
- function setup(c) {
137
- return c.retries;
138
- }
139
- setup({ retries: 3 });
140
- `);
141
- expect(r.issues.filter((i) => i.severity === "error")).toEqual([]);
142
- });
143
-
144
- it("error: optional field present but wrong type", () => {
145
- const r = issuesOf(`
146
- /**
147
- * @nudo:refine c configShape
148
- */
149
- function setup(c) {
150
- return c.retries;
151
- }
152
- setup({ retries: 3, label: 9 });
153
- `);
154
- expect(r.ok).toBe(false);
155
- expect(
156
- r.issues.some(
157
- (i) =>
158
- i.code === "nudo:constraint-violated" &&
159
- (i.expected ?? "").includes("string"),
160
- ),
161
- ).toBe(true);
162
- });
163
-
164
- it("error: numeric upper bound on shape field", () => {
165
- const r = issuesOf(`
166
- /**
167
- * @nudo:refine c configShape
168
- */
169
- function setup(c) {
170
- return c.retries;
171
- }
172
- setup({ retries: 99 });
173
- `);
174
- expect(r.ok).toBe(false);
175
- const err = r.issues.find((i) => i.code === "nudo:constraint-violated");
176
- expect(err).toBeDefined();
177
- expect(err!.expected).toContain("≤");
178
- });
179
-
180
- it("ok: unknown identifier arg (no false positive)", () => {
181
- const r = issuesOf(`
182
- /**
183
- * @nudo:refine u userShape
184
- */
185
- function register(u) {
186
- return u.name;
187
- }
188
- const payload = getPayload();
189
- register(payload);
190
- `);
191
- expect(r.issues.filter((i) => i.severity === "error")).toEqual([]);
192
- });
193
-
194
- it("ok: no refine → no shape gate", () => {
195
- const r = issuesOf(`
196
- function open(u) {
197
- return u;
198
- }
199
- open({ id: -1 });
200
- `);
201
- expect(r.issues.filter((i) => i.severity === "error")).toEqual([]);
202
- });
203
-
204
- it("ok: nested shape + array fields", () => {
205
- const r = issuesOf(`
206
- /**
207
- * @nudo:refine o orderShape
208
- */
209
- function place(o) {
210
- return o.user.id;
211
- }
212
- place({ user: { id: 1, name: "a" }, tags: ["x"] });
213
- `);
214
- expect(r.issues.filter((i) => i.severity === "error")).toEqual([]);
215
- });
216
-
217
- it("error: nested shape field bound", () => {
218
- const r = issuesOf(`
219
- /**
220
- * @nudo:refine o orderShape
221
- */
222
- function place(o) {
223
- return o.user.id;
224
- }
225
- place({ user: { id: -1, name: "a" }, tags: ["x"] });
226
- `);
227
- expect(r.ok).toBe(false);
228
- expect(r.issues.some((i) => i.code === "nudo:constraint-violated")).toBe(true);
229
- });
230
-
231
- it("error: array element min length", () => {
232
- const r = issuesOf(`
233
- /**
234
- * @nudo:refine o orderShape
235
- */
236
- function place(o) {
237
- return o.tags;
238
- }
239
- place({ user: { id: 1, name: "a" }, tags: [""] });
240
- `);
241
- expect(r.ok).toBe(false);
242
- expect(r.issues.some((i) => i.code === "nudo:constraint-violated")).toBe(true);
243
- });
244
-
245
- it("ok: int refine", () => {
246
- const ok = issuesOf(`
247
- /**
248
- * @nudo:refine n intId
249
- */
250
- function take(n) { return n; }
251
- take(3);
252
- `);
253
- expect(ok.issues.filter((i) => i.severity === "error")).toEqual([]);
254
- });
255
-
256
- it("error: int refine rejects non-integer", () => {
257
- const r = issuesOf(`
258
- /**
259
- * @nudo:refine n intId
260
- */
261
- function take(n) { return n; }
262
- take(1.5);
263
- `);
264
- expect(r.ok).toBe(false);
265
- expect(r.issues.some((i) => i.expected?.includes("int"))).toBe(true);
266
- });
267
-
268
- it("error: array refine rejects non-array", () => {
269
- const r = issuesOf(`
270
- /**
271
- * @nudo:refine xs positives
272
- */
273
- function take(xs) { return xs; }
274
- take(1);
275
- `);
276
- expect(r.ok).toBe(false);
277
- expect(r.issues.some((i) => i.expected?.includes("array"))).toBe(true);
278
- });
279
-
280
- it("error: array element bound", () => {
281
- const r = issuesOf(`
282
- /**
283
- * @nudo:refine xs positives
284
- */
285
- function take(xs) { return xs; }
286
- take([-1]);
287
- `);
288
- expect(r.ok).toBe(false);
289
- expect(r.issues.some((i) => i.code === "nudo:constraint-violated")).toBe(true);
290
- });
291
-
292
- it("error: string min length", () => {
293
- const r = issuesOf(`
294
- /**
295
- * @nudo:refine s shortName
296
- */
297
- function take(s) { return s; }
298
- take("");
299
- `);
300
- expect(r.ok).toBe(false);
301
- expect(r.issues.some((i) => i.expected?.includes("length"))).toBe(true);
302
- });
303
- });
@@ -1,204 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import {
3
- checkSource,
4
- resetCheckSourceMemo,
5
- getCheckSourceMemoSize,
6
- evictCheckSourceMemoForPaths,
7
- resetGeneralizeMemo,
8
- pTrue,
9
- } from "../index.ts";
10
- import { STD_NUDO_SRC, withStdImport } from "./nudo-constraints.ts";
11
-
12
- const SRC = `
13
- function add(a, b) {
14
- return a + b;
15
- }
16
- function needsPositive(x) {
17
- /** @nudo:refine x positive */
18
- return x + 1;
19
- }
20
- `;
21
-
22
- beforeEach(() => {
23
- resetCheckSourceMemo();
24
- resetGeneralizeMemo();
25
- });
26
-
27
- describe("checkSource whole-file memo", () => {
28
- it("hit returns equivalent report for same source", () => {
29
- const a = checkSource("/t/a.js", SRC, pTrue, {});
30
- const b = checkSource("/t/a.js", SRC, pTrue, {});
31
- expect(getCheckSourceMemoSize()).toBe(1);
32
- expect(b.ok).toBe(a.ok);
33
- expect(b.signatures.map((s) => s.name)).toEqual(a.signatures.map((s) => s.name));
34
- expect(b.issues.map((i) => i.code)).toEqual(a.issues.map((i) => i.code));
35
- expect(b.summary).toEqual(a.summary);
36
- });
37
-
38
- it("hits when only trailing non-nudo comments change", () => {
39
- checkSource("/t/a.js", SRC, pTrue, {});
40
- checkSource("/t/a.js", SRC + "\n// touch\n", pTrue, {});
41
- expect(getCheckSourceMemoSize()).toBe(1);
42
- });
43
-
44
- it("misses when source body changes", () => {
45
- checkSource("/t/a.js", SRC, pTrue, {});
46
- checkSource("/t/a.js", SRC + "\nconst zzz = 1;\n", pTrue, {});
47
- expect(getCheckSourceMemoSize()).toBe(2);
48
- });
49
-
50
- it("misses when filePath changes", () => {
51
- checkSource("/t/a.js", SRC, pTrue, {});
52
- checkSource("/t/b.js", SRC, pTrue, {});
53
- expect(getCheckSourceMemoSize()).toBe(2);
54
- });
55
-
56
- it("misses when loadModule identity changes", () => {
57
- const l1 = () => undefined;
58
- const l2 = () => undefined;
59
- checkSource("/t/a.js", SRC, pTrue, { loadModule: l1, fromFile: "/t/a.js" });
60
- checkSource("/t/a.js", SRC, pTrue, { loadModule: l2, fromFile: "/t/a.js" });
61
- expect(getCheckSourceMemoSize()).toBe(2);
62
- });
63
-
64
- it("misses when dep content changes", () => {
65
- let dep = "export const positive = number().gt(0);\n";
66
- const loadModule = () => dep;
67
- const opts = { loadModule, fromFile: "/t/a.js" };
68
- const src = `
69
- /// @nudo:import { positive } from "./shapes.nudo.js"
70
- function needsPositive(x) {
71
- /** @nudo:refine x positive */
72
- return x + 1;
73
- }
74
- `;
75
- checkSource("/t/a.js", src, pTrue, opts);
76
- dep = "export const positive = number().ge(0);\n";
77
- checkSource("/t/a.js", src, pTrue, opts);
78
- expect(getCheckSourceMemoSize()).toBe(2);
79
- });
80
-
81
- it("evictCheckSourceMemoForPaths drops dependents of that dep", () => {
82
- const loadModule = () => "export const positive = number().gt(0);\n";
83
- const opts = { loadModule, fromFile: "/t/a.js" };
84
- const src = `
85
- /// @nudo:import { positive } from "./shapes.nudo.js"
86
- function needsPositive(x) {
87
- /** @nudo:refine x positive */
88
- return x + 1;
89
- }
90
- `;
91
- checkSource("/t/a.js", src, pTrue, opts);
92
- checkSource("/t/b.js", SRC, pTrue, {});
93
- const before = getCheckSourceMemoSize();
94
- const n = evictCheckSourceMemoForPaths(["/t/shapes.nudo.js"]);
95
- expect(n).toBeGreaterThan(0);
96
- expect(getCheckSourceMemoSize()).toBeLessThan(before);
97
- // 无关文件仍在
98
- expect(getCheckSourceMemoSize()).toBe(1);
99
- });
100
-
101
- it("reset drops all entries", () => {
102
- checkSource("/t/a.js", SRC, pTrue, {});
103
- expect(getCheckSourceMemoSize()).toBeGreaterThan(0);
104
- resetCheckSourceMemo();
105
- expect(getCheckSourceMemoSize()).toBe(0);
106
- });
107
-
108
- it("mutating returned issues does not corrupt cache", () => {
109
- const a = checkSource("/t/a.js", SRC, pTrue, {});
110
- a.issues.push({
111
- severity: "error",
112
- code: "x",
113
- message: "mutated",
114
- });
115
- a.signatures.length = 0;
116
- const b = checkSource("/t/a.js", SRC, pTrue, {});
117
- expect(b.issues.some((i) => i.message === "mutated")).toBe(false);
118
- expect(b.signatures.length).toBeGreaterThan(0);
119
- });
120
-
121
- it("same loadModule identity hits memo (wrapper must not defeat key)", () => {
122
- const loadModule = () => undefined;
123
- const opts = { loadModule, fromFile: "/t/a.js" };
124
- checkSource("/t/a.js", SRC, pTrue, opts);
125
- const size1 = getCheckSourceMemoSize();
126
- checkSource("/t/a.js", SRC, pTrue, opts);
127
- checkSource("/t/a.js", SRC, pTrue, opts);
128
- expect(size1).toBe(1);
129
- expect(getCheckSourceMemoSize()).toBe(1);
130
- });
131
-
132
- it("hits when only trailing non-nudo comments change even with loadModule", () => {
133
- const loadModule = () => undefined;
134
- const opts = { loadModule, fromFile: "/t/a.js" };
135
- checkSource("/t/a.js", SRC, pTrue, opts);
136
- checkSource("/t/a.js", SRC + "\n// touch\n", pTrue, opts);
137
- expect(getCheckSourceMemoSize()).toBe(1);
138
- });
139
-
140
- it("reports constraint violation for const-assigned literal call", () => {
141
- const loadModule = (spec: string) =>
142
- spec.includes("std") ? STD_NUDO_SRC : undefined;
143
- const src = `${withStdImport(`
144
- /**
145
- * @nudo:refine x positive
146
- */
147
- function needsPositive(x) {
148
- if (x > 0) return x;
149
- return 0;
150
- }
151
- const bad = needsPositive(-1);
152
- `)}`;
153
- const r = checkSource("/t/scan.js", src, pTrue, { loadModule, fromFile: "/t/scan.js" });
154
- expect(r.issues.some((i) => i.code === "nudo:constraint-violated")).toBe(true);
155
- });
156
-
157
- it("reports constraint violation for if-condition literal call", () => {
158
- const loadModule = (spec: string) =>
159
- spec.includes("std") ? STD_NUDO_SRC : undefined;
160
- const src = `${withStdImport(`
161
- /**
162
- * @nudo:refine x positive
163
- */
164
- function needsPositive(x) {
165
- if (x > 0) return x;
166
- return 0;
167
- }
168
- if (needsPositive(-1)) {}
169
- `)}`;
170
- const r = checkSource("/t/scan-if.js", src, pTrue, { loadModule, fromFile: "/t/scan-if.js" });
171
- expect(r.issues.some((i) => i.code === "nudo:constraint-violated")).toBe(true);
172
- });
173
-
174
- it("fingerprints transitive require deps (grandchild change misses)", () => {
175
- const files: Record<string, string> = {
176
- "/t/w.js": "module.exports = { n: 1 };\n",
177
- "/t/v.js": 'const w = require("./w.js");\nmodule.exports = { fromW: w.n };\n',
178
- };
179
- const loadModule = (spec: string, fromFile: string) => {
180
- if (spec === "./w.js") return files["/t/w.js"];
181
- if (spec === "./v.js") return files["/t/v.js"];
182
- if (spec === "./w.js" && fromFile.endsWith("v.js")) return files["/t/w.js"];
183
- return undefined;
184
- };
185
- // resolve relative to the dep file when nested
186
- const load = (spec: string, fromFile: string) => {
187
- if (fromFile === "/t/a.js" && spec === "./v.js") return files["/t/v.js"];
188
- if (fromFile === "/t/v.js" && spec === "./w.js") return files["/t/w.js"];
189
- return loadModule(spec, fromFile);
190
- };
191
- const opts = { loadModule: load, fromFile: "/t/a.js" };
192
- const src = `
193
- const v = require("./v.js");
194
- function id(x) { return x; }
195
- id(v.fromW);
196
- `;
197
- checkSource("/t/a.js", src, pTrue, opts);
198
- expect(getCheckSourceMemoSize()).toBe(1);
199
- // grandchild content change, parent + child sources unchanged
200
- files["/t/w.js"] = "module.exports = { n: 2 };\n";
201
- checkSource("/t/a.js", src, pTrue, opts);
202
- expect(getCheckSourceMemoSize()).toBe(2);
203
- });
204
- });
@@ -1,69 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { checkSource, formatCheckReport, pTrue } from "../index.ts";
3
- import { withStdImport, stdOpts } from "./nudo-constraints.ts";
4
-
5
- describe("nudo check gate", () => {
6
- it("literal call violating constraint is error", () => {
7
- // @nudo:refine 声明契约;调用 -1 应报错
8
- const src = `
9
- /**
10
- * @nudo:refine x positive
11
- */
12
- function needsPositive(x) {
13
- if (x > 0) return x;
14
- return 0;
15
- }
16
- const r = needsPositive(-1);
17
- `;
18
- const report = checkSource("t.js", withStdImport(src), pTrue, stdOpts);
19
- expect(report.ok).toBe(false);
20
- const err = report.issues.find((i) => i.severity === "error");
21
- expect(err).toBeDefined();
22
- expect(err!.code).toBe("nudo:constraint-violated");
23
- });
24
-
25
- it("valid literal call is ok", () => {
26
- const src = `
27
- /**
28
- * @nudo:refine x positive
29
- */
30
- function needsPositive(x) {
31
- if (x > 0) return x;
32
- return 0;
33
- }
34
- const r = needsPositive(5);
35
- `;
36
- const report = checkSource("t.js", withStdImport(src), pTrue, stdOpts);
37
- expect(report.ok).toBe(true);
38
- expect(report.signatures.some((f) => f.name === "needsPositive")).toBe(true);
39
- });
40
-
41
- it("reports intensional signatures", () => {
42
- const src = `
43
- function add(a, b) { return a + b; }
44
- function scale(x) { return add(x, 1); }
45
- `;
46
- const report = checkSource("t.js", withStdImport(src), pTrue, stdOpts);
47
- const scale = report.signatures.find((f) => f.name === "scale");
48
- expect(scale!.display).toContain("A1");
49
- });
50
-
51
- it("formatCheckReport is Nudo-native (signatures + issues)", () => {
52
- const bad = checkSource(
53
- "t.js",
54
- withStdImport(`/**
55
- * @nudo:refine x positive
56
- */
57
- function f(x){ if (x>0) return x; return 0; }
58
- f(-1);
59
- `),
60
- pTrue,
61
- stdOpts,
62
- );
63
- const text = formatCheckReport(bad);
64
- expect(text).toContain("FAILED");
65
- expect(text).toContain("signatures");
66
- expect(text).toContain("actual:");
67
- expect(text).toContain("expected:");
68
- });
69
- });