@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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @nudojs/core
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6c38283: docs and ci
8
+ - 9f7f819: fix pkg info
9
+ - c175f71: version
10
+
3
11
  ## 0.1.0
4
12
 
5
13
  ### Minor Changes
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
- if (isSubtypeOf(left, T.string) || isSubtypeOf(right, T.string)) {
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.1.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
  }