@mionjs/run-types 0.8.8 → 0.8.10

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.
Files changed (56) hide show
  1. package/.dist/cjs/src/createRunType.cjs +3 -1
  2. package/.dist/cjs/src/createRunType.cjs.map +1 -1
  3. package/.dist/cjs/src/createRunType.d.ts.map +1 -1
  4. package/.dist/cjs/src/jitCompilers/binary/fromBinary.cjs +1 -1
  5. package/.dist/cjs/src/jitCompilers/binary/fromBinary.cjs.map +1 -1
  6. package/.dist/cjs/src/jitCompilers/binary/fromBinary.d.ts.map +1 -1
  7. package/.dist/cjs/src/jitCompilers/binary/toBinary.cjs +1 -1
  8. package/.dist/cjs/src/jitCompilers/binary/toBinary.cjs.map +1 -1
  9. package/.dist/cjs/src/jitCompilers/binary/toBinary.d.ts.map +1 -1
  10. package/.dist/cjs/src/jitCompilers/json/stringifyJson.cjs +1 -1
  11. package/.dist/cjs/src/jitCompilers/json/stringifyJson.cjs.map +1 -1
  12. package/.dist/cjs/src/jitCompilers/json/stringifyJson.d.ts.map +1 -1
  13. package/.dist/cjs/src/mocking/mockType.cjs +46 -1
  14. package/.dist/cjs/src/mocking/mockType.cjs.map +1 -1
  15. package/.dist/cjs/src/nodes/collection/templateLiteral.cjs +73 -0
  16. package/.dist/cjs/src/nodes/collection/templateLiteral.cjs.map +1 -0
  17. package/.dist/cjs/src/nodes/collection/templateLiteral.d.ts +16 -0
  18. package/.dist/cjs/src/nodes/collection/templateLiteral.d.ts.map +1 -0
  19. package/.dist/cjs/src/nodes/member/indexProperty.cjs +69 -22
  20. package/.dist/cjs/src/nodes/member/indexProperty.cjs.map +1 -1
  21. package/.dist/cjs/src/nodes/member/indexProperty.d.ts +1 -0
  22. package/.dist/cjs/src/nodes/member/indexProperty.d.ts.map +1 -1
  23. package/.dist/esm/src/createRunType.d.ts.map +1 -1
  24. package/.dist/esm/src/createRunType.js +3 -1
  25. package/.dist/esm/src/createRunType.js.map +1 -1
  26. package/.dist/esm/src/jitCompilers/binary/fromBinary.d.ts.map +1 -1
  27. package/.dist/esm/src/jitCompilers/binary/fromBinary.js +1 -1
  28. package/.dist/esm/src/jitCompilers/binary/fromBinary.js.map +1 -1
  29. package/.dist/esm/src/jitCompilers/binary/toBinary.d.ts.map +1 -1
  30. package/.dist/esm/src/jitCompilers/binary/toBinary.js +1 -1
  31. package/.dist/esm/src/jitCompilers/binary/toBinary.js.map +1 -1
  32. package/.dist/esm/src/jitCompilers/json/stringifyJson.d.ts.map +1 -1
  33. package/.dist/esm/src/jitCompilers/json/stringifyJson.js +1 -1
  34. package/.dist/esm/src/jitCompilers/json/stringifyJson.js.map +1 -1
  35. package/.dist/esm/src/mocking/mockType.js +47 -2
  36. package/.dist/esm/src/mocking/mockType.js.map +1 -1
  37. package/.dist/esm/src/mocking/mockUtils.js +1 -1
  38. package/.dist/esm/src/nodes/collection/templateLiteral.d.ts +16 -0
  39. package/.dist/esm/src/nodes/collection/templateLiteral.d.ts.map +1 -0
  40. package/.dist/esm/src/nodes/collection/templateLiteral.js +73 -0
  41. package/.dist/esm/src/nodes/collection/templateLiteral.js.map +1 -0
  42. package/.dist/esm/src/nodes/member/indexProperty.d.ts +1 -0
  43. package/.dist/esm/src/nodes/member/indexProperty.d.ts.map +1 -1
  44. package/.dist/esm/src/nodes/member/indexProperty.js +70 -23
  45. package/.dist/esm/src/nodes/member/indexProperty.js.map +1 -1
  46. package/package.json +3 -3
  47. package/src/createRunType.ts +5 -6
  48. package/src/jitCompilers/binary/fromBinary.ts +2 -1
  49. package/src/jitCompilers/binary/toBinary.ts +2 -1
  50. package/src/jitCompilers/json/stringifyJson.ts +2 -1
  51. package/src/jitCompilers/serialization-suite.ts +52 -0
  52. package/src/jitCompilers/xyz-Template/fromXYZ.ts +2 -1
  53. package/src/jitCompilers/xyz-Template/toXYZ.ts +2 -1
  54. package/src/mocking/mockType.ts +56 -1
  55. package/src/nodes/collection/templateLiteral.ts +87 -0
  56. package/src/nodes/member/indexProperty.ts +66 -17
@@ -0,0 +1,87 @@
1
+ /* ########
2
+ * 2025 mion
3
+ * Author: Ma-jerez
4
+ * License: MIT
5
+ * The software is provided "as is", without warranty of any kind.
6
+ * ######## */
7
+
8
+ import {ReflectionKind, type TypeTemplateLiteral, type TypeLiteral} from '@deepkit/type';
9
+ import type {JitFnCompiler, JitErrorsFnCompiler} from '../../lib/jitFnCompiler.ts';
10
+ import type {JitCode} from '../../types.ts';
11
+ import {CollectionRunType} from '../../lib/baseRunTypes.ts';
12
+
13
+ /**
14
+ * RunType for TypeScript template literal types, ie: `type T = \`api/user/${number}\``.
15
+ * The runtime value is a string. Validation is done by compiling the template literal type
16
+ * to a single anchored regex at JIT-build time and then calling `regex.test(v)` at runtime.
17
+ * Mocking walks the children directly (literal => verbatim, string/any => mockString, number => mockNumber)
18
+ * and concatenates the spans, which guarantees the result satisfies the validation regex.
19
+ */
20
+ export class TemplateLiteralRunType extends CollectionRunType<TypeTemplateLiteral> {
21
+ private _regexSource: string | undefined;
22
+
23
+ /** Builds the anchored regex source from src.types. Memoized. */
24
+ getRegexSource(): string {
25
+ if (this._regexSource !== undefined) return this._regexSource;
26
+ this._regexSource = buildAnchoredTemplateRegexSource(this.src.types || []);
27
+ return this._regexSource;
28
+ }
29
+
30
+ /** Returns a context-bound variable name holding the RegExp; emits the const into the compiler context. */
31
+ private getRegexVar(comp: JitFnCompiler): string {
32
+ const varName = comp.getLocalVarName('reTL', this);
33
+ if (!comp.hasContextItem(varName)) {
34
+ comp.setContextItem(varName, `const ${varName} = new RegExp(${JSON.stringify(this.getRegexSource())})`);
35
+ }
36
+ return varName;
37
+ }
38
+
39
+ emitIsType(comp: JitFnCompiler): JitCode {
40
+ const re = this.getRegexVar(comp);
41
+ return {code: `(typeof ${comp.vλl} === 'string' && ${re}.test(${comp.vλl}))`, type: 'E'};
42
+ }
43
+ emitTypeErrors(comp: JitErrorsFnCompiler): JitCode {
44
+ const re = this.getRegexVar(comp);
45
+ return {
46
+ code: `if (typeof ${comp.vλl} !== 'string' || !${re}.test(${comp.vλl})) ${comp.callJitErr(this)}`,
47
+ type: 'S',
48
+ };
49
+ }
50
+ /** value is already a JSON-safe string, no transform required */
51
+ emitPrepareForJson(): JitCode {
52
+ return {code: undefined, type: 'S'};
53
+ }
54
+ /** value is already a JSON-safe string, no transform required */
55
+ emitRestoreFromJson(): JitCode {
56
+ return {code: undefined, type: 'S'};
57
+ }
58
+ }
59
+
60
+ /** Build the full ^...$ regex source for a template literal type's spans */
61
+ export function buildAnchoredTemplateRegexSource(types: TypeTemplateLiteral['types']): string {
62
+ return `^${types.map((t) => spanToRegex(t)).join('')}$`;
63
+ }
64
+
65
+ /** Translate a single template-literal span (TypeString | TypeAny | TypeNumber | TypeLiteral | TypeInfer) into regex source */
66
+ export function spanToRegex(t: TypeTemplateLiteral['types'][number]): string {
67
+ switch (t.kind) {
68
+ case ReflectionKind.literal:
69
+ return escapeForRegex(String((t as TypeLiteral).literal));
70
+ case ReflectionKind.number:
71
+ // matches signed integers and floats (including leading dot like '.5'), mirroring TS's `${number}` semantics
72
+ return '-?(?:\\d+\\.?\\d*|\\.\\d+)';
73
+ case ReflectionKind.string:
74
+ case ReflectionKind.any:
75
+ case ReflectionKind.infer:
76
+ // `${string}` accepts the empty string, so use * not +
77
+ return '[\\s\\S]*';
78
+ default:
79
+ // unreachable per deepkit's TypeTemplateLiteral.types definition
80
+ throw new Error(`Unsupported template literal span kind: ${(t as {kind: number}).kind}`);
81
+ }
82
+ }
83
+
84
+ /** Escape regex metacharacters so a literal substring is matched verbatim */
85
+ function escapeForRegex(s: string): string {
86
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
87
+ }
@@ -1,10 +1,11 @@
1
- import {ReflectionKind, TypeIndexSignature} from '@deepkit/type';
1
+ import {ReflectionKind, type TypeIndexSignature, type TypeTemplateLiteral} from '@deepkit/type';
2
2
  import {MemberRunType} from '../../lib/baseRunTypes.ts';
3
3
  import {type JitCode} from '../../types.ts';
4
4
  import {JitFunctions} from '../../constants.functions.ts';
5
5
  import type {JitFnCompiler, JitErrorsFnCompiler} from '../../lib/jitFnCompiler.ts';
6
6
  import {InterfaceRunType} from '../collection/interface.ts';
7
7
  import {childIsExpression} from '../../lib/utils.ts';
8
+ import {buildAnchoredTemplateRegexSource} from '../collection/templateLiteral.ts';
8
9
 
9
10
  /* ########
10
11
  * 2024 mion
@@ -34,21 +35,44 @@ export class IndexSignatureRunType extends MemberRunType<TypeIndexSignature> {
34
35
  return false;
35
36
  }
36
37
 
38
+ /** if the index key is a template literal type, return the JIT context var holding the compiled key-pattern regex */
39
+ private getKeyPatternVar(comp: JitFnCompiler): string | undefined {
40
+ const idx = this.src.index;
41
+ if (idx?.kind !== ReflectionKind.templateLiteral) return undefined;
42
+ const varName = comp.getLocalVarName('reIdx', this);
43
+ if (!comp.hasContextItem(varName)) {
44
+ const src = buildAnchoredTemplateRegexSource((idx as TypeTemplateLiteral).types || []);
45
+ comp.setContextItem(varName, `const ${varName} = new RegExp(${JSON.stringify(src)})`);
46
+ }
47
+ return varName;
48
+ }
49
+
37
50
  // #### jit code ####
38
51
  emitIsType(comp: JitFnCompiler): JitCode {
39
52
  const child = this.getJitChild(comp);
40
53
  const childJit = comp.compileIsType(child, 'E');
41
- if (!childJit?.code) return {code: undefined, type: 'E'};
54
+ const prop = this.getChildVarName(comp);
55
+ const reVar = this.getKeyPatternVar(comp);
56
+ const skipCode = this.getSkipCode(comp, prop);
57
+ const keyCheck = reVar ? `if (!${reVar}.test(${prop})) return false;` : '';
58
+ if (!childJit?.code && !keyCheck) return {code: undefined, type: 'E'};
59
+ const valueCheck = childJit?.code ? `if (!(${childJit.code})) return false;` : '';
42
60
  return {
43
- code: `for (const ${this.getChildVarName(comp)} in ${comp.vλl}){if (!(${childJit.code})) return false;} return true;`,
61
+ code: `for (const ${prop} in ${comp.vλl}){${skipCode} ${keyCheck} ${valueCheck}} return true;`,
44
62
  type: 'RB',
45
63
  };
46
64
  }
47
65
  emitTypeErrors(comp: JitErrorsFnCompiler): JitCode {
48
66
  const child = this.getJitChild(comp);
49
67
  const childJit = comp.compileTypeErrors(child, 'S');
50
- if (!childJit?.code) return {code: undefined, type: 'S'};
51
- return {code: `for (const ${this.getChildVarName(comp)} in ${comp.vλl}) {${childJit.code}}`, type: 'S'};
68
+ const prop = this.getChildVarName(comp);
69
+ const reVar = this.getKeyPatternVar(comp);
70
+ const skipCode = this.getSkipCode(comp, prop);
71
+ // when the key fails the template literal pattern, report it (with the offending key in the path)
72
+ // and skip the value check to avoid compounding errors on values whose key was already invalid
73
+ const keyErr = reVar ? `if (!${reVar}.test(${prop})) {${comp.callJitErrWithPath(this, prop)}; continue;}` : '';
74
+ if (!childJit?.code && !keyErr) return {code: undefined, type: 'S'};
75
+ return {code: `for (const ${prop} in ${comp.vλl}) {${skipCode} ${keyErr} ${childJit?.code || ''}}`, type: 'S'};
52
76
  }
53
77
  emitPrepareForJson(comp: JitFnCompiler): JitCode {
54
78
  const child = this.getJitChild(comp);
@@ -57,9 +81,11 @@ export class IndexSignatureRunType extends MemberRunType<TypeIndexSignature> {
57
81
  const varName = comp.vλl;
58
82
  const prop = this.getChildVarName(comp);
59
83
  const skipCode = this.getSkipCode(comp, prop);
84
+ const reVar = this.getKeyPatternVar(comp);
85
+ const patternSkip = reVar ? `if (!${reVar}.test(${prop})) continue;` : '';
60
86
  const isExpression = childIsExpression(childJit, child);
61
87
  const code = isExpression ? `${comp.getChildVλl()} = ${childJit.code};` : childJit.code || '';
62
- return {code: `for (const ${prop} in ${varName}){${skipCode} ${code}}`, type: 'S'};
88
+ return {code: `for (const ${prop} in ${varName}){${skipCode} ${patternSkip} ${code}}`, type: 'S'};
63
89
  }
64
90
  emitRestoreFromJson(comp: JitFnCompiler): JitCode {
65
91
  const child = this.getJitChild(comp);
@@ -68,40 +94,63 @@ export class IndexSignatureRunType extends MemberRunType<TypeIndexSignature> {
68
94
  const varName = comp.vλl;
69
95
  const prop = this.getChildVarName(comp);
70
96
  const skipCode = this.getSkipCode(comp, prop);
97
+ const reVar = this.getKeyPatternVar(comp);
98
+ const patternSkip = reVar ? `if (!${reVar}.test(${prop})) continue;` : '';
71
99
  const isExpression = childIsExpression(childJit, child);
72
100
  const code = isExpression ? `${comp.getChildVλl()} = ${childJit.code};` : childJit.code || '';
73
- return {code: `for (const ${prop} in ${varName}){${skipCode} ${code}}`, type: 'S'};
101
+ return {code: `for (const ${prop} in ${varName}){${skipCode} ${patternSkip} ${code}}`, type: 'S'};
74
102
  }
75
103
  emitHasUnknownKeys(comp: JitFnCompiler): JitCode {
76
- if (this.getMemberType().getFamily() === 'A') return {code: undefined, type: 'E'};
104
+ const reVar = this.getKeyPatternVar(comp);
77
105
  const child = this.getJitChild(comp);
78
106
  const childJit = comp.compileHasUnknownKeys(child, 'E');
79
- if (!childJit?.code) return {code: '', type: 'E'};
107
+ // when the value is atomic and there's no key pattern, every key is "known" -> no check needed
108
+ if (this.getMemberType().getFamily() === 'A' && !reVar) return {code: undefined, type: 'E'};
80
109
  const varName = comp.vλl;
81
110
  const prop = this.getChildVarName(comp);
82
- const resultVal = comp.getLocalVarName('res', this);
111
+ const skipCode = this.getSkipCode(comp, prop);
112
+ const patternCheck = reVar ? `if (!${reVar}.test(${prop})) return true;` : '';
113
+ const childCheck = childJit?.code
114
+ ? `const ${comp.getLocalVarName('res', this)} = ${childJit.code};if (${comp.getLocalVarName('res', this)}) return true;`
115
+ : '';
116
+ if (!patternCheck && !childCheck) return {code: '', type: 'E'};
83
117
  return {
84
- code: `for (const ${prop} in ${varName}) {const ${resultVal} = ${childJit.code};if (${resultVal}) return true;}return false;`,
118
+ code: `for (const ${prop} in ${varName}) {${skipCode} ${patternCheck} ${childCheck}}return false;`,
85
119
  type: 'RB',
86
120
  };
87
121
  }
88
122
  emitUnknownKeyErrors(comp: JitErrorsFnCompiler): JitCode {
89
- if (this.getMemberType().getFamily() === 'A') return {code: undefined, type: 'S'};
123
+ const reVar = this.getKeyPatternVar(comp);
90
124
  const child = this.getJitChild(comp);
91
125
  const childJit = comp.compileUnknownKeyErrors(child, 'S');
92
- return this.traverseCode(comp, childJit);
126
+ if (this.getMemberType().getFamily() === 'A' && !reVar) return {code: undefined, type: 'S'};
127
+ const prop = this.getChildVarName(comp);
128
+ const skipCode = this.getSkipCode(comp, prop);
129
+ const patternErr = reVar ? `if (!${reVar}.test(${prop})) {${comp.callJitErrWithPath('never', prop)}; continue;}` : '';
130
+ if (!patternErr && !childJit?.code) return {code: undefined, type: 'S'};
131
+ return {code: `for (const ${prop} in ${comp.vλl}) {${skipCode} ${patternErr} ${childJit?.code || ''}}`, type: 'S'};
93
132
  }
94
133
  emitStripUnknownKeys(comp: JitFnCompiler): JitCode {
95
- if (this.getMemberType().getFamily() === 'A') return {code: undefined, type: 'S'};
134
+ const reVar = this.getKeyPatternVar(comp);
96
135
  const child = this.getJitChild(comp);
97
136
  const childJit = comp.compileStripUnknownKeys(child, 'S');
98
- return this.traverseCode(comp, childJit);
137
+ if (this.getMemberType().getFamily() === 'A' && !reVar) return {code: undefined, type: 'S'};
138
+ const prop = this.getChildVarName(comp);
139
+ const skipCode = this.getSkipCode(comp, prop);
140
+ const patternStrip = reVar ? `if (!${reVar}.test(${prop})) {delete ${comp.vλl}[${prop}]; continue;}` : '';
141
+ if (!patternStrip && !childJit?.code) return {code: undefined, type: 'S'};
142
+ return {code: `for (const ${prop} in ${comp.vλl}) {${skipCode} ${patternStrip} ${childJit?.code || ''}}`, type: 'S'};
99
143
  }
100
144
  emitUnknownKeysToUndefined(comp: JitFnCompiler): JitCode {
101
- if (this.getMemberType().getFamily() === 'A') return {code: undefined, type: 'S'};
145
+ const reVar = this.getKeyPatternVar(comp);
102
146
  const child = this.getJitChild(comp);
103
147
  const childJit = comp.compileUnknownKeysToUndefined(child, 'S');
104
- return this.traverseCode(comp, childJit);
148
+ if (this.getMemberType().getFamily() === 'A' && !reVar) return {code: undefined, type: 'S'};
149
+ const prop = this.getChildVarName(comp);
150
+ const skipCode = this.getSkipCode(comp, prop);
151
+ const patternUndef = reVar ? `if (!${reVar}.test(${prop})) {${comp.vλl}[${prop}] = undefined; continue;}` : '';
152
+ if (!patternUndef && !childJit?.code) return {code: undefined, type: 'S'};
153
+ return {code: `for (const ${prop} in ${comp.vλl}) {${skipCode} ${patternUndef} ${childJit?.code || ''}}`, type: 'S'};
105
154
  }
106
155
  traverseCode(comp: JitFnCompiler, childJit: JitCode | undefined): JitCode {
107
156
  if (!childJit?.code) return {code: undefined, type: 'S'};