@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.
- package/.dist/cjs/src/createRunType.cjs +3 -1
- package/.dist/cjs/src/createRunType.cjs.map +1 -1
- package/.dist/cjs/src/createRunType.d.ts.map +1 -1
- package/.dist/cjs/src/jitCompilers/binary/fromBinary.cjs +1 -1
- package/.dist/cjs/src/jitCompilers/binary/fromBinary.cjs.map +1 -1
- package/.dist/cjs/src/jitCompilers/binary/fromBinary.d.ts.map +1 -1
- package/.dist/cjs/src/jitCompilers/binary/toBinary.cjs +1 -1
- package/.dist/cjs/src/jitCompilers/binary/toBinary.cjs.map +1 -1
- package/.dist/cjs/src/jitCompilers/binary/toBinary.d.ts.map +1 -1
- package/.dist/cjs/src/jitCompilers/json/stringifyJson.cjs +1 -1
- package/.dist/cjs/src/jitCompilers/json/stringifyJson.cjs.map +1 -1
- package/.dist/cjs/src/jitCompilers/json/stringifyJson.d.ts.map +1 -1
- package/.dist/cjs/src/mocking/mockType.cjs +46 -1
- package/.dist/cjs/src/mocking/mockType.cjs.map +1 -1
- package/.dist/cjs/src/nodes/collection/templateLiteral.cjs +73 -0
- package/.dist/cjs/src/nodes/collection/templateLiteral.cjs.map +1 -0
- package/.dist/cjs/src/nodes/collection/templateLiteral.d.ts +16 -0
- package/.dist/cjs/src/nodes/collection/templateLiteral.d.ts.map +1 -0
- package/.dist/cjs/src/nodes/member/indexProperty.cjs +69 -22
- package/.dist/cjs/src/nodes/member/indexProperty.cjs.map +1 -1
- package/.dist/cjs/src/nodes/member/indexProperty.d.ts +1 -0
- package/.dist/cjs/src/nodes/member/indexProperty.d.ts.map +1 -1
- package/.dist/esm/src/createRunType.d.ts.map +1 -1
- package/.dist/esm/src/createRunType.js +3 -1
- package/.dist/esm/src/createRunType.js.map +1 -1
- package/.dist/esm/src/jitCompilers/binary/fromBinary.d.ts.map +1 -1
- package/.dist/esm/src/jitCompilers/binary/fromBinary.js +1 -1
- package/.dist/esm/src/jitCompilers/binary/fromBinary.js.map +1 -1
- package/.dist/esm/src/jitCompilers/binary/toBinary.d.ts.map +1 -1
- package/.dist/esm/src/jitCompilers/binary/toBinary.js +1 -1
- package/.dist/esm/src/jitCompilers/binary/toBinary.js.map +1 -1
- package/.dist/esm/src/jitCompilers/json/stringifyJson.d.ts.map +1 -1
- package/.dist/esm/src/jitCompilers/json/stringifyJson.js +1 -1
- package/.dist/esm/src/jitCompilers/json/stringifyJson.js.map +1 -1
- package/.dist/esm/src/mocking/mockType.js +47 -2
- package/.dist/esm/src/mocking/mockType.js.map +1 -1
- package/.dist/esm/src/mocking/mockUtils.js +1 -1
- package/.dist/esm/src/nodes/collection/templateLiteral.d.ts +16 -0
- package/.dist/esm/src/nodes/collection/templateLiteral.d.ts.map +1 -0
- package/.dist/esm/src/nodes/collection/templateLiteral.js +73 -0
- package/.dist/esm/src/nodes/collection/templateLiteral.js.map +1 -0
- package/.dist/esm/src/nodes/member/indexProperty.d.ts +1 -0
- package/.dist/esm/src/nodes/member/indexProperty.d.ts.map +1 -1
- package/.dist/esm/src/nodes/member/indexProperty.js +70 -23
- package/.dist/esm/src/nodes/member/indexProperty.js.map +1 -1
- package/package.json +3 -3
- package/src/createRunType.ts +5 -6
- package/src/jitCompilers/binary/fromBinary.ts +2 -1
- package/src/jitCompilers/binary/toBinary.ts +2 -1
- package/src/jitCompilers/json/stringifyJson.ts +2 -1
- package/src/jitCompilers/serialization-suite.ts +52 -0
- package/src/jitCompilers/xyz-Template/fromXYZ.ts +2 -1
- package/src/jitCompilers/xyz-Template/toXYZ.ts +2 -1
- package/src/mocking/mockType.ts +56 -1
- package/src/nodes/collection/templateLiteral.ts +87 -0
- 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
|
-
|
|
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 ${
|
|
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
|
-
|
|
51
|
-
|
|
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
|
-
|
|
104
|
+
const reVar = this.getKeyPatternVar(comp);
|
|
77
105
|
const child = this.getJitChild(comp);
|
|
78
106
|
const childJit = comp.compileHasUnknownKeys(child, 'E');
|
|
79
|
-
|
|
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
|
|
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}) {
|
|
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
|
-
|
|
123
|
+
const reVar = this.getKeyPatternVar(comp);
|
|
90
124
|
const child = this.getJitChild(comp);
|
|
91
125
|
const childJit = comp.compileUnknownKeyErrors(child, 'S');
|
|
92
|
-
|
|
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
|
-
|
|
134
|
+
const reVar = this.getKeyPatternVar(comp);
|
|
96
135
|
const child = this.getJitChild(comp);
|
|
97
136
|
const childJit = comp.compileStripUnknownKeys(child, 'S');
|
|
98
|
-
|
|
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
|
-
|
|
145
|
+
const reVar = this.getKeyPatternVar(comp);
|
|
102
146
|
const child = this.getJitChild(comp);
|
|
103
147
|
const childJit = comp.compileUnknownKeysToUndefined(child, 'S');
|
|
104
|
-
|
|
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'};
|