@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.
@@ -1,210 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import {
3
- T,
4
- typeValueEquals,
5
- isSubtypeOf,
6
- typeValueToString,
7
- deepCloneTypeValue,
8
- getPrimitiveTypeOf,
9
- getRefinedBase,
10
- } from "../type-value.ts";
11
- import { createTemplate, isTemplate, getTemplateParts, getKnownPrefix, getKnownSuffix } from "../refinements/template.ts";
12
- import { createRange, isRange, getRangeMeta } from "../refinements/range.ts";
13
-
14
- describe("Refined TypeValue", () => {
15
- describe("T.refine", () => {
16
- it("creates a refined type", () => {
17
- const odd = T.refine(T.number, { name: "odd", meta: {} });
18
- expect(odd.kind).toBe("refined");
19
- if (odd.kind === "refined") {
20
- expect(odd.base).toBe(T.number);
21
- expect(odd.refinement.name).toBe("odd");
22
- }
23
- });
24
- });
25
-
26
- describe("typeValueEquals", () => {
27
- it("equal refined types with same name and base", () => {
28
- const a = T.refine(T.number, { name: "odd", meta: {} });
29
- const b = T.refine(T.number, { name: "odd", meta: {} });
30
- expect(typeValueEquals(a, b)).toBe(true);
31
- });
32
-
33
- it("different name means not equal", () => {
34
- const a = T.refine(T.number, { name: "odd", meta: {} });
35
- const b = T.refine(T.number, { name: "even", meta: {} });
36
- expect(typeValueEquals(a, b)).toBe(false);
37
- });
38
-
39
- it("different base means not equal", () => {
40
- const a = T.refine(T.number, { name: "x", meta: {} });
41
- const b = T.refine(T.string, { name: "x", meta: {} });
42
- expect(typeValueEquals(a, b)).toBe(false);
43
- });
44
- });
45
-
46
- describe("isSubtypeOf", () => {
47
- it("refined is subtype of its base", () => {
48
- const odd = T.refine(T.number, { name: "odd", meta: {} });
49
- expect(isSubtypeOf(odd, T.number)).toBe(true);
50
- });
51
-
52
- it("refined is subtype of unknown", () => {
53
- const odd = T.refine(T.number, { name: "odd", meta: {} });
54
- expect(isSubtypeOf(odd, T.unknown)).toBe(true);
55
- });
56
-
57
- it("literal is subtype of refined when check passes", () => {
58
- const odd = T.refine(T.number, {
59
- name: "odd",
60
- meta: {},
61
- check: (v) => typeof v === "number" && v % 2 !== 0,
62
- });
63
- expect(isSubtypeOf(T.literal(3), odd)).toBe(true);
64
- expect(isSubtypeOf(T.literal(4), odd)).toBe(false);
65
- });
66
-
67
- it("nested refined is subtype of outer base", () => {
68
- const integer = T.refine(T.number, { name: "integer", meta: {} });
69
- const positiveInt = T.refine(integer, { name: "positive integer", meta: {} });
70
- expect(isSubtypeOf(positiveInt, T.number)).toBe(true);
71
- expect(isSubtypeOf(positiveInt, integer)).toBe(true);
72
- });
73
- });
74
-
75
- describe("typeValueToString", () => {
76
- it("uses refinement name", () => {
77
- const odd = T.refine(T.number, { name: "odd", meta: {} });
78
- expect(typeValueToString(odd)).toBe("odd");
79
- });
80
- });
81
-
82
- describe("getPrimitiveTypeOf", () => {
83
- it("delegates to base", () => {
84
- const odd = T.refine(T.number, { name: "odd", meta: {} });
85
- expect(getPrimitiveTypeOf(odd)).toBe("number");
86
- });
87
-
88
- it("handles nested refined", () => {
89
- const inner = T.refine(T.string, { name: "inner", meta: {} });
90
- const outer = T.refine(inner, { name: "outer", meta: {} });
91
- expect(getPrimitiveTypeOf(outer)).toBe("string");
92
- });
93
- });
94
-
95
- describe("getRefinedBase", () => {
96
- it("unwraps to primitive", () => {
97
- const inner = T.refine(T.number, { name: "a", meta: {} });
98
- const outer = T.refine(inner, { name: "b", meta: {} });
99
- expect(getRefinedBase(outer)).toBe(T.number);
100
- });
101
-
102
- it("returns non-refined as-is", () => {
103
- expect(getRefinedBase(T.string)).toBe(T.string);
104
- });
105
- });
106
-
107
- describe("deepCloneTypeValue", () => {
108
- it("clones refined type", () => {
109
- const odd = T.refine(T.number, { name: "odd", meta: { x: 1 } });
110
- const cloned = deepCloneTypeValue(odd);
111
- expect(typeValueEquals(cloned, odd)).toBe(true);
112
- expect(cloned).not.toBe(odd);
113
- });
114
- });
115
- });
116
-
117
- describe("Template String", () => {
118
- it("creates template from parts", () => {
119
- const tmpl = createTemplate([T.literal("xy"), T.string]);
120
- expect(isTemplate(tmpl)).toBe(true);
121
- expect(tmpl.kind).toBe("refined");
122
- });
123
-
124
- it("collapses to literal when all parts are literal", () => {
125
- const tmpl = createTemplate([T.literal("hello"), T.literal(" world")]);
126
- expect(tmpl.kind).toBe("literal");
127
- if (tmpl.kind === "literal") expect(tmpl.value).toBe("hello world");
128
- });
129
-
130
- it("collapses to T.string when only T.string", () => {
131
- const tmpl = createTemplate([T.string]);
132
- expect(tmpl.kind).toBe("primitive");
133
- });
134
-
135
- it("getKnownPrefix extracts leading literals", () => {
136
- expect(getKnownPrefix([T.literal("xy"), T.string])).toBe("xy");
137
- expect(getKnownPrefix([T.string, T.literal("z")])).toBe("");
138
- expect(getKnownPrefix([T.literal("a"), T.literal("b"), T.string])).toBe("ab");
139
- });
140
-
141
- it("getKnownSuffix extracts trailing literals", () => {
142
- expect(getKnownSuffix([T.string, T.literal("!")])).toBe("!");
143
- expect(getKnownSuffix([T.literal("x"), T.string])).toBe("");
144
- });
145
-
146
- it("getTemplateParts returns parts", () => {
147
- const tmpl = createTemplate([T.literal("x"), T.string]);
148
- const parts = getTemplateParts(tmpl);
149
- expect(parts).toHaveLength(2);
150
- });
151
-
152
- it("toString formats as template literal", () => {
153
- const tmpl = createTemplate([T.literal("xy"), T.string]);
154
- expect(typeValueToString(tmpl)).toBe("`xy${string}`");
155
- });
156
-
157
- it("is subtype of T.string", () => {
158
- const tmpl = createTemplate([T.literal("x"), T.string]);
159
- expect(isSubtypeOf(tmpl, T.string)).toBe(true);
160
- });
161
-
162
- it("check validates concrete strings", () => {
163
- const tmpl = createTemplate([T.literal("xy"), T.string]);
164
- if (tmpl.kind === "refined" && tmpl.refinement.check) {
165
- expect(tmpl.refinement.check("xyhello")).toBe(true);
166
- expect(tmpl.refinement.check("xy")).toBe(true);
167
- expect(tmpl.refinement.check("ahello")).toBe(false);
168
- }
169
- });
170
- });
171
-
172
- describe("Range", () => {
173
- it("creates range type", () => {
174
- const r = createRange({ min: 0 });
175
- expect(isRange(r)).toBe(true);
176
- expect(r.kind).toBe("refined");
177
- });
178
-
179
- it("collapses to literal when min === max", () => {
180
- const r = createRange({ min: 5, max: 5 });
181
- expect(r.kind).toBe("literal");
182
- if (r.kind === "literal") expect(r.value).toBe(5);
183
- });
184
-
185
- it("getRangeMeta returns metadata", () => {
186
- const r = createRange({ min: 0, max: 100, integer: true });
187
- const meta = getRangeMeta(r);
188
- expect(meta).toEqual({ min: 0, max: 100, integer: true });
189
- });
190
-
191
- it("is subtype of T.number", () => {
192
- const r = createRange({ min: 0 });
193
- expect(isSubtypeOf(r, T.number)).toBe(true);
194
- });
195
-
196
- it("toString formats with constraints", () => {
197
- expect(typeValueToString(createRange({ min: 0 }))).toBe("number (>= 0)");
198
- expect(typeValueToString(createRange({ min: 0, max: 100 }))).toBe("number (>= 0, <= 100)");
199
- expect(typeValueToString(createRange({ integer: true }))).toBe("integer");
200
- });
201
-
202
- it("check validates concrete numbers", () => {
203
- const r = createRange({ min: 0, max: 10 });
204
- if (r.kind === "refined" && r.refinement.check) {
205
- expect(r.refinement.check(5)).toBe(true);
206
- expect(r.refinement.check(-1)).toBe(false);
207
- expect(r.refinement.check(11)).toBe(false);
208
- }
209
- });
210
- });
@@ -1,213 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import {
3
- T,
4
- typeValueEquals,
5
- simplifyUnion,
6
- widenLiteral,
7
- isSubtypeOf,
8
- typeValueToString,
9
- narrowType,
10
- subtractType,
11
- getPrimitiveTypeOf,
12
- } from "../type-value.ts";
13
-
14
- describe("T factory", () => {
15
- it("creates literal type values", () => {
16
- expect(T.literal(1)).toEqual({ kind: "literal", value: 1 });
17
- expect(T.literal("hello")).toEqual({ kind: "literal", value: "hello" });
18
- expect(T.literal(true)).toEqual({ kind: "literal", value: true });
19
- expect(T.literal(null)).toEqual({ kind: "literal", value: null });
20
- expect(T.literal(undefined)).toEqual({ kind: "literal", value: undefined });
21
- });
22
-
23
- it("provides primitive singletons", () => {
24
- expect(T.number).toEqual({ kind: "primitive", type: "number" });
25
- expect(T.string).toEqual({ kind: "primitive", type: "string" });
26
- expect(T.boolean).toEqual({ kind: "primitive", type: "boolean" });
27
- });
28
-
29
- it("creates object type values", () => {
30
- const obj = T.object({ x: T.number, y: T.string });
31
- expect(obj.kind).toBe("object");
32
- if (obj.kind === "object") {
33
- expect(obj.properties.x).toBe(T.number);
34
- expect(obj.properties.y).toBe(T.string);
35
- expect(typeof obj.id).toBe("symbol");
36
- }
37
- });
38
-
39
- it("creates union type values with simplification", () => {
40
- const u = T.union(T.literal(1), T.literal(2));
41
- expect(u.kind).toBe("union");
42
- if (u.kind === "union") {
43
- expect(u.members).toHaveLength(2);
44
- }
45
- });
46
-
47
- it("simplifies single-member union to the member", () => {
48
- const u = T.union(T.number);
49
- expect(u).toBe(T.number);
50
- });
51
-
52
- it("simplifies empty union to never", () => {
53
- const u = T.union(T.never, T.never);
54
- expect(u).toEqual(T.never);
55
- });
56
- });
57
-
58
- describe("typeValueEquals", () => {
59
- it("compares literals", () => {
60
- expect(typeValueEquals(T.literal(1), T.literal(1))).toBe(true);
61
- expect(typeValueEquals(T.literal(1), T.literal(2))).toBe(false);
62
- });
63
-
64
- it("compares primitives", () => {
65
- expect(typeValueEquals(T.number, T.number)).toBe(true);
66
- expect(typeValueEquals(T.number, T.string)).toBe(false);
67
- });
68
-
69
- it("compares never and unknown", () => {
70
- expect(typeValueEquals(T.never, T.never)).toBe(true);
71
- expect(typeValueEquals(T.unknown, T.unknown)).toBe(true);
72
- expect(typeValueEquals(T.never, T.unknown)).toBe(false);
73
- });
74
- });
75
-
76
- describe("simplifyUnion", () => {
77
- it("deduplicates members", () => {
78
- const result = simplifyUnion([T.literal(1), T.literal(1), T.literal(2)]);
79
- expect(result.kind).toBe("union");
80
- if (result.kind === "union") {
81
- expect(result.members).toHaveLength(2);
82
- }
83
- });
84
-
85
- it("flattens nested unions", () => {
86
- const inner = T.union(T.literal(1), T.literal(2));
87
- const result = simplifyUnion([inner, T.literal(3)]);
88
- expect(result.kind).toBe("union");
89
- if (result.kind === "union") {
90
- expect(result.members).toHaveLength(3);
91
- }
92
- });
93
-
94
- it("absorbs unknown", () => {
95
- const result = simplifyUnion([T.number, T.unknown]);
96
- expect(result).toEqual(T.unknown);
97
- });
98
-
99
- it("removes never", () => {
100
- const result = simplifyUnion([T.number, T.never]);
101
- expect(result).toBe(T.number);
102
- });
103
- });
104
-
105
- describe("widenLiteral", () => {
106
- it("widens number literal to number", () => {
107
- expect(widenLiteral(T.literal(42))).toEqual(T.number);
108
- });
109
-
110
- it("widens string literal to string", () => {
111
- expect(widenLiteral(T.literal("hi"))).toEqual(T.string);
112
- });
113
-
114
- it("widens boolean literal to boolean", () => {
115
- expect(widenLiteral(T.literal(true))).toEqual(T.boolean);
116
- });
117
-
118
- it("returns non-literal unchanged", () => {
119
- expect(widenLiteral(T.number)).toBe(T.number);
120
- });
121
- });
122
-
123
- describe("isSubtypeOf", () => {
124
- it("everything is subtype of unknown", () => {
125
- expect(isSubtypeOf(T.number, T.unknown)).toBe(true);
126
- expect(isSubtypeOf(T.literal(1), T.unknown)).toBe(true);
127
- });
128
-
129
- it("never is subtype of everything", () => {
130
- expect(isSubtypeOf(T.never, T.number)).toBe(true);
131
- expect(isSubtypeOf(T.never, T.never)).toBe(true);
132
- });
133
-
134
- it("literal is subtype of its primitive", () => {
135
- expect(isSubtypeOf(T.literal(1), T.number)).toBe(true);
136
- expect(isSubtypeOf(T.literal("hi"), T.string)).toBe(true);
137
- expect(isSubtypeOf(T.literal(true), T.boolean)).toBe(true);
138
- });
139
-
140
- it("literal is not subtype of wrong primitive", () => {
141
- expect(isSubtypeOf(T.literal(1), T.string)).toBe(false);
142
- });
143
-
144
- it("union is subtype if all members are", () => {
145
- const u = T.union(T.literal(1), T.literal(2));
146
- expect(isSubtypeOf(u, T.number)).toBe(true);
147
- });
148
-
149
- it("value is subtype of union containing it", () => {
150
- const u = T.union(T.number, T.string);
151
- expect(isSubtypeOf(T.number, u)).toBe(true);
152
- });
153
- });
154
-
155
- describe("typeValueToString", () => {
156
- it("formats literals", () => {
157
- expect(typeValueToString(T.literal(1))).toBe("1");
158
- expect(typeValueToString(T.literal("hi"))).toBe('"hi"');
159
- expect(typeValueToString(T.literal(true))).toBe("true");
160
- expect(typeValueToString(T.literal(null))).toBe("null");
161
- expect(typeValueToString(T.literal(undefined))).toBe("undefined");
162
- });
163
-
164
- it("formats primitives", () => {
165
- expect(typeValueToString(T.number)).toBe("number");
166
- expect(typeValueToString(T.string)).toBe("string");
167
- });
168
-
169
- it("formats unions", () => {
170
- const u = T.union(T.number, T.string);
171
- expect(typeValueToString(u)).toBe("number | string");
172
- });
173
-
174
- it("formats objects", () => {
175
- const obj = T.object({ x: T.number });
176
- expect(typeValueToString(obj)).toBe("{ x: number }");
177
- });
178
-
179
- it("formats never and unknown", () => {
180
- expect(typeValueToString(T.never)).toBe("never");
181
- expect(typeValueToString(T.unknown)).toBe("unknown");
182
- });
183
- });
184
-
185
- describe("narrowType", () => {
186
- it("narrows union by predicate", () => {
187
- const u = T.union(T.literal(1), T.literal("a"), T.literal(true));
188
- const result = narrowType(u, (m) => m.kind === "literal" && typeof m.value === "number");
189
- expect(typeValueEquals(result, T.literal(1))).toBe(true);
190
- });
191
-
192
- it("returns never if nothing matches", () => {
193
- const result = narrowType(T.number, (m) => m.kind === "literal");
194
- expect(result).toEqual(T.never);
195
- });
196
- });
197
-
198
- describe("getPrimitiveTypeOf", () => {
199
- it("returns typeof for literals", () => {
200
- expect(getPrimitiveTypeOf(T.literal(1))).toBe("number");
201
- expect(getPrimitiveTypeOf(T.literal("hi"))).toBe("string");
202
- expect(getPrimitiveTypeOf(T.literal(null))).toBe("object");
203
- });
204
-
205
- it("returns type for primitives", () => {
206
- expect(getPrimitiveTypeOf(T.number)).toBe("number");
207
- });
208
-
209
- it("returns object for object/array/tuple", () => {
210
- expect(getPrimitiveTypeOf(T.object({}))).toBe("object");
211
- expect(getPrimitiveTypeOf(T.array(T.number))).toBe("object");
212
- });
213
- });
@@ -1,69 +0,0 @@
1
- import { type TypeValue, T } from "./type-value.ts";
2
-
3
- export type Environment = {
4
- lookup(name: string): TypeValue;
5
- bind(name: string, value: TypeValue): Environment;
6
- update(name: string, value: TypeValue): boolean;
7
- extend(bindings: Record<string, TypeValue>): Environment;
8
- has(name: string): boolean;
9
- snapshot(): Environment;
10
- getOwnBindings(): Record<string, TypeValue>;
11
- };
12
-
13
- export function createEnvironment(
14
- parent?: Environment,
15
- bindings: Map<string, TypeValue> = new Map(),
16
- ): Environment {
17
- const store = new Map(bindings);
18
-
19
- const env: Environment = {
20
- lookup(name) {
21
- const val = store.get(name);
22
- if (val !== undefined) return val;
23
- if (parent) return parent.lookup(name);
24
- return T.undefined;
25
- },
26
-
27
- bind(name, value) {
28
- store.set(name, value);
29
- return env;
30
- },
31
-
32
- update(name, value) {
33
- if (store.has(name)) {
34
- store.set(name, value);
35
- return true;
36
- }
37
- if (parent) return parent.update(name, value);
38
- return false;
39
- },
40
-
41
- extend(newBindings) {
42
- const childMap = new Map<string, TypeValue>();
43
- for (const [k, v] of Object.entries(newBindings)) {
44
- childMap.set(k, v);
45
- }
46
- return createEnvironment(env, childMap);
47
- },
48
-
49
- has(name) {
50
- return store.has(name) || (parent?.has(name) ?? false);
51
- },
52
-
53
- snapshot() {
54
- const clonedStore = new Map(store);
55
- const clonedParent = parent?.snapshot();
56
- return createEnvironment(clonedParent, clonedStore);
57
- },
58
-
59
- getOwnBindings() {
60
- const result: Record<string, TypeValue> = {};
61
- for (const [k, v] of store) {
62
- result[k] = v;
63
- }
64
- return result;
65
- },
66
- };
67
-
68
- return env;
69
- }
package/src/index.ts DELETED
@@ -1,40 +0,0 @@
1
- export {
2
- type TypeValue,
3
- type LiteralValue,
4
- type Refinement,
5
- T,
6
- typeValueEquals,
7
- simplifyUnion,
8
- widenLiteral,
9
- isSubtypeOf,
10
- typeValueToString,
11
- narrowType,
12
- subtractType,
13
- getPrimitiveTypeOf,
14
- getRefinedBase,
15
- deepCloneTypeValue,
16
- mergeObjectProperties,
17
- } from "./type-value.ts";
18
-
19
- export { Ops, applyBinaryOp, dispatchBinaryOp, dispatchMethod, dispatchProperty } from "./ops.ts";
20
-
21
- export {
22
- type Environment,
23
- createEnvironment,
24
- } from "./environment.ts";
25
-
26
- export {
27
- createTemplate,
28
- isTemplate,
29
- getTemplateParts,
30
- concatTemplates,
31
- getKnownPrefix,
32
- getKnownSuffix,
33
- } from "./refinements/template.ts";
34
-
35
- export {
36
- createRange,
37
- isRange,
38
- getRangeMeta,
39
- type RangeMeta,
40
- } from "./refinements/range.ts";