@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/README.md +26 -0
- package/dist/chunk-BU4A6XJ3.js +6856 -0
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +168 -0
- package/dist/index-CVYjXnwU.d.ts +902 -0
- package/dist/index.d.ts +953 -10
- package/dist/index.js +4489 -110
- package/package.json +33 -4
- package/CHANGELOG.md +0 -15
- package/src/__tests__/environment.test.ts +0 -48
- package/src/__tests__/ops-refined.test.ts +0 -194
- package/src/__tests__/ops.test.ts +0 -109
- package/src/__tests__/type-value-phase2.test.ts +0 -117
- package/src/__tests__/type-value-phase3.test.ts +0 -94
- package/src/__tests__/type-value-refined.test.ts +0 -210
- package/src/__tests__/type-value.test.ts +0 -213
- package/src/environment.ts +0 -69
- package/src/index.ts +0 -40
- package/src/ops.ts +0 -210
- package/src/refinements/index.ts +0 -2
- package/src/refinements/range.ts +0 -86
- package/src/refinements/template.ts +0 -178
- package/src/type-value.ts +0 -372
- package/tsconfig.json +0 -8
package/src/ops.ts
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { type TypeValue, T, isSubtypeOf, getRefinedBase } from "./type-value.ts";
|
|
2
|
-
import { concatTemplates, isTemplate } from "./refinements/template.ts";
|
|
3
|
-
|
|
4
|
-
function bothLiteral(
|
|
5
|
-
l: TypeValue,
|
|
6
|
-
r: TypeValue,
|
|
7
|
-
): { lv: string | number | boolean | null | undefined; rv: string | number | boolean | null | undefined } | null {
|
|
8
|
-
if (l.kind === "literal" && r.kind === "literal") {
|
|
9
|
-
return { lv: l.value, rv: r.value };
|
|
10
|
-
}
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const Ops = {
|
|
15
|
-
add(left: TypeValue, right: TypeValue): TypeValue {
|
|
16
|
-
const lit = bothLiteral(left, right);
|
|
17
|
-
if (lit) {
|
|
18
|
-
return T.literal((lit.lv as any) + (lit.rv as any));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const leftIsString = isSubtypeOf(left, T.string) || isTemplate(left);
|
|
22
|
-
const rightIsString = isSubtypeOf(right, T.string) || isTemplate(right);
|
|
23
|
-
if (leftIsString || rightIsString) {
|
|
24
|
-
const hasStructure =
|
|
25
|
-
(left.kind === "literal" && typeof left.value === "string") ||
|
|
26
|
-
(right.kind === "literal" && typeof right.value === "string") ||
|
|
27
|
-
isTemplate(left) || isTemplate(right);
|
|
28
|
-
if (hasStructure) {
|
|
29
|
-
return concatTemplates(left, right);
|
|
30
|
-
}
|
|
31
|
-
return T.string;
|
|
32
|
-
}
|
|
33
|
-
if (isSubtypeOf(left, T.number) && isSubtypeOf(right, T.number)) {
|
|
34
|
-
return T.number;
|
|
35
|
-
}
|
|
36
|
-
return T.union(T.number, T.string);
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
sub(left: TypeValue, right: TypeValue): TypeValue {
|
|
40
|
-
const lit = bothLiteral(left, right);
|
|
41
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
42
|
-
return T.literal(lit.lv - lit.rv);
|
|
43
|
-
}
|
|
44
|
-
return T.number;
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
mul(left: TypeValue, right: TypeValue): TypeValue {
|
|
48
|
-
const lit = bothLiteral(left, right);
|
|
49
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
50
|
-
return T.literal(lit.lv * lit.rv);
|
|
51
|
-
}
|
|
52
|
-
return T.number;
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
div(left: TypeValue, right: TypeValue): TypeValue {
|
|
56
|
-
const lit = bothLiteral(left, right);
|
|
57
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
58
|
-
return T.literal(lit.lv / lit.rv);
|
|
59
|
-
}
|
|
60
|
-
return T.number;
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
mod(left: TypeValue, right: TypeValue): TypeValue {
|
|
64
|
-
const lit = bothLiteral(left, right);
|
|
65
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
66
|
-
return T.literal(lit.lv % lit.rv);
|
|
67
|
-
}
|
|
68
|
-
return T.number;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
strictEq(left: TypeValue, right: TypeValue): TypeValue {
|
|
72
|
-
const lit = bothLiteral(left, right);
|
|
73
|
-
if (lit) return T.literal(lit.lv === lit.rv);
|
|
74
|
-
return T.boolean;
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
strictNeq(left: TypeValue, right: TypeValue): TypeValue {
|
|
78
|
-
const lit = bothLiteral(left, right);
|
|
79
|
-
if (lit) return T.literal(lit.lv !== lit.rv);
|
|
80
|
-
return T.boolean;
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
gt(left: TypeValue, right: TypeValue): TypeValue {
|
|
84
|
-
const lit = bothLiteral(left, right);
|
|
85
|
-
if (lit) return T.literal((lit.lv as any) > (lit.rv as any));
|
|
86
|
-
return T.boolean;
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
lt(left: TypeValue, right: TypeValue): TypeValue {
|
|
90
|
-
const lit = bothLiteral(left, right);
|
|
91
|
-
if (lit) return T.literal((lit.lv as any) < (lit.rv as any));
|
|
92
|
-
return T.boolean;
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
gte(left: TypeValue, right: TypeValue): TypeValue {
|
|
96
|
-
const lit = bothLiteral(left, right);
|
|
97
|
-
if (lit) return T.literal((lit.lv as any) >= (lit.rv as any));
|
|
98
|
-
return T.boolean;
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
lte(left: TypeValue, right: TypeValue): TypeValue {
|
|
102
|
-
const lit = bothLiteral(left, right);
|
|
103
|
-
if (lit) return T.literal((lit.lv as any) <= (lit.rv as any));
|
|
104
|
-
return T.boolean;
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
typeof_(operand: TypeValue): TypeValue {
|
|
108
|
-
if (operand.kind === "literal") {
|
|
109
|
-
const v = operand.value;
|
|
110
|
-
if (v === null) return T.literal("object");
|
|
111
|
-
return T.literal(typeof v);
|
|
112
|
-
}
|
|
113
|
-
if (operand.kind === "primitive") return T.literal(operand.type);
|
|
114
|
-
if (operand.kind === "refined") return Ops.typeof_(getRefinedBase(operand));
|
|
115
|
-
if (operand.kind === "object") return T.literal("object");
|
|
116
|
-
if (operand.kind === "array" || operand.kind === "tuple")
|
|
117
|
-
return T.literal("object");
|
|
118
|
-
if (operand.kind === "function") return T.literal("function");
|
|
119
|
-
if (operand.kind === "promise") return T.literal("object");
|
|
120
|
-
if (operand.kind === "instance") return T.literal("object");
|
|
121
|
-
return T.string;
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
not(operand: TypeValue): TypeValue {
|
|
125
|
-
if (operand.kind === "literal") return T.literal(!operand.value);
|
|
126
|
-
return T.boolean;
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
neg(operand: TypeValue): TypeValue {
|
|
130
|
-
if (operand.kind === "literal" && typeof operand.value === "number") {
|
|
131
|
-
return T.literal(-operand.value);
|
|
132
|
-
}
|
|
133
|
-
return T.number;
|
|
134
|
-
},
|
|
135
|
-
} as const;
|
|
136
|
-
|
|
137
|
-
const binaryOpMap: Record<string, (l: TypeValue, r: TypeValue) => TypeValue> = {
|
|
138
|
-
"+": Ops.add,
|
|
139
|
-
"-": Ops.sub,
|
|
140
|
-
"*": Ops.mul,
|
|
141
|
-
"/": Ops.div,
|
|
142
|
-
"%": Ops.mod,
|
|
143
|
-
"===": Ops.strictEq,
|
|
144
|
-
"!==": Ops.strictNeq,
|
|
145
|
-
">": Ops.gt,
|
|
146
|
-
"<": Ops.lt,
|
|
147
|
-
">=": Ops.gte,
|
|
148
|
-
"<=": Ops.lte,
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
export function applyBinaryOp(
|
|
152
|
-
op: string,
|
|
153
|
-
left: TypeValue,
|
|
154
|
-
right: TypeValue,
|
|
155
|
-
): TypeValue {
|
|
156
|
-
const fn = binaryOpMap[op];
|
|
157
|
-
if (!fn) return T.unknown;
|
|
158
|
-
return fn(left, right);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function dispatchBinaryOp(
|
|
162
|
-
op: string,
|
|
163
|
-
left: TypeValue,
|
|
164
|
-
right: TypeValue,
|
|
165
|
-
): TypeValue {
|
|
166
|
-
if (left.kind === "refined" && left.refinement.ops?.[op]) {
|
|
167
|
-
const result = left.refinement.ops[op](left, right);
|
|
168
|
-
if (result !== undefined) return result;
|
|
169
|
-
}
|
|
170
|
-
if (right.kind === "refined" && right.refinement.ops?.[op]) {
|
|
171
|
-
const result = right.refinement.ops[op](right, left);
|
|
172
|
-
if (result !== undefined) return result;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const baseLeft = left.kind === "refined" ? left.base : left;
|
|
176
|
-
const baseRight = right.kind === "refined" ? right.base : right;
|
|
177
|
-
if (baseLeft !== left || baseRight !== right) {
|
|
178
|
-
return dispatchBinaryOp(op, baseLeft, baseRight);
|
|
179
|
-
}
|
|
180
|
-
return applyBinaryOp(op, left, right);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export function dispatchMethod(
|
|
184
|
-
receiver: TypeValue,
|
|
185
|
-
name: string,
|
|
186
|
-
args: TypeValue[],
|
|
187
|
-
): TypeValue | undefined {
|
|
188
|
-
if (receiver.kind === "refined" && receiver.refinement.methods?.[name]) {
|
|
189
|
-
const result = receiver.refinement.methods[name](receiver, args);
|
|
190
|
-
if (result !== undefined) return result;
|
|
191
|
-
}
|
|
192
|
-
if (receiver.kind === "refined") {
|
|
193
|
-
return dispatchMethod(receiver.base, name, args);
|
|
194
|
-
}
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function dispatchProperty(
|
|
199
|
-
receiver: TypeValue,
|
|
200
|
-
name: string,
|
|
201
|
-
): TypeValue | undefined {
|
|
202
|
-
if (receiver.kind === "refined" && receiver.refinement.properties?.[name]) {
|
|
203
|
-
const result = receiver.refinement.properties[name](receiver);
|
|
204
|
-
if (result !== undefined) return result;
|
|
205
|
-
}
|
|
206
|
-
if (receiver.kind === "refined") {
|
|
207
|
-
return dispatchProperty(receiver.base, name);
|
|
208
|
-
}
|
|
209
|
-
return undefined;
|
|
210
|
-
}
|
package/src/refinements/index.ts
DELETED
package/src/refinements/range.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { type TypeValue, type Refinement, T } from "../type-value.ts";
|
|
2
|
-
|
|
3
|
-
export type RangeMeta = {
|
|
4
|
-
min?: number;
|
|
5
|
-
max?: number;
|
|
6
|
-
integer?: boolean;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
function formatRangeName(meta: RangeMeta): string {
|
|
10
|
-
const parts: string[] = [];
|
|
11
|
-
if (meta.integer) parts.push("integer");
|
|
12
|
-
else parts.push("number");
|
|
13
|
-
|
|
14
|
-
const constraints: string[] = [];
|
|
15
|
-
if (meta.min != null) constraints.push(`>= ${meta.min}`);
|
|
16
|
-
if (meta.max != null) constraints.push(`<= ${meta.max}`);
|
|
17
|
-
if (constraints.length > 0) parts.push(`(${constraints.join(", ")})`);
|
|
18
|
-
|
|
19
|
-
return parts.join(" ");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function createRangeRefinement(meta: RangeMeta): Refinement {
|
|
23
|
-
return {
|
|
24
|
-
name: formatRangeName(meta),
|
|
25
|
-
meta: { ...meta },
|
|
26
|
-
check(value: unknown) {
|
|
27
|
-
if (typeof value !== "number") return false;
|
|
28
|
-
if (meta.integer && !Number.isInteger(value)) return false;
|
|
29
|
-
if (meta.min != null && value < meta.min) return false;
|
|
30
|
-
if (meta.max != null && value > meta.max) return false;
|
|
31
|
-
return true;
|
|
32
|
-
},
|
|
33
|
-
ops: {
|
|
34
|
-
">="(self: TypeValue, other: TypeValue) {
|
|
35
|
-
const m = getRangeMeta(self);
|
|
36
|
-
if (!m || other.kind !== "literal" || typeof other.value !== "number") return undefined;
|
|
37
|
-
if (m.min != null && m.min >= other.value) return T.literal(true);
|
|
38
|
-
if (m.max != null && m.max < other.value) return T.literal(false);
|
|
39
|
-
return undefined;
|
|
40
|
-
},
|
|
41
|
-
">"(self: TypeValue, other: TypeValue) {
|
|
42
|
-
const m = getRangeMeta(self);
|
|
43
|
-
if (!m || other.kind !== "literal" || typeof other.value !== "number") return undefined;
|
|
44
|
-
if (m.min != null && m.min > other.value) return T.literal(true);
|
|
45
|
-
if (m.max != null && m.max <= other.value) return T.literal(false);
|
|
46
|
-
return undefined;
|
|
47
|
-
},
|
|
48
|
-
"<="(self: TypeValue, other: TypeValue) {
|
|
49
|
-
const m = getRangeMeta(self);
|
|
50
|
-
if (!m || other.kind !== "literal" || typeof other.value !== "number") return undefined;
|
|
51
|
-
if (m.max != null && m.max <= other.value) return T.literal(true);
|
|
52
|
-
if (m.min != null && m.min > other.value) return T.literal(false);
|
|
53
|
-
return undefined;
|
|
54
|
-
},
|
|
55
|
-
"<"(self: TypeValue, other: TypeValue) {
|
|
56
|
-
const m = getRangeMeta(self);
|
|
57
|
-
if (!m || other.kind !== "literal" || typeof other.value !== "number") return undefined;
|
|
58
|
-
if (m.max != null && m.max < other.value) return T.literal(true);
|
|
59
|
-
if (m.min != null && m.min >= other.value) return T.literal(false);
|
|
60
|
-
return undefined;
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function createRange(meta: RangeMeta): TypeValue {
|
|
67
|
-
if (meta.min != null && meta.max != null && meta.min === meta.max) {
|
|
68
|
-
return T.literal(meta.min);
|
|
69
|
-
}
|
|
70
|
-
return T.refine(T.number, createRangeRefinement(meta));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function isRange(tv: TypeValue): boolean {
|
|
74
|
-
if (tv.kind !== "refined") return false;
|
|
75
|
-
const m = tv.refinement.meta;
|
|
76
|
-
return m.min !== undefined || m.max !== undefined || m.integer !== undefined;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function getRangeMeta(tv: TypeValue): RangeMeta | undefined {
|
|
80
|
-
if (tv.kind !== "refined") return undefined;
|
|
81
|
-
const m = tv.refinement.meta;
|
|
82
|
-
if (m.min !== undefined || m.max !== undefined || m.integer !== undefined) {
|
|
83
|
-
return m as RangeMeta;
|
|
84
|
-
}
|
|
85
|
-
return undefined;
|
|
86
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { type TypeValue, type Refinement, T, typeValueToString } from "../type-value.ts";
|
|
2
|
-
import { createRange } from "./range.ts";
|
|
3
|
-
|
|
4
|
-
export function getKnownPrefix(parts: TypeValue[]): string {
|
|
5
|
-
let prefix = "";
|
|
6
|
-
for (const p of parts) {
|
|
7
|
-
if (p.kind === "literal" && typeof p.value === "string") {
|
|
8
|
-
prefix += p.value;
|
|
9
|
-
} else {
|
|
10
|
-
break;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return prefix;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getKnownSuffix(parts: TypeValue[]): string {
|
|
17
|
-
let suffix = "";
|
|
18
|
-
for (let i = parts.length - 1; i >= 0; i--) {
|
|
19
|
-
const p = parts[i];
|
|
20
|
-
if (p.kind === "literal" && typeof p.value === "string") {
|
|
21
|
-
suffix = p.value + suffix;
|
|
22
|
-
} else {
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return suffix;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function getFixedLength(parts: TypeValue[]): number {
|
|
30
|
-
let len = 0;
|
|
31
|
-
for (const p of parts) {
|
|
32
|
-
if (p.kind === "literal" && typeof p.value === "string") {
|
|
33
|
-
len += p.value.length;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return len;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function getAllFixedText(parts: TypeValue[]): string {
|
|
40
|
-
return parts
|
|
41
|
-
.filter((p): p is TypeValue & { kind: "literal" } => p.kind === "literal" && typeof p.value === "string")
|
|
42
|
-
.map((p) => p.value as string)
|
|
43
|
-
.join("");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function formatTemplateName(parts: TypeValue[]): string {
|
|
47
|
-
const inner = parts
|
|
48
|
-
.map((p) => (p.kind === "literal" && typeof p.value === "string" ? p.value : `\${${typeValueToString(p)}}`))
|
|
49
|
-
.join("");
|
|
50
|
-
return `\`${inner}\``;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function normalizeParts(parts: TypeValue[]): TypeValue[] {
|
|
54
|
-
const result: TypeValue[] = [];
|
|
55
|
-
for (const p of parts) {
|
|
56
|
-
const last = result[result.length - 1];
|
|
57
|
-
if (
|
|
58
|
-
last?.kind === "literal" && typeof last.value === "string" &&
|
|
59
|
-
p.kind === "literal" && typeof p.value === "string"
|
|
60
|
-
) {
|
|
61
|
-
result[result.length - 1] = T.literal(last.value + p.value);
|
|
62
|
-
} else if (
|
|
63
|
-
last?.kind === "primitive" && last.type === "string" &&
|
|
64
|
-
p.kind === "primitive" && p.type === "string"
|
|
65
|
-
) {
|
|
66
|
-
// T.string + T.string collapses to T.string
|
|
67
|
-
} else {
|
|
68
|
-
result.push(p);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function createTemplateRefinement(parts: TypeValue[]): Refinement {
|
|
75
|
-
return {
|
|
76
|
-
name: formatTemplateName(parts),
|
|
77
|
-
meta: { parts },
|
|
78
|
-
check(value: unknown) {
|
|
79
|
-
if (typeof value !== "string") return false;
|
|
80
|
-
return matchesTemplate(value, parts);
|
|
81
|
-
},
|
|
82
|
-
ops: {
|
|
83
|
-
"+"(self: TypeValue, other: TypeValue) {
|
|
84
|
-
return concatTemplates(self, other);
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
methods: {
|
|
88
|
-
startsWith(_self: TypeValue, args: TypeValue[]) {
|
|
89
|
-
const arg = args[0];
|
|
90
|
-
if (arg?.kind !== "literal" || typeof arg.value !== "string") return undefined;
|
|
91
|
-
const prefix = getKnownPrefix((_self as any).refinement.meta.parts as TypeValue[]);
|
|
92
|
-
const search = arg.value;
|
|
93
|
-
if (prefix.length >= search.length) {
|
|
94
|
-
return T.literal(prefix.startsWith(search));
|
|
95
|
-
}
|
|
96
|
-
if (search.startsWith(prefix)) return undefined;
|
|
97
|
-
return T.literal(false);
|
|
98
|
-
},
|
|
99
|
-
endsWith(_self: TypeValue, args: TypeValue[]) {
|
|
100
|
-
const arg = args[0];
|
|
101
|
-
if (arg?.kind !== "literal" || typeof arg.value !== "string") return undefined;
|
|
102
|
-
const suffix = getKnownSuffix((_self as any).refinement.meta.parts as TypeValue[]);
|
|
103
|
-
const search = arg.value;
|
|
104
|
-
if (suffix.length >= search.length) {
|
|
105
|
-
return T.literal(suffix.endsWith(search));
|
|
106
|
-
}
|
|
107
|
-
if (search.endsWith(suffix)) return undefined;
|
|
108
|
-
return T.literal(false);
|
|
109
|
-
},
|
|
110
|
-
includes(_self: TypeValue, args: TypeValue[]) {
|
|
111
|
-
const arg = args[0];
|
|
112
|
-
if (arg?.kind !== "literal" || typeof arg.value !== "string") return undefined;
|
|
113
|
-
const fixed = getAllFixedText((_self as any).refinement.meta.parts as TypeValue[]);
|
|
114
|
-
if (fixed.includes(arg.value)) return T.literal(true);
|
|
115
|
-
return undefined;
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
properties: {
|
|
119
|
-
length(_self: TypeValue) {
|
|
120
|
-
const parts = (_self as any).refinement.meta.parts as TypeValue[];
|
|
121
|
-
const hasAbstract = parts.some((p) => p.kind !== "literal");
|
|
122
|
-
if (!hasAbstract) return undefined;
|
|
123
|
-
const minLen = getFixedLength(parts);
|
|
124
|
-
return createRange({ min: minLen });
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function matchesTemplate(value: string, parts: TypeValue[]): boolean {
|
|
131
|
-
let pos = 0;
|
|
132
|
-
for (let i = 0; i < parts.length; i++) {
|
|
133
|
-
const p = parts[i];
|
|
134
|
-
if (p.kind === "literal" && typeof p.value === "string") {
|
|
135
|
-
if (!value.startsWith(p.value, pos)) return false;
|
|
136
|
-
pos += p.value.length;
|
|
137
|
-
} else {
|
|
138
|
-
if (i === parts.length - 1) return true;
|
|
139
|
-
const next = parts[i + 1];
|
|
140
|
-
if (next?.kind === "literal" && typeof next.value === "string") {
|
|
141
|
-
const idx = value.indexOf(next.value, pos);
|
|
142
|
-
if (idx === -1) return false;
|
|
143
|
-
pos = idx;
|
|
144
|
-
} else {
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return pos === value.length;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export function createTemplate(parts: TypeValue[]): TypeValue {
|
|
153
|
-
const normalized = normalizeParts(parts);
|
|
154
|
-
if (normalized.length === 1 && normalized[0].kind === "literal") {
|
|
155
|
-
return normalized[0];
|
|
156
|
-
}
|
|
157
|
-
if (normalized.length === 1 && normalized[0].kind === "primitive" && normalized[0].type === "string") {
|
|
158
|
-
return T.string;
|
|
159
|
-
}
|
|
160
|
-
return T.refine(T.string, createTemplateRefinement(normalized));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function isTemplate(tv: TypeValue): boolean {
|
|
164
|
-
return tv.kind === "refined" && Array.isArray(tv.refinement.meta.parts);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function getTemplateParts(tv: TypeValue): TypeValue[] | undefined {
|
|
168
|
-
if (tv.kind === "refined" && Array.isArray(tv.refinement.meta.parts)) {
|
|
169
|
-
return tv.refinement.meta.parts as TypeValue[];
|
|
170
|
-
}
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function concatTemplates(left: TypeValue, right: TypeValue): TypeValue {
|
|
175
|
-
const leftParts = getTemplateParts(left) ?? [left];
|
|
176
|
-
const rightParts = getTemplateParts(right) ?? [right];
|
|
177
|
-
return createTemplate([...leftParts, ...rightParts]);
|
|
178
|
-
}
|