@nudojs/cli 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/package.json +12 -7
  2. package/src/abs-route.ts +273 -0
  3. package/src/builtins/builtin-prototype.ts +279 -0
  4. package/src/builtins/builtin-static.ts +260 -0
  5. package/src/eval-binary.ts +113 -0
  6. package/src/evaluator-api.ts +3 -0
  7. package/src/evaluator.ts +174 -633
  8. package/src/index.ts +373 -270
  9. package/src/narrowing.ts +7 -0
  10. package/src/phi-from-test.ts +42 -0
  11. package/src/run-test.ts +92 -0
  12. package/src/term-registry.ts +96 -0
  13. package/CHANGELOG.md +0 -41
  14. package/dist/chunk-3HCPXPEW.js +0 -5286
  15. package/dist/evaluator-api.d.ts +0 -151
  16. package/dist/evaluator-api.js +0 -214
  17. package/dist/index.d.ts +0 -2
  18. package/dist/index.js +0 -4035
  19. package/examples/real-world.js +0 -66
  20. package/examples/validate-real-world.ts +0 -160
  21. package/src/__tests__/builtin-advanced.test.ts +0 -292
  22. package/src/__tests__/builtin-api.test.ts +0 -308
  23. package/src/__tests__/builtin-functions.test.ts +0 -313
  24. package/src/__tests__/builtin-map.test.ts +0 -129
  25. package/src/__tests__/builtin-promise.test.ts +0 -150
  26. package/src/__tests__/builtin-regexp.test.ts +0 -133
  27. package/src/__tests__/builtin-set.test.ts +0 -163
  28. package/src/__tests__/builtin-url.test.ts +0 -256
  29. package/src/__tests__/builtin-web.test.ts +0 -340
  30. package/src/__tests__/combination-scenarios.test.ts +0 -384
  31. package/src/__tests__/edge-cases.test.ts +0 -749
  32. package/src/__tests__/evaluator-loops.test.ts +0 -283
  33. package/src/__tests__/evaluator-nullish-coalescing.test.ts +0 -86
  34. package/src/__tests__/evaluator-optional-chaining.test.ts +0 -37
  35. package/src/__tests__/evaluator-phase2.test.ts +0 -564
  36. package/src/__tests__/evaluator-phase3.test.ts +0 -864
  37. package/src/__tests__/evaluator-refined.test.ts +0 -121
  38. package/src/__tests__/evaluator-string-methods.test.ts +0 -151
  39. package/src/__tests__/evaluator.test.ts +0 -943
  40. package/src/__tests__/mock-arrow-function.test.ts +0 -169
  41. package/src/__tests__/mock-comparison.test.ts +0 -221
  42. package/src/__tests__/mock-comprehensive.test.ts +0 -250
  43. package/src/__tests__/mock-final-integration.test.ts +0 -263
  44. package/src/__tests__/mock-helpers.test.ts +0 -174
  45. package/src/__tests__/mock-realworld.test.ts +0 -191
  46. package/src/__tests__/mock-rest-debug.test.ts +0 -110
  47. package/src/__tests__/mock-rest-edge.test.ts +0 -176
  48. package/src/__tests__/mock-sinon-runtime.test.ts +0 -168
  49. package/src/__tests__/narrowing-discriminated.test.ts +0 -35
  50. package/src/__tests__/narrowing-in-operator.test.ts +0 -56
  51. package/src/__tests__/narrowing-switch.test.ts +0 -56
  52. package/src/__tests__/narrowing-truthiness.test.ts +0 -52
  53. package/src/__tests__/narrowing.test.ts +0 -82
  54. package/src/__tests__/nudo-mock-syntax.test.ts +0 -245
  55. package/src/__tests__/realworld-patterns.test.ts +0 -430
  56. package/src/__tests__/syntax-sugar.test.ts +0 -345
  57. package/src/builtins/index.ts +0 -61
  58. package/tsconfig.json +0 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nudojs/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "CLI and evaluator API for the Nudo type inference engine",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,6 +20,10 @@
20
20
  "bin": {
21
21
  "nudo": "src/index.ts"
22
22
  },
23
+ "files": [
24
+ "src",
25
+ "!src/__tests__"
26
+ ],
23
27
  "repository": {
24
28
  "type": "git",
25
29
  "url": "git+https://github.com/nudojs/nudo.git",
@@ -33,13 +37,14 @@
33
37
  "registry": "https://registry.npmjs.org"
34
38
  },
35
39
  "dependencies": {
36
- "@nudojs/parser": "0.2.0",
37
- "@nudojs/service": "0.2.1",
38
- "@nudojs/core": "0.2.0",
39
- "@nudojs/env": "0.2.0",
40
- "@nudojs/harvester": "0.1.0"
40
+ "commander": "^13.0.0",
41
+ "@nudojs/core": "0.3.0",
42
+ "@nudojs/parser": "0.3.0",
43
+ "@nudojs/service": "0.3.0",
44
+ "@nudojs/env": "0.2.1",
45
+ "@nudojs/harvester": "0.2.0"
41
46
  },
42
47
  "scripts": {
43
- "build": "tsup src/index.ts src/evaluator-api.ts --format esm --dts"
48
+ "build": "tsup"
44
49
  }
45
50
  }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * TypeValue IR ⇄ Abs 代数的路由。
3
+ *
4
+ * 唯一计算路径:算术/比较/相等/一元语言表面先走 Abs;
5
+ * 仅 refined 宿主扩展(/ % 自定义 ops、方法表)与代数无法建模的操作数才回落。
6
+ */
7
+
8
+ import type { TypeValue } from "@nudojs/core";
9
+ import {
10
+ typeValueToAbs,
11
+ absToTypeValue,
12
+ simplifyUnion,
13
+ add as kAdd,
14
+ sub as kSub,
15
+ mul as kMul,
16
+ div as kDiv,
17
+ mod as kMod,
18
+ cmp as kCmp,
19
+ typeofAbs as kTypeof,
20
+ negAbs as kNeg,
21
+ notAbs as kNot,
22
+ strictEqAbs as kStrictEq,
23
+ type Abs,
24
+ type Pred,
25
+ pTrue,
26
+ and as predAnd,
27
+ ge as predGe,
28
+ le as predLe,
29
+ lit as termLit,
30
+ getRangeMeta,
31
+ spread as kSpread,
32
+ joinAbs as kJoinAbs,
33
+ abs as makeAbs,
34
+ boolLit,
35
+ currentPhi,
36
+ pushPhi,
37
+ popPhi,
38
+ resetPhi,
39
+ withPhiConstraint,
40
+ describePhi,
41
+ } from "@nudojs/core";
42
+ import { getTerm, getPred } from "./term-registry.ts";
43
+
44
+ export { currentPhi, pushPhi, popPhi, resetPhi, withPhiConstraint, describePhi };
45
+
46
+ const ARITH_OPS = new Set(["+", "-", "*", "/", "%"]);
47
+ const CMP_OPS = new Set(["<", "<=", ">", ">="]);
48
+ const EQ_OPS = new Set(["===", "!=="]);
49
+ const UNARY_OPS = new Set(["typeof", "!", "-"]);
50
+
51
+ function isNumericOperand(tv: TypeValue): boolean {
52
+ if (!tv) return false;
53
+ if (tv.kind === "literal") return typeof tv.value === "number";
54
+ if (tv.kind === "primitive") return tv.type === "number";
55
+ if (tv.kind === "refined") return isNumericOperand(tv.base);
56
+ return false;
57
+ }
58
+
59
+ function isStringOperand(tv: TypeValue): boolean {
60
+ if (!tv) return false;
61
+ if (tv.kind === "literal") return typeof tv.value === "string";
62
+ if (tv.kind === "primitive") return tv.type === "string";
63
+ if (tv.kind === "refined") return isStringOperand(tv.base);
64
+ return false;
65
+ }
66
+
67
+ /** 比较:数值或字符串字面量 */
68
+ function isComparableOperand(tv: TypeValue): boolean {
69
+ if (tv.kind === "literal") {
70
+ return typeof tv.value === "number" || typeof tv.value === "string";
71
+ }
72
+ if (tv.kind === "primitive") return tv.type === "number" || tv.type === "string";
73
+ if (tv.kind === "refined") return isComparableOperand(tv.base);
74
+ return false;
75
+ }
76
+
77
+ /**
78
+ * 宿主 refinement 自定义了该运算 → 让位。
79
+ * 仅限代数未覆盖的运算(/ %):template 的 + 等仍走代数(concatString)。
80
+ */
81
+ function hasCustomRefinementOp(tv: TypeValue, op: string): boolean {
82
+ if (op !== "/" && op !== "%") return false;
83
+ let cur: TypeValue | undefined = tv;
84
+ while (cur && cur.kind === "refined") {
85
+ if (cur.refinement.ops?.[op]) return true;
86
+ cur = cur.base;
87
+ }
88
+ return false;
89
+ }
90
+
91
+ /** `+` 可接受:双数值;或任一侧 string/template(拼接) */
92
+ function canAdd(left: TypeValue, right: TypeValue): boolean {
93
+ if (isNumericOperand(left) && isNumericOperand(right)) return true;
94
+ if (isStringOperand(left) || isStringOperand(right)) return true;
95
+ if (left.kind === "refined" && isStringOperand(left.base)) return true;
96
+ if (right.kind === "refined" && isStringOperand(right.base)) return true;
97
+ return false;
98
+ }
99
+
100
+ /**
101
+ * 带 term 旁路的 TypeValue → Abs。
102
+ * range refined 编码为 term 上的 Pred(唯一约束通道),不再靠 refinement.ops。
103
+ */
104
+ function toAbsWithTerms(tv: TypeValue): Abs {
105
+ const base = typeValueToAbs(tv);
106
+ const term = getTerm(tv);
107
+ const pred = getPred(tv);
108
+ let abs: Abs =
109
+ !term && !pred && !getRangeMeta(tv)
110
+ ? base
111
+ : makeAbs(base.shape, term ?? base.term, pred ?? base.pred, base.conf);
112
+
113
+ const range = getRangeMeta(tv);
114
+ if (range && abs.term) {
115
+ const facts: Pred[] = [];
116
+ if (range.min != null) facts.push(predGe(abs.term, termLit(range.min)));
117
+ if (range.max != null) facts.push(predLe(abs.term, termLit(range.max)));
118
+ if (facts.length > 0) {
119
+ const existing = abs.pred && abs.pred.op !== "true" ? abs.pred : undefined;
120
+ abs = makeAbs(
121
+ abs.shape,
122
+ abs.term,
123
+ existing ? predAnd(existing, ...facts) : predAnd(...facts),
124
+ abs.conf,
125
+ );
126
+ }
127
+ }
128
+ return abs;
129
+ }
130
+
131
+ /**
132
+ * 代数计算二元算术/比较/相等。
133
+ * - union:逐成员代数后 simplifyUnion(约束不丢)
134
+ * - undefined = 不可处理 → 调用方走 refined 扩展 / 形状兜底
135
+ */
136
+ export function tryAbsBinary(
137
+ op: string,
138
+ left: TypeValue,
139
+ right: TypeValue,
140
+ ): TypeValue | undefined {
141
+ if (!ARITH_OPS.has(op) && !CMP_OPS.has(op) && !EQ_OPS.has(op)) return undefined;
142
+
143
+ if (left.kind === "union" || right.kind === "union") {
144
+ const ls = left.kind === "union" ? left.members : [left];
145
+ const rs = right.kind === "union" ? right.members : [right];
146
+ const results: TypeValue[] = [];
147
+ for (const l of ls) {
148
+ for (const r of rs) {
149
+ const one = tryAbsBinary(op, l, r);
150
+ if (one === undefined) return undefined;
151
+ results.push(one);
152
+ }
153
+ }
154
+ return simplifyUnion(results);
155
+ }
156
+
157
+ if (hasCustomRefinementOp(left, op) || hasCustomRefinementOp(right, op)) {
158
+ return undefined;
159
+ }
160
+
161
+ if (op === "+") {
162
+ if (!canAdd(left, right)) return undefined;
163
+ } else if (op === "-" || op === "*" || op === "/" || op === "%") {
164
+ if (!isNumericOperand(left) || !isNumericOperand(right)) return undefined;
165
+ } else if (op === "===" || op === "!==") {
166
+ // 任意操作数:先 strictEqAbs(字面量 / nullish / 同 var)
167
+ } else {
168
+ // 比较:数值或字符串
169
+ if (!isComparableOperand(left) || !isComparableOperand(right)) return undefined;
170
+ }
171
+
172
+ try {
173
+ const la = toAbsWithTerms(left);
174
+ const ra = toAbsWithTerms(right);
175
+ const phi = currentPhi();
176
+
177
+ if (op === "===" || op === "!==") {
178
+ const eq = kStrictEq(la, ra);
179
+ if (eq !== undefined) {
180
+ return absToTypeValue(op === "===" ? boolLit(eq) : boolLit(!eq));
181
+ }
182
+ // 无法判定:数值同型走 cmp 挂 pred;否则交还调用方
183
+ if (isNumericOperand(left) && isNumericOperand(right)) {
184
+ return absToTypeValue(kCmp(op === "===" ? "eq" : "ne", la, ra, phi));
185
+ }
186
+ return undefined;
187
+ }
188
+
189
+ let result: Abs;
190
+ if (op === "+") result = kAdd(la, ra, phi);
191
+ else if (op === "-") result = kSub(la, ra, phi);
192
+ else if (op === "*") result = kMul(la, ra, phi);
193
+ else if (op === "/") result = kDiv(la, ra, phi);
194
+ else if (op === "%") result = kMod(la, ra, phi);
195
+ else if (op === "<") result = kCmp("lt", la, ra, phi);
196
+ else if (op === "<=") result = kCmp("le", la, ra, phi);
197
+ else if (op === ">") result = kCmp("gt", la, ra, phi);
198
+ else if (op === ">=") result = kCmp("ge", la, ra, phi);
199
+ else return undefined;
200
+
201
+ return absToTypeValue(result);
202
+ } catch {
203
+ return undefined;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * 一元语言表面经代数:typeof / ! / -。
209
+ * 返回 undefined 表示调用方自行处理(union 分发等)。
210
+ */
211
+ export function tryAbsUnary(
212
+ op: string,
213
+ operand: TypeValue,
214
+ ): TypeValue | undefined {
215
+ if (!UNARY_OPS.has(op)) return undefined;
216
+ if (operand.kind === "union") {
217
+ const outs: TypeValue[] = [];
218
+ for (const m of operand.members) {
219
+ const one = tryAbsUnary(op, m);
220
+ if (one === undefined) return undefined;
221
+ outs.push(one);
222
+ }
223
+ return simplifyUnion(outs);
224
+ }
225
+ try {
226
+ const a = toAbsWithTerms(operand);
227
+ if (op === "typeof") return absToTypeValue(kTypeof(a));
228
+ if (op === "!") return absToTypeValue(kNot(a));
229
+ if (op === "-") {
230
+ if (!isNumericOperand(operand) && operand.kind !== "unknown") return undefined;
231
+ return absToTypeValue(kNeg(a, currentPhi()));
232
+ }
233
+ return undefined;
234
+ } catch {
235
+ return undefined;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * 对象 spread 经代数(右侧覆盖,保留字面量)。
241
+ * 返回 undefined 表示走 Object.assign 路径。
242
+ */
243
+ export function tryAbsObjectSpread(
244
+ base: TypeValue,
245
+ over: TypeValue,
246
+ ): TypeValue | undefined {
247
+ if (base.kind !== "object" && base.kind !== "union") return undefined;
248
+ if (over.kind !== "object" && over.kind !== "union") return undefined;
249
+ try {
250
+ const ba = toAbsWithTerms(base);
251
+ const oa = toAbsWithTerms(over);
252
+ const r = kSpread(ba, oa);
253
+ return absToTypeValue(r);
254
+ } catch {
255
+ return undefined;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * 对象 join(异 key 保持 sum,不折 optional)。
261
+ */
262
+ export function tryAbsJoinObjects(
263
+ a: TypeValue,
264
+ b: TypeValue,
265
+ ): TypeValue | undefined {
266
+ if (a.kind !== "object" || b.kind !== "object") return undefined;
267
+ try {
268
+ const r = kJoinAbs(toAbsWithTerms(a), toAbsWithTerms(b));
269
+ return absToTypeValue(r);
270
+ } catch {
271
+ return undefined;
272
+ }
273
+ }
@@ -0,0 +1,279 @@
1
+ /**
2
+ * 内置类原型机制:错误类集合、可构造内置类、Object/Array/Map/Set/…/Buffer
3
+ * 的原型成员近似表,以及 `X.prototype` 单例与类命名空间记忆化。
4
+ * 求值器成员分派与补全侧成员派生共用本表的唯一真值。
5
+ */
6
+
7
+ import { T, simplifyUnion, type TypeValue } from "@nudojs/core";
8
+ import { MAP_INSTANCE_METHODS, mapEntriesIterable } from "./builtin-map.ts";
9
+ import { SET_INSTANCE_METHODS } from "./builtin-set.ts";
10
+
11
+ // hasOwnProp 守卫:普通 Record 表查找会从原型链泄漏原生函数(如 "constructor")。
12
+ // 与 evaluator 内同名 helper 同语义;独立副本避免 evaluator → 本模块循环依赖。
13
+ function hasOwnProp(props: Record<string, unknown>, name: string): boolean {
14
+ return Object.prototype.hasOwnProperty.call(props, name);
15
+ }
16
+
17
+ const BUILTIN_ERROR_CLASSES = new Set([
18
+ "Error", "TypeError", "SyntaxError", "RangeError", "ReferenceError", "URIError", "EvalError",
19
+ ]);
20
+ // Constructible built-in classes. A bare reference to one of these names
21
+ // resolves to a namespace object (like the BUILTIN_STATIC_METHODS entries),
22
+ // and `X.prototype` evaluates to an instance of X instead of degrading to
23
+ // undefined/unknown.
24
+ const BUILTIN_PROTOTYPE_CLASSES = new Set([
25
+ ...BUILTIN_ERROR_CLASSES,
26
+ "Date", "Object", "Map", "Set", "Promise", "RegExp", "Array", "Function",
27
+ "String", "Number", "Boolean", "Symbol", "WeakMap", "WeakSet", "Buffer",
28
+ ]);
29
+ // Object.prototype members. Real property access reads through the
30
+ // prototype chain, so every object-typed receiver materializes these —
31
+ // destructuring `const { hasOwnProperty } = obj` yields a function (not
32
+ // undefined) and `Object.prototype.hasOwnProperty` types as boolean.
33
+ // Lookup must be own-property guarded: a plain `{}` record would otherwise
34
+ // leak native JS functions (e.g. for "constructor") into the type system.
35
+ //
36
+ // toString/valueOf carry impls so the receiver (thisVal) shapes the result:
37
+ // `Object.prototype.toString.call(x)` yields the brand literal
38
+ // ('[object Map]', '[object Null]', ...) that Map-based type dispatch
39
+ // (hoek internals.typeMap) keys on; valueOf returns its receiver.
40
+ const OBJECT_PROTOTYPE_METHODS: Record<string, TypeValue> = {
41
+ hasOwnProperty: T.fnSig([T.unknown], T.boolean),
42
+ isPrototypeOf: T.fnSig([T.unknown], T.boolean),
43
+ propertyIsEnumerable: T.fnSig([T.unknown], T.boolean),
44
+ toString: T.fnSig([], T.string, T.never, (_args, thisVal) => objectToStringBrand(thisVal)),
45
+ toLocaleString: T.fnSig([], T.string),
46
+ valueOf: T.fnSig([], T.unknown, T.never, (_args, thisVal) => thisVal),
47
+ };
48
+ // Common prototype members approximated as unknown-result signatures
49
+ // (mirrors the knownInstanceMethods fallback pattern). `X.prototype.m`
50
+ // stays a callable function value instead of degrading to undefined.
51
+ const BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS: Record<string, Record<string, TypeValue>> = {
52
+ Object: { ...OBJECT_PROTOTYPE_METHODS },
53
+ Array: {
54
+ push: T.fnSig([T.unknown], T.number),
55
+ pop: T.fnSig([], T.unknown),
56
+ shift: T.fnSig([], T.unknown),
57
+ unshift: T.fnSig([T.unknown], T.number),
58
+ slice: T.fnSig([T.number, T.number], T.array(T.unknown)),
59
+ splice: T.fnSig([T.number, T.number], T.array(T.unknown)),
60
+ concat: T.fnSig([T.unknown], T.array(T.unknown)),
61
+ join: T.fnSig([T.string], T.string),
62
+ indexOf: T.fnSig([T.unknown], T.number),
63
+ lastIndexOf: T.fnSig([T.unknown], T.number),
64
+ includes: T.fnSig([T.unknown], T.boolean),
65
+ map: T.fnSig([T.unknown], T.array(T.unknown)),
66
+ flatMap: T.fnSig([T.unknown], T.array(T.unknown)),
67
+ filter: T.fnSig([T.unknown], T.array(T.unknown)),
68
+ forEach: T.fnSig([T.unknown], T.undefined),
69
+ find: T.fnSig([T.unknown], T.unknown),
70
+ findIndex: T.fnSig([T.unknown], T.number),
71
+ some: T.fnSig([T.unknown], T.boolean),
72
+ every: T.fnSig([T.unknown], T.boolean),
73
+ reduce: T.fnSig([T.unknown, T.unknown], T.unknown),
74
+ sort: T.fnSig([T.unknown], T.array(T.unknown)),
75
+ reverse: T.fnSig([], T.array(T.unknown)),
76
+ toString: T.fnSig([], T.string),
77
+ },
78
+ Function: {
79
+ call: T.fnSig([T.unknown], T.unknown),
80
+ apply: T.fnSig([T.unknown, T.unknown], T.unknown),
81
+ bind: T.fnSig([T.unknown], T.unknown),
82
+ toString: T.fnSig([], T.string),
83
+ },
84
+ Map: {
85
+ // Impl-routed: `Map.prototype.m.call(instance)` (and direct prototype
86
+ // calls) consult the receiver's exact entry side table, making the
87
+ // hoek deepEqual reflection idioms decide literally.
88
+ get: T.fnSig([T.unknown], T.unknown, T.never, (args, thisVal) => MAP_INSTANCE_METHODS.get(args[0] ?? T.unknown, thisVal ?? T.unknown)),
89
+ set: T.fnSig([T.unknown, T.unknown], T.unknown, T.never, (args, thisVal) => MAP_INSTANCE_METHODS.set(args[0] ?? T.unknown, args[1] ?? T.unknown, thisVal ?? T.unknown)),
90
+ has: T.fnSig([T.unknown], T.boolean, T.never, (args, thisVal) => MAP_INSTANCE_METHODS.has(args[0] ?? T.unknown, thisVal ?? T.unknown)),
91
+ delete: T.fnSig([T.unknown], T.boolean, T.never, (args, thisVal) => MAP_INSTANCE_METHODS.delete(args[0] ?? T.unknown, thisVal ?? T.unknown)),
92
+ clear: T.fnSig([], T.undefined, T.never, (args, thisVal) => MAP_INSTANCE_METHODS.clear(args[0] ?? T.unknown, thisVal ?? T.unknown)),
93
+ forEach: T.fnSig([T.unknown], T.undefined),
94
+ keys: T.fnSig([], T.array(T.unknown), T.never, (_args, thisVal) => MAP_INSTANCE_METHODS.keys(thisVal ?? T.unknown)),
95
+ values: T.fnSig([], T.array(T.unknown), T.never, (_args, thisVal) => MAP_INSTANCE_METHODS.values(thisVal ?? T.unknown)),
96
+ entries: T.fnSig([], T.array(T.tuple([T.unknown, T.unknown])), T.never, (_args, thisVal) => mapEntriesIterable(thisVal ?? T.unknown)),
97
+ toString: T.fnSig([], T.string),
98
+ },
99
+ Set: {
100
+ // Impl-routed like Map above (Set.prototype.values.call(s) ≡ s.values()).
101
+ add: T.fnSig([T.unknown], T.unknown, T.never, (args, thisVal) => SET_INSTANCE_METHODS.add(args[0] ?? T.unknown, thisVal ?? T.unknown)),
102
+ has: T.fnSig([T.unknown], T.boolean, T.never, (args, thisVal) => SET_INSTANCE_METHODS.has(args[0] ?? T.unknown, thisVal ?? T.unknown)),
103
+ delete: T.fnSig([T.unknown], T.boolean, T.never, (args, thisVal) => SET_INSTANCE_METHODS.delete(args[0] ?? T.unknown, thisVal ?? T.unknown)),
104
+ clear: T.fnSig([], T.undefined, T.never, (args, thisVal) => SET_INSTANCE_METHODS.clear(args[0] ?? T.unknown, thisVal ?? T.unknown)),
105
+ forEach: T.fnSig([T.unknown], T.undefined),
106
+ keys: T.fnSig([], T.array(T.unknown), T.never, (_args, thisVal) => SET_INSTANCE_METHODS.keys(thisVal ?? T.unknown)),
107
+ values: T.fnSig([], T.array(T.unknown), T.never, (_args, thisVal) => SET_INSTANCE_METHODS.values(thisVal ?? T.unknown)),
108
+ entries: T.fnSig([], T.array(T.tuple([T.unknown, T.unknown])), T.never, (_args, thisVal) => SET_INSTANCE_METHODS.entries(thisVal ?? T.unknown)),
109
+ toString: T.fnSig([], T.string),
110
+ },
111
+ WeakMap: {
112
+ get: T.fnSig([T.unknown], T.unknown),
113
+ set: T.fnSig([T.unknown, T.unknown], T.unknown),
114
+ has: T.fnSig([T.unknown], T.boolean),
115
+ delete: T.fnSig([T.unknown], T.boolean),
116
+ toString: T.fnSig([], T.string),
117
+ },
118
+ WeakSet: {
119
+ add: T.fnSig([T.unknown], T.unknown),
120
+ has: T.fnSig([T.unknown], T.boolean),
121
+ delete: T.fnSig([T.unknown], T.boolean),
122
+ toString: T.fnSig([], T.string),
123
+ },
124
+ Promise: {
125
+ then: T.fnSig([T.unknown], T.promise(T.unknown)),
126
+ catch: T.fnSig([T.unknown], T.promise(T.unknown)),
127
+ finally: T.fnSig([T.unknown], T.promise(T.unknown)),
128
+ toString: T.fnSig([], T.string),
129
+ },
130
+ Date: {
131
+ getTime: T.fnSig([], T.number),
132
+ valueOf: T.fnSig([], T.number),
133
+ toISOString: T.fnSig([], T.string),
134
+ toJSON: T.fnSig([], T.string),
135
+ toLocaleString: T.fnSig([], T.string),
136
+ toString: T.fnSig([], T.string),
137
+ },
138
+ RegExp: {
139
+ test: T.fnSig([T.string], T.boolean),
140
+ exec: T.fnSig([T.string], T.union(T.object({}), T.null)),
141
+ toString: T.fnSig([], T.string),
142
+ },
143
+ String: {
144
+ charAt: T.fnSig([T.number], T.string),
145
+ charCodeAt: T.fnSig([T.number], T.number),
146
+ indexOf: T.fnSig([T.string], T.number),
147
+ lastIndexOf: T.fnSig([T.string], T.number),
148
+ includes: T.fnSig([T.string], T.boolean),
149
+ startsWith: T.fnSig([T.string], T.boolean),
150
+ endsWith: T.fnSig([T.string], T.boolean),
151
+ slice: T.fnSig([T.number, T.number], T.string),
152
+ substring: T.fnSig([T.number, T.number], T.string),
153
+ toUpperCase: T.fnSig([], T.string),
154
+ toLowerCase: T.fnSig([], T.string),
155
+ trim: T.fnSig([], T.string),
156
+ replace: T.fnSig([T.unknown, T.string], T.string),
157
+ split: T.fnSig([T.string], T.array(T.string)),
158
+ toString: T.fnSig([], T.string),
159
+ valueOf: T.fnSig([], T.string),
160
+ },
161
+ Number: {
162
+ toFixed: T.fnSig([T.number], T.string),
163
+ toPrecision: T.fnSig([T.number], T.string),
164
+ valueOf: T.fnSig([], T.number),
165
+ toString: T.fnSig([T.number], T.string),
166
+ },
167
+ Boolean: {
168
+ valueOf: T.fnSig([], T.boolean),
169
+ toString: T.fnSig([], T.string),
170
+ },
171
+ Symbol: {
172
+ toString: T.fnSig([], T.string),
173
+ valueOf: T.fnSig([], T.symbol),
174
+ },
175
+ Buffer: {
176
+ equals: T.fnSig([T.unknown], T.boolean),
177
+ compare: T.fnSig([T.unknown], T.number),
178
+ toString: T.fnSig([T.unknown], T.string),
179
+ toJSON: T.fnSig([], T.unknown),
180
+ },
181
+ Error: {
182
+ toString: T.fnSig([], T.string),
183
+ },
184
+ };
185
+ // Memoized namespace values for built-in classes. Reference-stable so
186
+ // `'x'.constructor === String` compares identical objects (typeValueEquals
187
+ // falls back to reference equality for object kinds).
188
+ const _builtinClassValues = new Map<string, TypeValue>();
189
+ function builtinClassValue(name: string): TypeValue {
190
+ let v = _builtinClassValues.get(name);
191
+ if (v === undefined) {
192
+ v = T.object({});
193
+ (v as any)._builtinName = name;
194
+ _builtinClassValues.set(name, v);
195
+ }
196
+ return v;
197
+ }
198
+ // Cached `X.prototype` singletons. hoek-style modules assign
199
+ // `exports.array = Array.prototype` and later compare
200
+ // `baseProto === Types.buffer`: strict-equality on instances only stays
201
+ // precise when every evaluation of `X.prototype` yields the same TypeValue.
202
+ const BUILTIN_PROTOTYPE_SINGLETONS = new Map<string, TypeValue>();
203
+
204
+ function builtinPrototype(className: string): TypeValue {
205
+ let proto = BUILTIN_PROTOTYPE_SINGLETONS.get(className);
206
+ if (!proto) {
207
+ const methods = hasOwnProp(BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS, className)
208
+ ? BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS[className]
209
+ : BUILTIN_ERROR_CLASSES.has(className)
210
+ ? BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS.Error
211
+ : {};
212
+ proto = T.instanceOf(className, { ...methods });
213
+ (proto as any)._builtinProto = className;
214
+ BUILTIN_PROTOTYPE_SINGLETONS.set(className, proto);
215
+ }
216
+ return proto;
217
+ }
218
+ // `Object.prototype.toString` brand string ('[object Map]', '[object Null]',
219
+ // ...). Returns undefined when the receiver has no representable brand, so
220
+ // the fnSig falls back to its plain `string` return type.
221
+ function objectToStringBrand(v: TypeValue | undefined): TypeValue | undefined {
222
+ if (!v) return undefined;
223
+ if (v.kind === "union") {
224
+ const parts = v.members.map((m) => objectToStringBrand(m));
225
+ if (parts.some((p) => p === undefined)) return undefined;
226
+ return simplifyUnion(parts as TypeValue[]);
227
+ }
228
+ let base: TypeValue = v;
229
+ while (base.kind === "refined") base = base.base;
230
+ switch (base.kind) {
231
+ case "object": return T.literal("[object Object]");
232
+ case "array":
233
+ case "tuple": return T.literal("[object Array]");
234
+ case "function": return T.literal("[object Function]");
235
+ case "promise": return T.literal("[object Promise]");
236
+ case "instance": return T.literal(`[object ${base.className}]`);
237
+ case "literal": {
238
+ if (base.value === null) return T.literal("[object Null]");
239
+ if (base.value === undefined) return T.literal("[object Undefined]");
240
+ const t = typeof base.value;
241
+ return T.literal(`[object ${t === "number" ? "Number" : t === "string" ? "String" : "Boolean"}]`);
242
+ }
243
+ case "primitive": {
244
+ const brands: Record<string, string> = {
245
+ number: "Number", string: "String", boolean: "Boolean", bigint: "BigInt", symbol: "Symbol",
246
+ };
247
+ return T.literal(`[object ${brands[base.type] ?? "Object"}]`);
248
+ }
249
+ default: return undefined;
250
+ }
251
+ }
252
+ // Object.getPrototypeOf: map a receiver onto its class's cached prototype
253
+ // singleton (plain objects → Object.prototype, arrays → Array.prototype,
254
+ // instances → their class's prototype). Unrepresentable receivers degrade
255
+ // to unknown.
256
+ function protoOfValue(v: TypeValue | undefined): TypeValue {
257
+ if (!v) return T.unknown;
258
+ if (v.kind === "union") return simplifyUnion(v.members.map((m) => protoOfValue(m)));
259
+ let base: TypeValue = v;
260
+ while (base.kind === "refined") base = base.base;
261
+ switch (base.kind) {
262
+ case "object": return builtinPrototype("Object");
263
+ case "array":
264
+ case "tuple": return builtinPrototype("Array");
265
+ case "function": return builtinPrototype("Function");
266
+ case "promise": return builtinPrototype("Promise");
267
+ case "instance": return builtinPrototype(base.className);
268
+ default: return T.unknown;
269
+ }
270
+ }
271
+ export {
272
+ BUILTIN_ERROR_CLASSES,
273
+ BUILTIN_PROTOTYPE_CLASSES,
274
+ OBJECT_PROTOTYPE_METHODS,
275
+ BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS,
276
+ builtinClassValue,
277
+ builtinPrototype,
278
+ protoOfValue,
279
+ };