@nudojs/cli 0.2.1 → 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-3HCPXPEW.js → chunk-YDM4HHQ4.js} +1783 -1421
- package/dist/evaluator-api.d.ts +12 -1
- package/dist/evaluator-api.js +52 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +366 -3572
- package/package.json +23 -11
- package/CHANGELOG.md +0 -41
- 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/builtin-intl.ts +0 -17
- package/src/builtins/builtin-map.ts +0 -174
- package/src/builtins/builtin-promise.ts +0 -116
- package/src/builtins/builtin-reflect.ts +0 -30
- package/src/builtins/builtin-regexp.ts +0 -34
- package/src/builtins/builtin-set.ts +0 -131
- package/src/builtins/builtin-symbol.ts +0 -16
- package/src/builtins/builtin-url.ts +0 -58
- package/src/builtins/builtin-weak.ts +0 -22
- package/src/builtins/builtin-web.ts +0 -111
- package/src/builtins/index.ts +0 -61
- package/src/config.ts +0 -32
- package/src/env-loader.ts +0 -174
- package/src/evaluator-api.ts +0 -36
- package/src/evaluator.ts +0 -5623
- package/src/index.ts +0 -931
- package/src/narrowing.ts +0 -537
- package/src/resolve-npm.ts +0 -63
- package/tsconfig.json +0 -9
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { parse, extractDirectives, type CaseDirective } from "@nudojs/parser";
|
|
3
|
-
import { typeValueToString, createEnvironment } from "@nudojs/core";
|
|
4
|
-
import { evaluateFunctionFull } from "../evaluator.js";
|
|
5
|
-
|
|
6
|
-
function runTest(source: string): { name: string; caseName: string; result: string }[] {
|
|
7
|
-
const ast = parse(source);
|
|
8
|
-
const directives = extractDirectives(ast);
|
|
9
|
-
const env = createEnvironment();
|
|
10
|
-
const results: { name: string; caseName: string; result: string }[] = [];
|
|
11
|
-
|
|
12
|
-
for (const fn of directives) {
|
|
13
|
-
const caseDirectives = fn.directives.filter((d): d is CaseDirective => d.kind === "case");
|
|
14
|
-
for (const dir of caseDirectives) {
|
|
15
|
-
const result = evaluateFunctionFull(fn.node, dir.args, env);
|
|
16
|
-
results.push({
|
|
17
|
-
name: fn.name,
|
|
18
|
-
caseName: dir.name,
|
|
19
|
-
result: typeValueToString(result.value),
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return results;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe("Syntax Sugar Tests", () => {
|
|
27
|
-
describe("Compound Assignment Operators", () => {
|
|
28
|
-
it("+=", () => {
|
|
29
|
-
const results = runTest(`
|
|
30
|
-
// @nudo:case "add-assign" ([1, 2, 3, 4, 5])
|
|
31
|
-
function sum(arr) {
|
|
32
|
-
let total = 0;
|
|
33
|
-
for (const n of arr) {
|
|
34
|
-
total += n;
|
|
35
|
-
}
|
|
36
|
-
return total;
|
|
37
|
-
}
|
|
38
|
-
`);
|
|
39
|
-
expect(results[0].result).toBe("15");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("-=", () => {
|
|
43
|
-
const results = runTest(`
|
|
44
|
-
// @nudo:case "sub-assign" (100, [10, 20, 30])
|
|
45
|
-
function subtract(initial, values) {
|
|
46
|
-
let result = initial;
|
|
47
|
-
for (const v of values) {
|
|
48
|
-
result -= v;
|
|
49
|
-
}
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
`);
|
|
53
|
-
expect(results[0].result).toBe("40");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("*=", () => {
|
|
57
|
-
const results = runTest(`
|
|
58
|
-
// @nudo:case "mul-assign" ([2, 3, 4])
|
|
59
|
-
function product(arr) {
|
|
60
|
-
let result = 1;
|
|
61
|
-
for (const n of arr) {
|
|
62
|
-
result *= n;
|
|
63
|
-
}
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
`);
|
|
67
|
-
expect(results[0].result).toBe("24");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("/=", () => {
|
|
71
|
-
const results = runTest(`
|
|
72
|
-
// @nudo:case "div-assign" (1000, [2, 5])
|
|
73
|
-
function divide(initial, divisors) {
|
|
74
|
-
let result = initial;
|
|
75
|
-
for (const d of divisors) {
|
|
76
|
-
result /= d;
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
79
|
-
}
|
|
80
|
-
`);
|
|
81
|
-
expect(results[0].result).toBe("100");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("%=", () => {
|
|
85
|
-
const results = runTest(`
|
|
86
|
-
// @nudo:case "mod-assign" (17, [5, 3])
|
|
87
|
-
function modChain(initial, divisors) {
|
|
88
|
-
let result = initial;
|
|
89
|
-
for (const d of divisors) {
|
|
90
|
-
result %= d;
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
`);
|
|
95
|
-
expect(results[0].result).toBe("2");
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("string +=", () => {
|
|
99
|
-
const results = runTest(`
|
|
100
|
-
// @nudo:case "concat-assign" (["Hello", " ", "World"])
|
|
101
|
-
function concat(parts) {
|
|
102
|
-
let result = "";
|
|
103
|
-
for (const part of parts) {
|
|
104
|
-
result += part;
|
|
105
|
-
}
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
`);
|
|
109
|
-
expect(results[0].result).toBe('"Hello World"');
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
describe("Object Method Shorthand", () => {
|
|
114
|
-
it("basic method shorthand", () => {
|
|
115
|
-
const results = runTest(`
|
|
116
|
-
// @nudo:case "method" ()
|
|
117
|
-
function createObj() {
|
|
118
|
-
return {
|
|
119
|
-
greet(name) {
|
|
120
|
-
return "Hello, " + name;
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
`);
|
|
125
|
-
expect(results[0].result).toContain("greet");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("multiple methods", () => {
|
|
129
|
-
const results = runTest(`
|
|
130
|
-
// @nudo:case "methods" ()
|
|
131
|
-
function createCalculator() {
|
|
132
|
-
return {
|
|
133
|
-
add(a, b) { return a + b; },
|
|
134
|
-
subtract(a, b) { return a - b; },
|
|
135
|
-
multiply(a, b) { return a * b; }
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
`);
|
|
139
|
-
expect(results[0].result).toContain("add");
|
|
140
|
-
expect(results[0].result).toContain("subtract");
|
|
141
|
-
expect(results[0].result).toContain("multiply");
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("method with rest parameters", () => {
|
|
145
|
-
const results = runTest(`
|
|
146
|
-
// @nudo:case "rest-params" ()
|
|
147
|
-
function createLogger() {
|
|
148
|
-
return {
|
|
149
|
-
log(...args) {
|
|
150
|
-
return args;
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
`);
|
|
155
|
-
expect(results[0].result).toContain("log");
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("mixed properties and methods", () => {
|
|
159
|
-
const results = runTest(`
|
|
160
|
-
// @nudo:case "mixed" ()
|
|
161
|
-
function createService() {
|
|
162
|
-
return {
|
|
163
|
-
name: "MyService",
|
|
164
|
-
version: 1,
|
|
165
|
-
start() { return "started"; },
|
|
166
|
-
stop() { return "stopped"; }
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
`);
|
|
170
|
-
expect(results[0].result).toContain("name");
|
|
171
|
-
expect(results[0].result).toContain("version");
|
|
172
|
-
expect(results[0].result).toContain("start");
|
|
173
|
-
expect(results[0].result).toContain("stop");
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
describe("Async/Await Combinations", () => {
|
|
178
|
-
it("async function returning literal", () => {
|
|
179
|
-
const results = runTest(`
|
|
180
|
-
// @nudo:case "async-literal" ()
|
|
181
|
-
async function getValue() {
|
|
182
|
-
return 42;
|
|
183
|
-
}
|
|
184
|
-
`);
|
|
185
|
-
expect(results[0].result).toBe("Promise<42>");
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("async function returning Promise", () => {
|
|
189
|
-
const results = runTest(`
|
|
190
|
-
// @nudo:case "async-promise" ()
|
|
191
|
-
async function getValue() {
|
|
192
|
-
return Promise.resolve(42);
|
|
193
|
-
}
|
|
194
|
-
`);
|
|
195
|
-
expect(results[0].result).toBe("Promise<42>");
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("async function with await", () => {
|
|
199
|
-
const results = runTest(`
|
|
200
|
-
// @nudo:case "async-await" ()
|
|
201
|
-
async function getValue() {
|
|
202
|
-
const result = await Promise.resolve(42);
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
`);
|
|
206
|
-
expect(results[0].result).toBe("Promise<42>");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("async function with try/catch", () => {
|
|
210
|
-
const results = runTest(`
|
|
211
|
-
// @nudo:case "async-try-catch" ()
|
|
212
|
-
async function safeOperation() {
|
|
213
|
-
try {
|
|
214
|
-
const result = await Promise.resolve("success");
|
|
215
|
-
return { success: true, data: result };
|
|
216
|
-
} catch (e) {
|
|
217
|
-
return { success: false, error: "failed" };
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
`);
|
|
221
|
-
expect(results[0].result).toContain("Promise");
|
|
222
|
-
expect(results[0].result).toContain("success");
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it("async function with multiple awaits", () => {
|
|
226
|
-
const results = runTest(`
|
|
227
|
-
// @nudo:case "async-multi-await" ()
|
|
228
|
-
async function fetchAll() {
|
|
229
|
-
const a = await Promise.resolve(1);
|
|
230
|
-
const b = await Promise.resolve(2);
|
|
231
|
-
return [a, b];
|
|
232
|
-
}
|
|
233
|
-
`);
|
|
234
|
-
expect(results[0].result).toBe("Promise<[1, 2]>");
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
describe("Destructuring Patterns", () => {
|
|
239
|
-
it("object destructuring in parameters", () => {
|
|
240
|
-
const results = runTest(`
|
|
241
|
-
// @nudo:case "destructure-obj" ({ name: "Alice", age: 30 })
|
|
242
|
-
function greet({ name, age }) {
|
|
243
|
-
return name + " is " + age;
|
|
244
|
-
}
|
|
245
|
-
`);
|
|
246
|
-
expect(results[0].result).toBe('"Alice is 30"');
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it("array destructuring in parameters", () => {
|
|
250
|
-
const results = runTest(`
|
|
251
|
-
// @nudo:case "destructure-arr" ([1, 2, 3])
|
|
252
|
-
function sum([a, b, c]) {
|
|
253
|
-
return a + b + c;
|
|
254
|
-
}
|
|
255
|
-
`);
|
|
256
|
-
expect(results[0].result).toBe("6");
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it("nested destructuring", () => {
|
|
260
|
-
const results = runTest(`
|
|
261
|
-
// @nudo:case "destructure-nested" ({ user: { name: "Alice", address: { city: "NYC" } } })
|
|
262
|
-
function getCity({ user: { address: { city } } }) {
|
|
263
|
-
return city;
|
|
264
|
-
}
|
|
265
|
-
`);
|
|
266
|
-
expect(results[0].result).toBe('"NYC"');
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it("destructuring with defaults", () => {
|
|
270
|
-
const results = runTest(`
|
|
271
|
-
// @nudo:case "destructure-default" ({ name: "Alice" })
|
|
272
|
-
function greet({ name, greeting = "Hello" }) {
|
|
273
|
-
return greeting + ", " + name;
|
|
274
|
-
}
|
|
275
|
-
`);
|
|
276
|
-
expect(results[0].result).toBe('"Hello, Alice"');
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
describe("Template Literals", () => {
|
|
281
|
-
it("basic template literal", () => {
|
|
282
|
-
const results = runTest(`
|
|
283
|
-
// @nudo:case "template" ("World")
|
|
284
|
-
function greet(name) {
|
|
285
|
-
return \`Hello, \${name}!\`;
|
|
286
|
-
}
|
|
287
|
-
`);
|
|
288
|
-
expect(results[0].result).toBe('"Hello, World!"');
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it("template literal with expressions", () => {
|
|
292
|
-
const results = runTest(`
|
|
293
|
-
// @nudo:case "template-expr" (2, 3)
|
|
294
|
-
function calculate(a, b) {
|
|
295
|
-
return \`\${a} + \${b} = \${a + b}\`;
|
|
296
|
-
}
|
|
297
|
-
`);
|
|
298
|
-
expect(results[0].result).toBe('"2 + 3 = 5"');
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe("Optional Chaining", () => {
|
|
303
|
-
it("basic optional chaining", () => {
|
|
304
|
-
const results = runTest(`
|
|
305
|
-
// @nudo:case "optional-chain" ({ user: { name: "Alice" } })
|
|
306
|
-
function getName(obj) {
|
|
307
|
-
return obj?.user?.name;
|
|
308
|
-
}
|
|
309
|
-
`);
|
|
310
|
-
expect(results[0].result).toBe('"Alice"');
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("optional chaining with undefined", () => {
|
|
314
|
-
const results = runTest(`
|
|
315
|
-
// @nudo:case "optional-undefined" ({})
|
|
316
|
-
function getName(obj) {
|
|
317
|
-
return obj?.user?.name;
|
|
318
|
-
}
|
|
319
|
-
`);
|
|
320
|
-
expect(results[0].result).toBe("undefined");
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
describe("Nullish Coalescing", () => {
|
|
325
|
-
it("basic nullish coalescing", () => {
|
|
326
|
-
const results = runTest(`
|
|
327
|
-
// @nudo:case "nullish" (null)
|
|
328
|
-
function getValue(x) {
|
|
329
|
-
return x ?? "default";
|
|
330
|
-
}
|
|
331
|
-
`);
|
|
332
|
-
expect(results[0].result).toBe('"default"');
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
it("nullish with defined value", () => {
|
|
336
|
-
const results = runTest(`
|
|
337
|
-
// @nudo:case "nullish-defined" ("hello")
|
|
338
|
-
function getValue(x) {
|
|
339
|
-
return x ?? "default";
|
|
340
|
-
}
|
|
341
|
-
`);
|
|
342
|
-
expect(results[0].result).toBe('"hello"');
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type TypeValue, T } from "@nudojs/core";
|
|
2
|
-
|
|
3
|
-
export const INTL_DATETIMEFORMAT_METHODS: Record<string, (...args: TypeValue[]) => TypeValue> = {
|
|
4
|
-
format: () => T.string,
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export const INTL_NUMBERFORMAT_METHODS: Record<string, (...args: TypeValue[]) => TypeValue> = {
|
|
8
|
-
format: () => T.string,
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export function createDateTimeFormatType(): TypeValue {
|
|
12
|
-
return T.instanceOf("DateTimeFormat", {});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function createNumberFormatType(): TypeValue {
|
|
16
|
-
return T.instanceOf("NumberFormat", {});
|
|
17
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { type TypeValue, T, simplifyUnion, typeValueEquals } from "@nudojs/core";
|
|
2
|
-
|
|
3
|
-
// Literal/instance keys recorded at construction (`new Map([['k', v]])`) or
|
|
4
|
-
// via set() — precise enough for typeValueEquals lookup. Symbolic keys are
|
|
5
|
-
// skipped: they cannot be definitely matched, so get() falls back to the
|
|
6
|
-
// V | undefined approximation.
|
|
7
|
-
export type MapEntry = { key: TypeValue; value: TypeValue };
|
|
8
|
-
|
|
9
|
-
function entryKeyTrackable(key: TypeValue | undefined): boolean {
|
|
10
|
-
return key?.kind === "literal" || key?.kind === "instance";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Union keys distribute: every member hitting → union of values, every
|
|
14
|
-
// member missing → undefined, mixed → values | undefined.
|
|
15
|
-
function lookupEntries(entries: MapEntry[], key: TypeValue): TypeValue | null {
|
|
16
|
-
if (key.kind === "union") {
|
|
17
|
-
const results = key.members.map((m) => lookupEntries(entries, m));
|
|
18
|
-
if (results.some((r) => r === null)) return null;
|
|
19
|
-
return simplifyUnion(results as TypeValue[]);
|
|
20
|
-
}
|
|
21
|
-
if (!entryKeyTrackable(key)) return null;
|
|
22
|
-
const hit = entries.find((e) => typeValueEquals(e.key, key));
|
|
23
|
-
return hit ? hit.value : T.undefined;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const MAP_INSTANCE_METHODS: Record<string, (...args: TypeValue[]) => TypeValue> = {
|
|
27
|
-
get: (key: TypeValue, map?: TypeValue) => {
|
|
28
|
-
const entries = exactMapEntries(map);
|
|
29
|
-
if (entries) {
|
|
30
|
-
const looked = lookupEntries(entries, key);
|
|
31
|
-
if (looked !== null) return looked;
|
|
32
|
-
}
|
|
33
|
-
const typeArgs = (map as any)?._typeArgs;
|
|
34
|
-
if (typeArgs?.V) return T.union(typeArgs.V, T.undefined);
|
|
35
|
-
// Untyped receiver (unknown / non-Map value reaching the impl): a
|
|
36
|
-
// definite undefined would be a false claim and poison downstream
|
|
37
|
-
// guards (hoek isDeepEqual's `ref.name` path) into error-severity
|
|
38
|
-
// misses. Unknown keeps it honest.
|
|
39
|
-
return T.unknown;
|
|
40
|
-
},
|
|
41
|
-
set: (key: TypeValue, value: TypeValue, map?: TypeValue) => {
|
|
42
|
-
if (map) {
|
|
43
|
-
const typeArgs = (map as any)._typeArgs || { K: T.unknown, V: T.unknown };
|
|
44
|
-
(map as any)._typeArgs = {
|
|
45
|
-
K: typeArgs.K.kind === "unknown" ? key : simplifyUnion([typeArgs.K, key]),
|
|
46
|
-
V: typeArgs.V.kind === "unknown" ? value : simplifyUnion([typeArgs.V, value]),
|
|
47
|
-
};
|
|
48
|
-
if (entryKeyTrackable(key)) {
|
|
49
|
-
// An exact entry list can start on the first trackable set()
|
|
50
|
-
// (nothing untracked came before); once partial it stays partial.
|
|
51
|
-
if ((map as any)._entries === undefined && (map as any)._entriesExact !== false) {
|
|
52
|
-
(map as any)._entries = [];
|
|
53
|
-
(map as any)._entriesExact = true;
|
|
54
|
-
}
|
|
55
|
-
if ((map as any)._entriesExact) {
|
|
56
|
-
const entries = (map as any)._entries as MapEntry[];
|
|
57
|
-
const hit = entries.find((e) => typeValueEquals(e.key, key));
|
|
58
|
-
if (hit) hit.value = value;
|
|
59
|
-
else entries.push({ key, value });
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
(map as any)._entriesExact = false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return map ?? T.unknown;
|
|
66
|
-
},
|
|
67
|
-
has: (key: TypeValue, map?: TypeValue) => {
|
|
68
|
-
const entries = exactMapEntries(map);
|
|
69
|
-
if (entries && entryKeyTrackable(key)) {
|
|
70
|
-
return T.literal(entries.some((e) => typeValueEquals(e.key, key)));
|
|
71
|
-
}
|
|
72
|
-
return T.boolean;
|
|
73
|
-
},
|
|
74
|
-
delete: (key: TypeValue, map?: TypeValue) => {
|
|
75
|
-
const entries = exactMapEntries(map);
|
|
76
|
-
if (entries && entryKeyTrackable(key)) {
|
|
77
|
-
const idx = entries.findIndex((e) => typeValueEquals(e.key, key));
|
|
78
|
-
if (idx >= 0) {
|
|
79
|
-
entries.splice(idx, 1);
|
|
80
|
-
return T.literal(true);
|
|
81
|
-
}
|
|
82
|
-
return T.literal(false);
|
|
83
|
-
}
|
|
84
|
-
return T.boolean;
|
|
85
|
-
},
|
|
86
|
-
clear: (value?: TypeValue, map?: TypeValue) => {
|
|
87
|
-
// m.clear() arrives as clear(receiver): the map sits in the first
|
|
88
|
-
// param slot when the call has no arguments.
|
|
89
|
-
const target = map ?? value;
|
|
90
|
-
if (target && (target as any)._entriesExact) {
|
|
91
|
-
(target as any)._entries = [];
|
|
92
|
-
}
|
|
93
|
-
return T.undefined;
|
|
94
|
-
},
|
|
95
|
-
forEach: () => T.undefined,
|
|
96
|
-
keys: (map?: TypeValue) => {
|
|
97
|
-
const entries = exactMapEntries(map);
|
|
98
|
-
if (entries) return T.tuple(entries.map((e) => e.key));
|
|
99
|
-
const typeArgs = (map as any)?._typeArgs;
|
|
100
|
-
if (typeArgs?.K) return T.array(typeArgs.K);
|
|
101
|
-
return T.array(T.unknown);
|
|
102
|
-
},
|
|
103
|
-
values: (map?: TypeValue) => {
|
|
104
|
-
const entries = exactMapEntries(map);
|
|
105
|
-
if (entries) return T.tuple(entries.map((e) => e.value));
|
|
106
|
-
const typeArgs = (map as any)?._typeArgs;
|
|
107
|
-
if (typeArgs?.V) return T.array(typeArgs.V);
|
|
108
|
-
return T.array(T.unknown);
|
|
109
|
-
},
|
|
110
|
-
entries: (map?: TypeValue) => mapEntriesIterable(map),
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Exact entry side table: present only when every key seen so far
|
|
114
|
-
// (construction pairs + set() calls) was trackable. Partial tables must
|
|
115
|
-
// not decide get()/has()/delete()/iteration — an untracked key could map
|
|
116
|
-
// to anything.
|
|
117
|
-
export function exactMapEntries(map: TypeValue | undefined): MapEntry[] | undefined {
|
|
118
|
-
if (!map || (map as any)._entriesExact !== true) return undefined;
|
|
119
|
-
return (map as any)._entries as MapEntry[] | undefined;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// entries() keeps feeding for-of destructuring (`for (const [k, v] of m)`)
|
|
123
|
-
// and `new Map(...)` consumers: a tuple of [key, value] pairs when the
|
|
124
|
-
// side table is complete, else Array<[K, V]>.
|
|
125
|
-
export function mapEntriesIterable(map: TypeValue | undefined): TypeValue {
|
|
126
|
-
const entries = exactMapEntries(map);
|
|
127
|
-
if (entries) return T.tuple(entries.map((e) => T.tuple([e.key, e.value])));
|
|
128
|
-
const typeArgs = (map as any)?._typeArgs;
|
|
129
|
-
if (typeArgs?.K && typeArgs?.V) return T.array(T.tuple([typeArgs.K, typeArgs.V]));
|
|
130
|
-
return T.array(T.tuple([T.unknown, T.unknown]));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function createMapType(args?: TypeValue[]): TypeValue {
|
|
134
|
-
const obj = T.instanceOf("Map", {});
|
|
135
|
-
|
|
136
|
-
if (args && args.length > 0) {
|
|
137
|
-
const arg = args[0];
|
|
138
|
-
if (arg.kind === "tuple" || arg.kind === "array") {
|
|
139
|
-
const elements = arg.kind === "tuple" ? arg.elements : [arg.element];
|
|
140
|
-
const keys: TypeValue[] = [];
|
|
141
|
-
const values: TypeValue[] = [];
|
|
142
|
-
const entries: MapEntry[] = [];
|
|
143
|
-
let exact = true;
|
|
144
|
-
for (const el of elements) {
|
|
145
|
-
if (el.kind === "tuple" && el.elements.length >= 2) {
|
|
146
|
-
keys.push(el.elements[0]);
|
|
147
|
-
values.push(el.elements[1]);
|
|
148
|
-
if (entryKeyTrackable(el.elements[0])) {
|
|
149
|
-
entries.push({ key: el.elements[0], value: el.elements[1] });
|
|
150
|
-
} else {
|
|
151
|
-
exact = false;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (keys.length > 0) {
|
|
156
|
-
(obj as any)._typeArgs = { K: simplifyUnion(keys), V: simplifyUnion(values) };
|
|
157
|
-
}
|
|
158
|
-
// Only a fully trackable key list is exact: symbolic keys mean the
|
|
159
|
-
// recorded entries are a subset of the real ones.
|
|
160
|
-
if (exact && entries.length > 0) {
|
|
161
|
-
(obj as any)._entries = entries;
|
|
162
|
-
(obj as any)._entriesExact = true;
|
|
163
|
-
} else if (!exact) {
|
|
164
|
-
(obj as any)._entriesExact = false;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (!(obj as any)._typeArgs) {
|
|
170
|
-
(obj as any)._typeArgs = { K: T.unknown, V: T.unknown };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return obj;
|
|
174
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { type TypeValue, T, simplifyUnion } from "@nudojs/core";
|
|
2
|
-
|
|
3
|
-
export const PROMISE_STATIC_METHODS: Record<string, TypeValue> = {
|
|
4
|
-
resolve: T.unknown,
|
|
5
|
-
reject: T.never,
|
|
6
|
-
all: T.unknown,
|
|
7
|
-
race: T.unknown,
|
|
8
|
-
allSettled: T.unknown,
|
|
9
|
-
any: T.unknown,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function evaluatePromiseStaticMethod(
|
|
13
|
-
methodName: string,
|
|
14
|
-
args: TypeValue[],
|
|
15
|
-
): TypeValue | null {
|
|
16
|
-
if (methodName === "resolve") {
|
|
17
|
-
if (args.length > 0) return T.promise(args[0]);
|
|
18
|
-
return T.promise(T.undefined);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (methodName === "reject") {
|
|
22
|
-
return T.promise(T.never);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (methodName === "all") {
|
|
26
|
-
if (args.length > 0 && args[0].kind === "tuple") {
|
|
27
|
-
const elemTypes = args[0].elements.map((e: TypeValue) => {
|
|
28
|
-
if (e.kind === "promise") return e.value;
|
|
29
|
-
return T.unknown;
|
|
30
|
-
});
|
|
31
|
-
return T.promise(T.tuple(elemTypes));
|
|
32
|
-
}
|
|
33
|
-
if (args.length > 0 && args[0].kind === "array") {
|
|
34
|
-
return T.promise(T.array(args[0].element));
|
|
35
|
-
}
|
|
36
|
-
return T.promise(T.array(T.unknown));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (methodName === "race") {
|
|
40
|
-
if (args.length > 0 && args[0].kind === "tuple") {
|
|
41
|
-
const elemTypes = args[0].elements.map((e: TypeValue) => {
|
|
42
|
-
if (e.kind === "promise") return e.value;
|
|
43
|
-
return T.unknown;
|
|
44
|
-
});
|
|
45
|
-
return T.promise(simplifyUnion(elemTypes));
|
|
46
|
-
}
|
|
47
|
-
if (args.length > 0 && args[0].kind === "array") {
|
|
48
|
-
const el = args[0].element;
|
|
49
|
-
return T.promise(el.kind === "promise" ? el.value : T.unknown);
|
|
50
|
-
}
|
|
51
|
-
return T.promise(T.unknown);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (methodName === "allSettled") {
|
|
55
|
-
// Best effort: each slot settles to { status, value } — the fulfilled
|
|
56
|
-
// value is the element's promise payload (unknown for non-promises).
|
|
57
|
-
const settledOf = (e: TypeValue): TypeValue =>
|
|
58
|
-
T.object({
|
|
59
|
-
status: T.union(T.literal("fulfilled"), T.literal("rejected")),
|
|
60
|
-
value: e.kind === "promise" ? e.value : T.unknown,
|
|
61
|
-
});
|
|
62
|
-
if (args.length > 0 && args[0].kind === "tuple") {
|
|
63
|
-
return T.promise(T.tuple(args[0].elements.map(settledOf)));
|
|
64
|
-
}
|
|
65
|
-
if (args.length > 0 && args[0].kind === "array") {
|
|
66
|
-
return T.promise(T.array(settledOf(args[0].element)));
|
|
67
|
-
}
|
|
68
|
-
return T.promise(T.array(T.unknown));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (methodName === "any") {
|
|
72
|
-
if (args.length > 0 && args[0].kind === "tuple") {
|
|
73
|
-
const elemTypes = args[0].elements.map((e: TypeValue) => {
|
|
74
|
-
if (e.kind === "promise") return e.value;
|
|
75
|
-
return T.unknown;
|
|
76
|
-
});
|
|
77
|
-
return T.promise(simplifyUnion(elemTypes));
|
|
78
|
-
}
|
|
79
|
-
if (args.length > 0 && args[0].kind === "array") {
|
|
80
|
-
const el = args[0].element;
|
|
81
|
-
return T.promise(el.kind === "promise" ? el.value : T.unknown);
|
|
82
|
-
}
|
|
83
|
-
return T.promise(T.unknown);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function evaluatePromiseInstanceMethod(
|
|
90
|
-
promiseValue: TypeValue,
|
|
91
|
-
methodName: string,
|
|
92
|
-
args: TypeValue[],
|
|
93
|
-
): TypeValue | null {
|
|
94
|
-
if (methodName === "then") {
|
|
95
|
-
if (promiseValue.kind === "promise") {
|
|
96
|
-
// A real function callback is executed by the evaluator's main call
|
|
97
|
-
// path (applyPromiseThenCallback) before reaching here; only
|
|
98
|
-
// non-callable handlers fall through to an unresolved value type.
|
|
99
|
-
return T.promise(T.unknown);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (methodName === "catch") {
|
|
104
|
-
if (promiseValue.kind === "promise") {
|
|
105
|
-
return promiseValue; // catch returns same promise type
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (methodName === "finally") {
|
|
110
|
-
if (promiseValue.kind === "promise") {
|
|
111
|
-
return promiseValue; // finally returns same promise type
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type TypeValue, T } from "@nudojs/core";
|
|
2
|
-
|
|
3
|
-
export const REFLECT_METHODS: Record<string, (...args: TypeValue[]) => TypeValue> = {
|
|
4
|
-
has: () => T.boolean,
|
|
5
|
-
get: () => T.unknown,
|
|
6
|
-
set: () => T.boolean,
|
|
7
|
-
deleteProperty: () => T.boolean,
|
|
8
|
-
apply: () => T.unknown,
|
|
9
|
-
construct: () => T.object({}),
|
|
10
|
-
// Shape-aware like Object.keys: known receivers list their declared
|
|
11
|
-
// own-property names (hoek's Utils.keys → clone/flatten key loops then
|
|
12
|
-
// iterate concrete keys instead of a symbolic string), arrays add the
|
|
13
|
-
// non-enumerable "length" own key. Unknown shapes keep the approximation.
|
|
14
|
-
ownKeys: (target?: TypeValue) => {
|
|
15
|
-
let base = target;
|
|
16
|
-
while (base?.kind === "refined") base = base.base;
|
|
17
|
-
if (base && (base.kind === "object" || base.kind === "instance")) {
|
|
18
|
-
return T.tuple(Object.keys(base.properties).map((k) => T.literal(k)));
|
|
19
|
-
}
|
|
20
|
-
if (base?.kind === "tuple") {
|
|
21
|
-
return T.tuple([
|
|
22
|
-
...base.elements.map((_, i) => T.literal(String(i))),
|
|
23
|
-
T.literal("length"),
|
|
24
|
-
]);
|
|
25
|
-
}
|
|
26
|
-
return T.array(T.string);
|
|
27
|
-
},
|
|
28
|
-
getPrototypeOf: () => T.union(T.object({}), T.null),
|
|
29
|
-
getOwnPropertyDescriptor: () => T.union(T.object({}), T.undefined),
|
|
30
|
-
};
|