@nudojs/core 0.1.0 → 0.2.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.
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/dist/index.d.ts +34 -1
- package/dist/index.js +312 -1
- package/package.json +11 -1
- package/src/__tests__/ops-refined.test.ts +194 -0
- package/src/__tests__/type-value-refined.test.ts +210 -0
- package/src/index.ts +19 -1
- package/src/ops.ts +65 -2
- package/src/refinements/index.ts +2 -0
- package/src/refinements/range.ts +86 -0
- package/src/refinements/template.ts +178 -0
- package/src/type-value.ts +40 -0
package/CHANGELOG.md
CHANGED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nudo Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
CHANGED
|
@@ -12,12 +12,24 @@ type Environment = {
|
|
|
12
12
|
declare function createEnvironment(parent?: Environment, bindings?: Map<string, TypeValue>): Environment;
|
|
13
13
|
|
|
14
14
|
type LiteralValue = string | number | boolean | null | undefined;
|
|
15
|
+
type Refinement = {
|
|
16
|
+
name: string;
|
|
17
|
+
meta: Record<string, unknown>;
|
|
18
|
+
check?: (value: unknown) => boolean;
|
|
19
|
+
ops?: Record<string, (self: TypeValue, other: TypeValue) => TypeValue | undefined>;
|
|
20
|
+
methods?: Record<string, (self: TypeValue, args: TypeValue[]) => TypeValue | undefined>;
|
|
21
|
+
properties?: Record<string, (self: TypeValue) => TypeValue | undefined>;
|
|
22
|
+
};
|
|
15
23
|
type TypeValue = {
|
|
16
24
|
kind: "literal";
|
|
17
25
|
value: LiteralValue;
|
|
18
26
|
} | {
|
|
19
27
|
kind: "primitive";
|
|
20
28
|
type: "number" | "string" | "boolean" | "bigint" | "symbol";
|
|
29
|
+
} | {
|
|
30
|
+
kind: "refined";
|
|
31
|
+
base: TypeValue;
|
|
32
|
+
refinement: Refinement;
|
|
21
33
|
} | {
|
|
22
34
|
kind: "object";
|
|
23
35
|
properties: Record<string, TypeValue>;
|
|
@@ -67,6 +79,7 @@ declare const T: {
|
|
|
67
79
|
readonly instanceOf: (className: string, properties?: Record<string, TypeValue>) => TypeValue;
|
|
68
80
|
readonly union: typeof createUnion;
|
|
69
81
|
readonly fn: (params: string[], body: Node, closure: Environment) => TypeValue;
|
|
82
|
+
readonly refine: (base: TypeValue, refinement: Refinement) => TypeValue;
|
|
70
83
|
};
|
|
71
84
|
declare function typeValueEquals(a: TypeValue, b: TypeValue): boolean;
|
|
72
85
|
declare function simplifyUnion(members: TypeValue[]): TypeValue;
|
|
@@ -82,6 +95,7 @@ declare function typeValueToString(tv: TypeValue): string;
|
|
|
82
95
|
declare function narrowType(tv: TypeValue, predicate: (member: TypeValue) => boolean): TypeValue;
|
|
83
96
|
declare function subtractType(tv: TypeValue, predicate: (member: TypeValue) => boolean): TypeValue;
|
|
84
97
|
declare function getPrimitiveTypeOf(tv: TypeValue): string | undefined;
|
|
98
|
+
declare function getRefinedBase(tv: TypeValue): TypeValue;
|
|
85
99
|
|
|
86
100
|
declare const Ops: {
|
|
87
101
|
readonly add: (left: TypeValue, right: TypeValue) => TypeValue;
|
|
@@ -100,5 +114,24 @@ declare const Ops: {
|
|
|
100
114
|
readonly neg: (operand: TypeValue) => TypeValue;
|
|
101
115
|
};
|
|
102
116
|
declare function applyBinaryOp(op: string, left: TypeValue, right: TypeValue): TypeValue;
|
|
117
|
+
declare function dispatchBinaryOp(op: string, left: TypeValue, right: TypeValue): TypeValue;
|
|
118
|
+
declare function dispatchMethod(receiver: TypeValue, name: string, args: TypeValue[]): TypeValue | undefined;
|
|
119
|
+
declare function dispatchProperty(receiver: TypeValue, name: string): TypeValue | undefined;
|
|
120
|
+
|
|
121
|
+
declare function getKnownPrefix(parts: TypeValue[]): string;
|
|
122
|
+
declare function getKnownSuffix(parts: TypeValue[]): string;
|
|
123
|
+
declare function createTemplate(parts: TypeValue[]): TypeValue;
|
|
124
|
+
declare function isTemplate(tv: TypeValue): boolean;
|
|
125
|
+
declare function getTemplateParts(tv: TypeValue): TypeValue[] | undefined;
|
|
126
|
+
declare function concatTemplates(left: TypeValue, right: TypeValue): TypeValue;
|
|
127
|
+
|
|
128
|
+
type RangeMeta = {
|
|
129
|
+
min?: number;
|
|
130
|
+
max?: number;
|
|
131
|
+
integer?: boolean;
|
|
132
|
+
};
|
|
133
|
+
declare function createRange(meta: RangeMeta): TypeValue;
|
|
134
|
+
declare function isRange(tv: TypeValue): boolean;
|
|
135
|
+
declare function getRangeMeta(tv: TypeValue): RangeMeta | undefined;
|
|
103
136
|
|
|
104
|
-
export { type Environment, type LiteralValue, Ops, T, type TypeValue, applyBinaryOp, createEnvironment, deepCloneTypeValue, getPrimitiveTypeOf, isSubtypeOf, mergeObjectProperties, narrowType, simplifyUnion, subtractType, typeValueEquals, typeValueToString, widenLiteral };
|
|
137
|
+
export { type Environment, type LiteralValue, Ops, type RangeMeta, type Refinement, T, type TypeValue, applyBinaryOp, concatTemplates, createEnvironment, createRange, createTemplate, deepCloneTypeValue, dispatchBinaryOp, dispatchMethod, dispatchProperty, getKnownPrefix, getKnownSuffix, getPrimitiveTypeOf, getRangeMeta, getRefinedBase, getTemplateParts, isRange, isSubtypeOf, isTemplate, mergeObjectProperties, narrowType, simplifyUnion, subtractType, typeValueEquals, typeValueToString, widenLiteral };
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,11 @@ var T = {
|
|
|
32
32
|
params,
|
|
33
33
|
body,
|
|
34
34
|
closure
|
|
35
|
+
}),
|
|
36
|
+
refine: (base, refinement) => ({
|
|
37
|
+
kind: "refined",
|
|
38
|
+
base,
|
|
39
|
+
refinement
|
|
35
40
|
})
|
|
36
41
|
};
|
|
37
42
|
function typeValueEquals(a, b) {
|
|
@@ -40,6 +45,9 @@ function typeValueEquals(a, b) {
|
|
|
40
45
|
if (a.kind === "primitive" && b.kind === "primitive") return a.type === b.type;
|
|
41
46
|
if (a.kind === "never" && b.kind === "never") return true;
|
|
42
47
|
if (a.kind === "unknown" && b.kind === "unknown") return true;
|
|
48
|
+
if (a.kind === "refined" && b.kind === "refined") {
|
|
49
|
+
return a.refinement.name === b.refinement.name && typeValueEquals(a.base, b.base);
|
|
50
|
+
}
|
|
43
51
|
if (a.kind === "promise" && b.kind === "promise") {
|
|
44
52
|
return typeValueEquals(a.value, b.value);
|
|
45
53
|
}
|
|
@@ -93,6 +101,16 @@ function isSubtypeOf(a, b) {
|
|
|
93
101
|
if (b.type === "boolean" && typeof v === "boolean") return true;
|
|
94
102
|
return false;
|
|
95
103
|
}
|
|
104
|
+
if (a.kind === "literal" && b.kind === "refined") {
|
|
105
|
+
if (!isSubtypeOf(a, b.base)) return false;
|
|
106
|
+
return b.refinement.check ? b.refinement.check(a.value) : false;
|
|
107
|
+
}
|
|
108
|
+
if (a.kind === "refined") {
|
|
109
|
+
if (b.kind === "refined" && a.refinement.name === b.refinement.name) {
|
|
110
|
+
return isSubtypeOf(a.base, b.base);
|
|
111
|
+
}
|
|
112
|
+
return isSubtypeOf(a.base, b);
|
|
113
|
+
}
|
|
96
114
|
if (a.kind === "object" && b.kind === "object") {
|
|
97
115
|
return Object.entries(b.properties).every(
|
|
98
116
|
([k, bv]) => k in a.properties && isSubtypeOf(a.properties[k], bv)
|
|
@@ -164,6 +182,9 @@ function deepCloneTypeValue(tv, idMap) {
|
|
|
164
182
|
}
|
|
165
183
|
return { kind: "instance", className: tv.className, properties: newProps };
|
|
166
184
|
}
|
|
185
|
+
if (tv.kind === "refined") {
|
|
186
|
+
return { kind: "refined", base: deepCloneTypeValue(tv.base, map), refinement: tv.refinement };
|
|
187
|
+
}
|
|
167
188
|
if (tv.kind === "union") {
|
|
168
189
|
return simplifyUnion(tv.members.map((m) => deepCloneTypeValue(m, map)));
|
|
169
190
|
}
|
|
@@ -194,6 +215,8 @@ function typeValueToString(tv) {
|
|
|
194
215
|
}
|
|
195
216
|
case "primitive":
|
|
196
217
|
return tv.type;
|
|
218
|
+
case "refined":
|
|
219
|
+
return tv.refinement.name;
|
|
197
220
|
case "object": {
|
|
198
221
|
const entries = Object.entries(tv.properties);
|
|
199
222
|
if (entries.length === 0) return "{}";
|
|
@@ -243,6 +266,7 @@ function getPrimitiveTypeOf(tv) {
|
|
|
243
266
|
return typeof v;
|
|
244
267
|
}
|
|
245
268
|
if (tv.kind === "primitive") return tv.type;
|
|
269
|
+
if (tv.kind === "refined") return getPrimitiveTypeOf(tv.base);
|
|
246
270
|
if (tv.kind === "object") return "object";
|
|
247
271
|
if (tv.kind === "array" || tv.kind === "tuple") return "object";
|
|
248
272
|
if (tv.kind === "function") return "function";
|
|
@@ -250,6 +274,237 @@ function getPrimitiveTypeOf(tv) {
|
|
|
250
274
|
if (tv.kind === "instance") return "object";
|
|
251
275
|
return void 0;
|
|
252
276
|
}
|
|
277
|
+
function getRefinedBase(tv) {
|
|
278
|
+
return tv.kind === "refined" ? getRefinedBase(tv.base) : tv;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/refinements/range.ts
|
|
282
|
+
function formatRangeName(meta) {
|
|
283
|
+
const parts = [];
|
|
284
|
+
if (meta.integer) parts.push("integer");
|
|
285
|
+
else parts.push("number");
|
|
286
|
+
const constraints = [];
|
|
287
|
+
if (meta.min != null) constraints.push(`>= ${meta.min}`);
|
|
288
|
+
if (meta.max != null) constraints.push(`<= ${meta.max}`);
|
|
289
|
+
if (constraints.length > 0) parts.push(`(${constraints.join(", ")})`);
|
|
290
|
+
return parts.join(" ");
|
|
291
|
+
}
|
|
292
|
+
function createRangeRefinement(meta) {
|
|
293
|
+
return {
|
|
294
|
+
name: formatRangeName(meta),
|
|
295
|
+
meta: { ...meta },
|
|
296
|
+
check(value) {
|
|
297
|
+
if (typeof value !== "number") return false;
|
|
298
|
+
if (meta.integer && !Number.isInteger(value)) return false;
|
|
299
|
+
if (meta.min != null && value < meta.min) return false;
|
|
300
|
+
if (meta.max != null && value > meta.max) return false;
|
|
301
|
+
return true;
|
|
302
|
+
},
|
|
303
|
+
ops: {
|
|
304
|
+
">="(self, other) {
|
|
305
|
+
const m = getRangeMeta(self);
|
|
306
|
+
if (!m || other.kind !== "literal" || typeof other.value !== "number") return void 0;
|
|
307
|
+
if (m.min != null && m.min >= other.value) return T.literal(true);
|
|
308
|
+
if (m.max != null && m.max < other.value) return T.literal(false);
|
|
309
|
+
return void 0;
|
|
310
|
+
},
|
|
311
|
+
">"(self, other) {
|
|
312
|
+
const m = getRangeMeta(self);
|
|
313
|
+
if (!m || other.kind !== "literal" || typeof other.value !== "number") return void 0;
|
|
314
|
+
if (m.min != null && m.min > other.value) return T.literal(true);
|
|
315
|
+
if (m.max != null && m.max <= other.value) return T.literal(false);
|
|
316
|
+
return void 0;
|
|
317
|
+
},
|
|
318
|
+
"<="(self, other) {
|
|
319
|
+
const m = getRangeMeta(self);
|
|
320
|
+
if (!m || other.kind !== "literal" || typeof other.value !== "number") return void 0;
|
|
321
|
+
if (m.max != null && m.max <= other.value) return T.literal(true);
|
|
322
|
+
if (m.min != null && m.min > other.value) return T.literal(false);
|
|
323
|
+
return void 0;
|
|
324
|
+
},
|
|
325
|
+
"<"(self, other) {
|
|
326
|
+
const m = getRangeMeta(self);
|
|
327
|
+
if (!m || other.kind !== "literal" || typeof other.value !== "number") return void 0;
|
|
328
|
+
if (m.max != null && m.max < other.value) return T.literal(true);
|
|
329
|
+
if (m.min != null && m.min >= other.value) return T.literal(false);
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function createRange(meta) {
|
|
336
|
+
if (meta.min != null && meta.max != null && meta.min === meta.max) {
|
|
337
|
+
return T.literal(meta.min);
|
|
338
|
+
}
|
|
339
|
+
return T.refine(T.number, createRangeRefinement(meta));
|
|
340
|
+
}
|
|
341
|
+
function isRange(tv) {
|
|
342
|
+
if (tv.kind !== "refined") return false;
|
|
343
|
+
const m = tv.refinement.meta;
|
|
344
|
+
return m.min !== void 0 || m.max !== void 0 || m.integer !== void 0;
|
|
345
|
+
}
|
|
346
|
+
function getRangeMeta(tv) {
|
|
347
|
+
if (tv.kind !== "refined") return void 0;
|
|
348
|
+
const m = tv.refinement.meta;
|
|
349
|
+
if (m.min !== void 0 || m.max !== void 0 || m.integer !== void 0) {
|
|
350
|
+
return m;
|
|
351
|
+
}
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/refinements/template.ts
|
|
356
|
+
function getKnownPrefix(parts) {
|
|
357
|
+
let prefix = "";
|
|
358
|
+
for (const p of parts) {
|
|
359
|
+
if (p.kind === "literal" && typeof p.value === "string") {
|
|
360
|
+
prefix += p.value;
|
|
361
|
+
} else {
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return prefix;
|
|
366
|
+
}
|
|
367
|
+
function getKnownSuffix(parts) {
|
|
368
|
+
let suffix = "";
|
|
369
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
370
|
+
const p = parts[i];
|
|
371
|
+
if (p.kind === "literal" && typeof p.value === "string") {
|
|
372
|
+
suffix = p.value + suffix;
|
|
373
|
+
} else {
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return suffix;
|
|
378
|
+
}
|
|
379
|
+
function getFixedLength(parts) {
|
|
380
|
+
let len = 0;
|
|
381
|
+
for (const p of parts) {
|
|
382
|
+
if (p.kind === "literal" && typeof p.value === "string") {
|
|
383
|
+
len += p.value.length;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return len;
|
|
387
|
+
}
|
|
388
|
+
function getAllFixedText(parts) {
|
|
389
|
+
return parts.filter((p) => p.kind === "literal" && typeof p.value === "string").map((p) => p.value).join("");
|
|
390
|
+
}
|
|
391
|
+
function formatTemplateName(parts) {
|
|
392
|
+
const inner = parts.map((p) => p.kind === "literal" && typeof p.value === "string" ? p.value : `\${${typeValueToString(p)}}`).join("");
|
|
393
|
+
return `\`${inner}\``;
|
|
394
|
+
}
|
|
395
|
+
function normalizeParts(parts) {
|
|
396
|
+
const result = [];
|
|
397
|
+
for (const p of parts) {
|
|
398
|
+
const last = result[result.length - 1];
|
|
399
|
+
if (last?.kind === "literal" && typeof last.value === "string" && p.kind === "literal" && typeof p.value === "string") {
|
|
400
|
+
result[result.length - 1] = T.literal(last.value + p.value);
|
|
401
|
+
} else if (last?.kind === "primitive" && last.type === "string" && p.kind === "primitive" && p.type === "string") {
|
|
402
|
+
} else {
|
|
403
|
+
result.push(p);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
function createTemplateRefinement(parts) {
|
|
409
|
+
return {
|
|
410
|
+
name: formatTemplateName(parts),
|
|
411
|
+
meta: { parts },
|
|
412
|
+
check(value) {
|
|
413
|
+
if (typeof value !== "string") return false;
|
|
414
|
+
return matchesTemplate(value, parts);
|
|
415
|
+
},
|
|
416
|
+
ops: {
|
|
417
|
+
"+"(self, other) {
|
|
418
|
+
return concatTemplates(self, other);
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
methods: {
|
|
422
|
+
startsWith(_self, args) {
|
|
423
|
+
const arg = args[0];
|
|
424
|
+
if (arg?.kind !== "literal" || typeof arg.value !== "string") return void 0;
|
|
425
|
+
const prefix = getKnownPrefix(_self.refinement.meta.parts);
|
|
426
|
+
const search = arg.value;
|
|
427
|
+
if (prefix.length >= search.length) {
|
|
428
|
+
return T.literal(prefix.startsWith(search));
|
|
429
|
+
}
|
|
430
|
+
if (search.startsWith(prefix)) return void 0;
|
|
431
|
+
return T.literal(false);
|
|
432
|
+
},
|
|
433
|
+
endsWith(_self, args) {
|
|
434
|
+
const arg = args[0];
|
|
435
|
+
if (arg?.kind !== "literal" || typeof arg.value !== "string") return void 0;
|
|
436
|
+
const suffix = getKnownSuffix(_self.refinement.meta.parts);
|
|
437
|
+
const search = arg.value;
|
|
438
|
+
if (suffix.length >= search.length) {
|
|
439
|
+
return T.literal(suffix.endsWith(search));
|
|
440
|
+
}
|
|
441
|
+
if (search.endsWith(suffix)) return void 0;
|
|
442
|
+
return T.literal(false);
|
|
443
|
+
},
|
|
444
|
+
includes(_self, args) {
|
|
445
|
+
const arg = args[0];
|
|
446
|
+
if (arg?.kind !== "literal" || typeof arg.value !== "string") return void 0;
|
|
447
|
+
const fixed = getAllFixedText(_self.refinement.meta.parts);
|
|
448
|
+
if (fixed.includes(arg.value)) return T.literal(true);
|
|
449
|
+
return void 0;
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
properties: {
|
|
453
|
+
length(_self) {
|
|
454
|
+
const parts2 = _self.refinement.meta.parts;
|
|
455
|
+
const hasAbstract = parts2.some((p) => p.kind !== "literal");
|
|
456
|
+
if (!hasAbstract) return void 0;
|
|
457
|
+
const minLen = getFixedLength(parts2);
|
|
458
|
+
return createRange({ min: minLen });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function matchesTemplate(value, parts) {
|
|
464
|
+
let pos = 0;
|
|
465
|
+
for (let i = 0; i < parts.length; i++) {
|
|
466
|
+
const p = parts[i];
|
|
467
|
+
if (p.kind === "literal" && typeof p.value === "string") {
|
|
468
|
+
if (!value.startsWith(p.value, pos)) return false;
|
|
469
|
+
pos += p.value.length;
|
|
470
|
+
} else {
|
|
471
|
+
if (i === parts.length - 1) return true;
|
|
472
|
+
const next = parts[i + 1];
|
|
473
|
+
if (next?.kind === "literal" && typeof next.value === "string") {
|
|
474
|
+
const idx = value.indexOf(next.value, pos);
|
|
475
|
+
if (idx === -1) return false;
|
|
476
|
+
pos = idx;
|
|
477
|
+
} else {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return pos === value.length;
|
|
483
|
+
}
|
|
484
|
+
function createTemplate(parts) {
|
|
485
|
+
const normalized = normalizeParts(parts);
|
|
486
|
+
if (normalized.length === 1 && normalized[0].kind === "literal") {
|
|
487
|
+
return normalized[0];
|
|
488
|
+
}
|
|
489
|
+
if (normalized.length === 1 && normalized[0].kind === "primitive" && normalized[0].type === "string") {
|
|
490
|
+
return T.string;
|
|
491
|
+
}
|
|
492
|
+
return T.refine(T.string, createTemplateRefinement(normalized));
|
|
493
|
+
}
|
|
494
|
+
function isTemplate(tv) {
|
|
495
|
+
return tv.kind === "refined" && Array.isArray(tv.refinement.meta.parts);
|
|
496
|
+
}
|
|
497
|
+
function getTemplateParts(tv) {
|
|
498
|
+
if (tv.kind === "refined" && Array.isArray(tv.refinement.meta.parts)) {
|
|
499
|
+
return tv.refinement.meta.parts;
|
|
500
|
+
}
|
|
501
|
+
return void 0;
|
|
502
|
+
}
|
|
503
|
+
function concatTemplates(left, right) {
|
|
504
|
+
const leftParts = getTemplateParts(left) ?? [left];
|
|
505
|
+
const rightParts = getTemplateParts(right) ?? [right];
|
|
506
|
+
return createTemplate([...leftParts, ...rightParts]);
|
|
507
|
+
}
|
|
253
508
|
|
|
254
509
|
// src/ops.ts
|
|
255
510
|
function bothLiteral(l, r) {
|
|
@@ -264,7 +519,13 @@ var Ops = {
|
|
|
264
519
|
if (lit) {
|
|
265
520
|
return T.literal(lit.lv + lit.rv);
|
|
266
521
|
}
|
|
267
|
-
|
|
522
|
+
const leftIsString = isSubtypeOf(left, T.string) || isTemplate(left);
|
|
523
|
+
const rightIsString = isSubtypeOf(right, T.string) || isTemplate(right);
|
|
524
|
+
if (leftIsString || rightIsString) {
|
|
525
|
+
const hasStructure = left.kind === "literal" && typeof left.value === "string" || right.kind === "literal" && typeof right.value === "string" || isTemplate(left) || isTemplate(right);
|
|
526
|
+
if (hasStructure) {
|
|
527
|
+
return concatTemplates(left, right);
|
|
528
|
+
}
|
|
268
529
|
return T.string;
|
|
269
530
|
}
|
|
270
531
|
if (isSubtypeOf(left, T.number) && isSubtypeOf(right, T.number)) {
|
|
@@ -337,6 +598,7 @@ var Ops = {
|
|
|
337
598
|
return T.literal(typeof v);
|
|
338
599
|
}
|
|
339
600
|
if (operand.kind === "primitive") return T.literal(operand.type);
|
|
601
|
+
if (operand.kind === "refined") return Ops.typeof_(getRefinedBase(operand));
|
|
340
602
|
if (operand.kind === "object") return T.literal("object");
|
|
341
603
|
if (operand.kind === "array" || operand.kind === "tuple")
|
|
342
604
|
return T.literal("object");
|
|
@@ -374,6 +636,42 @@ function applyBinaryOp(op, left, right) {
|
|
|
374
636
|
if (!fn) return T.unknown;
|
|
375
637
|
return fn(left, right);
|
|
376
638
|
}
|
|
639
|
+
function dispatchBinaryOp(op, left, right) {
|
|
640
|
+
if (left.kind === "refined" && left.refinement.ops?.[op]) {
|
|
641
|
+
const result = left.refinement.ops[op](left, right);
|
|
642
|
+
if (result !== void 0) return result;
|
|
643
|
+
}
|
|
644
|
+
if (right.kind === "refined" && right.refinement.ops?.[op]) {
|
|
645
|
+
const result = right.refinement.ops[op](right, left);
|
|
646
|
+
if (result !== void 0) return result;
|
|
647
|
+
}
|
|
648
|
+
const baseLeft = left.kind === "refined" ? left.base : left;
|
|
649
|
+
const baseRight = right.kind === "refined" ? right.base : right;
|
|
650
|
+
if (baseLeft !== left || baseRight !== right) {
|
|
651
|
+
return dispatchBinaryOp(op, baseLeft, baseRight);
|
|
652
|
+
}
|
|
653
|
+
return applyBinaryOp(op, left, right);
|
|
654
|
+
}
|
|
655
|
+
function dispatchMethod(receiver, name, args) {
|
|
656
|
+
if (receiver.kind === "refined" && receiver.refinement.methods?.[name]) {
|
|
657
|
+
const result = receiver.refinement.methods[name](receiver, args);
|
|
658
|
+
if (result !== void 0) return result;
|
|
659
|
+
}
|
|
660
|
+
if (receiver.kind === "refined") {
|
|
661
|
+
return dispatchMethod(receiver.base, name, args);
|
|
662
|
+
}
|
|
663
|
+
return void 0;
|
|
664
|
+
}
|
|
665
|
+
function dispatchProperty(receiver, name) {
|
|
666
|
+
if (receiver.kind === "refined" && receiver.refinement.properties?.[name]) {
|
|
667
|
+
const result = receiver.refinement.properties[name](receiver);
|
|
668
|
+
if (result !== void 0) return result;
|
|
669
|
+
}
|
|
670
|
+
if (receiver.kind === "refined") {
|
|
671
|
+
return dispatchProperty(receiver.base, name);
|
|
672
|
+
}
|
|
673
|
+
return void 0;
|
|
674
|
+
}
|
|
377
675
|
|
|
378
676
|
// src/environment.ts
|
|
379
677
|
function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
|
|
@@ -426,10 +724,23 @@ export {
|
|
|
426
724
|
Ops,
|
|
427
725
|
T,
|
|
428
726
|
applyBinaryOp,
|
|
727
|
+
concatTemplates,
|
|
429
728
|
createEnvironment,
|
|
729
|
+
createRange,
|
|
730
|
+
createTemplate,
|
|
430
731
|
deepCloneTypeValue,
|
|
732
|
+
dispatchBinaryOp,
|
|
733
|
+
dispatchMethod,
|
|
734
|
+
dispatchProperty,
|
|
735
|
+
getKnownPrefix,
|
|
736
|
+
getKnownSuffix,
|
|
431
737
|
getPrimitiveTypeOf,
|
|
738
|
+
getRangeMeta,
|
|
739
|
+
getRefinedBase,
|
|
740
|
+
getTemplateParts,
|
|
741
|
+
isRange,
|
|
432
742
|
isSubtypeOf,
|
|
743
|
+
isTemplate,
|
|
433
744
|
mergeObjectProperties,
|
|
434
745
|
narrowType,
|
|
435
746
|
simplifyUnion,
|
package/package.json
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.ts"
|
|
8
8
|
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/nudojs/nudo.git",
|
|
12
|
+
"directory": "packages/core"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public",
|
|
16
|
+
"provenance": true,
|
|
17
|
+
"registry": "https://registry.npmjs.org"
|
|
18
|
+
},
|
|
9
19
|
"scripts": {
|
|
10
20
|
"build": "tsup src/index.ts --format esm --dts"
|
|
11
21
|
}
|