@nudojs/core 0.2.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/package.json CHANGED
@@ -1,22 +1,51 @@
1
1
  {
2
2
  "name": "@nudojs/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
+ "description": "Nudo type system: type-as-computation (Abs) with TypeValue evaluation IR",
4
5
  "type": "module",
5
- "main": "src/index.ts",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "nudo",
9
+ "type-inference",
10
+ "javascript",
11
+ "abstract-interpretation",
12
+ "type-system"
13
+ ],
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
6
16
  "exports": {
7
- ".": "./src/index.ts"
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
+ }
8
25
  },
26
+ "files": [
27
+ "dist"
28
+ ],
9
29
  "repository": {
10
30
  "type": "git",
11
31
  "url": "git+https://github.com/nudojs/nudo.git",
12
32
  "directory": "packages/core"
13
33
  },
34
+ "homepage": "https://github.com/nudojs/nudo/tree/main/packages/core",
35
+ "bugs": "https://github.com/nudojs/nudo/issues",
14
36
  "publishConfig": {
15
37
  "access": "public",
16
38
  "provenance": true,
17
39
  "registry": "https://registry.npmjs.org"
18
40
  },
41
+ "dependencies": {
42
+ "@babel/parser": "^7.29.0",
43
+ "@babel/types": "^7.29.0"
44
+ },
45
+ "devDependencies": {
46
+ "commander": "^13.0.0"
47
+ },
19
48
  "scripts": {
20
- "build": "tsup src/index.ts --format esm --dts"
49
+ "build": "tsup"
21
50
  }
22
51
  }
package/CHANGELOG.md DELETED
@@ -1,15 +0,0 @@
1
- # @nudojs/core
2
-
3
- ## 0.2.0
4
-
5
- ### Minor Changes
6
-
7
- - 6c38283: docs and ci
8
- - 9f7f819: fix pkg info
9
- - c175f71: version
10
-
11
- ## 0.1.0
12
-
13
- ### Minor Changes
14
-
15
- - 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,194 +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
- const result = dispatchBinaryOp("%", odd, T.literal(3));
42
- expect(result.kind).toBe("primitive");
43
- });
44
- });
45
-
46
- describe("template string via add", () => {
47
- it("literal string + T.string produces template", () => {
48
- const result = dispatchBinaryOp("+", T.literal("xy"), T.string);
49
- expect(isTemplate(result)).toBe(true);
50
- });
51
-
52
- it("T.string + literal string produces template", () => {
53
- const result = dispatchBinaryOp("+", T.string, T.literal("!"));
54
- expect(isTemplate(result)).toBe(true);
55
- });
56
-
57
- it("template + template concatenates", () => {
58
- const left = createTemplate([T.literal("a"), T.string]);
59
- const right = createTemplate([T.literal("b"), T.string]);
60
- const result = dispatchBinaryOp("+", left, right);
61
- expect(isTemplate(result)).toBe(true);
62
- });
63
-
64
- it("two literal strings still produce literal", () => {
65
- const result = dispatchBinaryOp("+", T.literal("a"), T.literal("b"));
66
- expect(typeValueEquals(result, T.literal("ab"))).toBe(true);
67
- });
68
-
69
- it("T.string + T.string stays T.string", () => {
70
- const result = dispatchBinaryOp("+", T.string, T.string);
71
- expect(typeValueEquals(result, T.string)).toBe(true);
72
- });
73
- });
74
-
75
- describe("range comparison ops", () => {
76
- it("range(min:0) >= 0 is true", () => {
77
- const r = createRange({ min: 0 });
78
- const result = dispatchBinaryOp(">=", r, T.literal(0));
79
- expect(typeValueEquals(result, T.literal(true))).toBe(true);
80
- });
81
-
82
- it("range(min:0) >= -1 is true", () => {
83
- const r = createRange({ min: 0 });
84
- const result = dispatchBinaryOp(">=", r, T.literal(-1));
85
- expect(typeValueEquals(result, T.literal(true))).toBe(true);
86
- });
87
-
88
- it("range(max:100) > 200 is false", () => {
89
- const r = createRange({ max: 100 });
90
- const result = dispatchBinaryOp(">", r, T.literal(200));
91
- expect(typeValueEquals(result, T.literal(false))).toBe(true);
92
- });
93
-
94
- it("range(min:0, max:100) > 50 is uncertain (boolean)", () => {
95
- const r = createRange({ min: 0, max: 100 });
96
- const result = dispatchBinaryOp(">", r, T.literal(50));
97
- expect(typeValueEquals(result, T.boolean)).toBe(true);
98
- });
99
-
100
- it("range(max:10) <= 10 is true", () => {
101
- const r = createRange({ max: 10 });
102
- const result = dispatchBinaryOp("<=", r, T.literal(10));
103
- expect(typeValueEquals(result, T.literal(true))).toBe(true);
104
- });
105
-
106
- it("range(min:5) < 3 is false", () => {
107
- const r = createRange({ min: 5 });
108
- const result = dispatchBinaryOp("<", r, T.literal(3));
109
- expect(typeValueEquals(result, T.literal(false))).toBe(true);
110
- });
111
- });
112
- });
113
-
114
- describe("dispatchMethod", () => {
115
- describe("template startsWith", () => {
116
- it("known prefix matches", () => {
117
- const tmpl = createTemplate([T.literal("xy"), T.string]);
118
- const result = dispatchMethod(tmpl, "startsWith", [T.literal("x")]);
119
- expect(result).toBeDefined();
120
- expect(typeValueEquals(result!, T.literal(true))).toBe(true);
121
- });
122
-
123
- it("known prefix full match", () => {
124
- const tmpl = createTemplate([T.literal("xy"), T.string]);
125
- const result = dispatchMethod(tmpl, "startsWith", [T.literal("xy")]);
126
- expect(typeValueEquals(result!, T.literal(true))).toBe(true);
127
- });
128
-
129
- it("known prefix mismatch", () => {
130
- const tmpl = createTemplate([T.literal("xy"), T.string]);
131
- const result = dispatchMethod(tmpl, "startsWith", [T.literal("a")]);
132
- expect(typeValueEquals(result!, T.literal(false))).toBe(true);
133
- });
134
-
135
- it("uncertain when search extends beyond prefix", () => {
136
- const tmpl = createTemplate([T.literal("xy"), T.string]);
137
- const result = dispatchMethod(tmpl, "startsWith", [T.literal("xyz")]);
138
- expect(result).toBeUndefined();
139
- });
140
- });
141
-
142
- describe("template endsWith", () => {
143
- it("known suffix matches", () => {
144
- const tmpl = createTemplate([T.string, T.literal("!")]);
145
- const result = dispatchMethod(tmpl, "endsWith", [T.literal("!")]);
146
- expect(typeValueEquals(result!, T.literal(true))).toBe(true);
147
- });
148
-
149
- it("known suffix mismatch", () => {
150
- const tmpl = createTemplate([T.string, T.literal("!")]);
151
- const result = dispatchMethod(tmpl, "endsWith", [T.literal("?")]);
152
- expect(typeValueEquals(result!, T.literal(false))).toBe(true);
153
- });
154
- });
155
-
156
- describe("template includes", () => {
157
- it("known fixed text includes substring", () => {
158
- const tmpl = createTemplate([T.literal("hello"), T.string, T.literal("world")]);
159
- const result = dispatchMethod(tmpl, "includes", [T.literal("hello")]);
160
- expect(typeValueEquals(result!, T.literal(true))).toBe(true);
161
- });
162
-
163
- it("uncertain when not in fixed text", () => {
164
- const tmpl = createTemplate([T.literal("hello"), T.string]);
165
- const result = dispatchMethod(tmpl, "includes", [T.literal("xyz")]);
166
- expect(result).toBeUndefined();
167
- });
168
- });
169
-
170
- it("returns undefined for unknown methods", () => {
171
- const tmpl = createTemplate([T.literal("x"), T.string]);
172
- const result = dispatchMethod(tmpl, "unknownMethod", []);
173
- expect(result).toBeUndefined();
174
- });
175
-
176
- it("returns undefined for non-refined types", () => {
177
- const result = dispatchMethod(T.string, "startsWith", [T.literal("x")]);
178
- expect(result).toBeUndefined();
179
- });
180
- });
181
-
182
- describe("dispatchProperty", () => {
183
- it("template length returns range", () => {
184
- const tmpl = createTemplate([T.literal("xy"), T.string]);
185
- const result = dispatchProperty(tmpl, "length");
186
- expect(result).toBeDefined();
187
- expect(result!.kind).toBe("refined");
188
- });
189
-
190
- it("returns undefined for non-refined types", () => {
191
- const result = dispatchProperty(T.string, "length");
192
- expect(result).toBeUndefined();
193
- });
194
- });
@@ -1,109 +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
- describe("Ops.sub / mul / div / mod", () => {
25
- it("computes literal arithmetic", () => {
26
- expect(typeValueEquals(Ops.sub(T.literal(5), T.literal(3)), T.literal(2))).toBe(true);
27
- expect(typeValueEquals(Ops.mul(T.literal(3), T.literal(4)), T.literal(12))).toBe(true);
28
- expect(typeValueEquals(Ops.div(T.literal(10), T.literal(2)), T.literal(5))).toBe(true);
29
- expect(typeValueEquals(Ops.mod(T.literal(7), T.literal(3)), T.literal(1))).toBe(true);
30
- });
31
-
32
- it("returns number for abstract operands", () => {
33
- expect(typeValueEquals(Ops.sub(T.number, T.number), T.number)).toBe(true);
34
- expect(typeValueEquals(Ops.mul(T.number, T.literal(2)), T.number)).toBe(true);
35
- });
36
- });
37
-
38
- describe("Ops.strictEq / strictNeq", () => {
39
- it("compares literals", () => {
40
- expect(typeValueEquals(Ops.strictEq(T.literal(1), T.literal(1)), T.literal(true))).toBe(true);
41
- expect(typeValueEquals(Ops.strictEq(T.literal(1), T.literal(2)), T.literal(false))).toBe(true);
42
- expect(typeValueEquals(Ops.strictNeq(T.literal(1), T.literal(2)), T.literal(true))).toBe(true);
43
- });
44
-
45
- it("returns boolean for abstract operands", () => {
46
- expect(typeValueEquals(Ops.strictEq(T.number, T.number), T.boolean)).toBe(true);
47
- });
48
- });
49
-
50
- describe("Ops.gt / lt / gte / lte", () => {
51
- it("compares literal numbers", () => {
52
- expect(typeValueEquals(Ops.gt(T.literal(3), T.literal(2)), T.literal(true))).toBe(true);
53
- expect(typeValueEquals(Ops.lt(T.literal(1), T.literal(2)), T.literal(true))).toBe(true);
54
- expect(typeValueEquals(Ops.gte(T.literal(2), T.literal(2)), T.literal(true))).toBe(true);
55
- expect(typeValueEquals(Ops.lte(T.literal(3), T.literal(2)), T.literal(false))).toBe(true);
56
- });
57
-
58
- it("returns boolean for abstract operands", () => {
59
- expect(typeValueEquals(Ops.gt(T.number, T.number), T.boolean)).toBe(true);
60
- });
61
- });
62
-
63
- describe("Ops.typeof_", () => {
64
- it("returns literal typeof for known types", () => {
65
- expect(typeValueEquals(Ops.typeof_(T.literal(1)), T.literal("number"))).toBe(true);
66
- expect(typeValueEquals(Ops.typeof_(T.literal("hi")), T.literal("string"))).toBe(true);
67
- expect(typeValueEquals(Ops.typeof_(T.literal(null)), T.literal("object"))).toBe(true);
68
- expect(typeValueEquals(Ops.typeof_(T.number), T.literal("number"))).toBe(true);
69
- expect(typeValueEquals(Ops.typeof_(T.object({})), T.literal("object"))).toBe(true);
70
- });
71
- });
72
-
73
- describe("Ops.not", () => {
74
- it("negates literal booleans", () => {
75
- expect(typeValueEquals(Ops.not(T.literal(true)), T.literal(false))).toBe(true);
76
- expect(typeValueEquals(Ops.not(T.literal(false)), T.literal(true))).toBe(true);
77
- });
78
-
79
- it("negates truthy/falsy literals", () => {
80
- expect(typeValueEquals(Ops.not(T.literal(0)), T.literal(true))).toBe(true);
81
- expect(typeValueEquals(Ops.not(T.literal("")), T.literal(true))).toBe(true);
82
- expect(typeValueEquals(Ops.not(T.literal(1)), T.literal(false))).toBe(true);
83
- });
84
-
85
- it("returns boolean for abstract types", () => {
86
- expect(typeValueEquals(Ops.not(T.number), T.boolean)).toBe(true);
87
- });
88
- });
89
-
90
- describe("Ops.neg", () => {
91
- it("negates literal number", () => {
92
- expect(typeValueEquals(Ops.neg(T.literal(5)), T.literal(-5))).toBe(true);
93
- });
94
-
95
- it("returns number for abstract", () => {
96
- expect(typeValueEquals(Ops.neg(T.number), T.number)).toBe(true);
97
- });
98
- });
99
-
100
- describe("applyBinaryOp", () => {
101
- it("dispatches to correct op", () => {
102
- expect(typeValueEquals(applyBinaryOp("+", T.literal(1), T.literal(2)), T.literal(3))).toBe(true);
103
- expect(typeValueEquals(applyBinaryOp("===", T.literal(1), T.literal(1)), T.literal(true))).toBe(true);
104
- });
105
-
106
- it("returns unknown for unsupported op", () => {
107
- expect(applyBinaryOp("**", T.number, T.number)).toEqual(T.unknown);
108
- });
109
- });
@@ -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
- });