@mionjs/devtools 0.8.7 → 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/build/eslint/cjs/index.cjs.map +1 -1
- package/build/eslint/cjs/src/eslint/index.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/index.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.d.ts.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.d.ts +1 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.d.ts.map +1 -0
- package/build/eslint/cjs/src/pureFns/purityRules.d.ts +1 -0
- package/build/eslint/cjs/src/pureFns/purityRules.d.ts.map +1 -0
- package/build/eslint/esm/index.js.map +1 -1
- package/build/eslint/esm/src/eslint/index.d.ts +1 -0
- package/build/eslint/esm/src/eslint/index.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.d.ts.map +1 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.d.ts +1 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.d.ts.map +1 -0
- package/build/eslint/esm/src/pureFns/purityRules.d.ts +1 -0
- package/build/eslint/esm/src/pureFns/purityRules.d.ts.map +1 -0
- package/build/vite-plugin/cjs/index.cjs +0 -1
- package/build/vite-plugin/cjs/index.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/pureFns/purityRules.d.ts +1 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs +0 -1
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/vite-plugin/index.d.ts +2 -1
- package/build/vite-plugin/cjs/src/vite-plugin/index.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.d.ts.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.d.ts.map +1 -0
- package/build/vite-plugin/esm/index.js +2 -3
- package/build/vite-plugin/esm/src/pureFns/purityRules.d.ts +1 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.d.ts +2 -1
- package/build/vite-plugin/esm/src/vite-plugin/index.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.js +2 -3
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js.map +1 -1
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.d.ts.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.d.ts.map +1 -0
- package/package.json +5 -3
- package/src/eslint/index.ts +53 -0
- package/src/eslint/rules/enforce-type-imports.ts +157 -0
- package/src/eslint/rules/formatTypeNames.ts +92 -0
- package/src/eslint/rules/no-mixed-union-properties.ts +276 -0
- package/src/eslint/rules/no-type-imports.ts +386 -0
- package/src/eslint/rules/no-typeof-runtype.ts +112 -0
- package/src/eslint/rules/no-unreachable-union-types.ts +357 -0
- package/src/eslint/rules/no-vite-client.ts +101 -0
- package/src/eslint/rules/pure-functions.ts +523 -0
- package/src/eslint/rules/strong-typed-routes.ts +537 -0
- package/src/eslint/rules/type-formats-imports.ts +73 -0
- package/src/pureFns/purityRules.ts +76 -0
- package/src/vite-plugin/README.md +399 -0
- package/src/vite-plugin/aotCacheGenerator.ts +397 -0
- package/src/vite-plugin/aotDiskCache.ts +193 -0
- package/src/vite-plugin/cjsPackageJsonPlugin.ts +21 -0
- package/src/vite-plugin/constants.ts +50 -0
- package/src/vite-plugin/extractPureFn.ts +845 -0
- package/src/vite-plugin/index.ts +27 -0
- package/src/vite-plugin/mionVitePlugin.ts +738 -0
- package/src/vite-plugin/resolveModule.ts +50 -0
- package/src/vite-plugin/transformers.ts +209 -0
- package/src/vite-plugin/types.ts +182 -0
- package/src/vite-plugin/virtualModule.ts +94 -0
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/* ########
|
|
2
|
+
* 2024 mion
|
|
3
|
+
* Author: Ma-jerez
|
|
4
|
+
* License: MIT
|
|
5
|
+
* The software is provided "as is", without warranty of any kind.
|
|
6
|
+
* ######## */
|
|
7
|
+
|
|
8
|
+
import {TSESTree, TSESLint, AST_NODE_TYPES} from '@typescript-eslint/utils';
|
|
9
|
+
|
|
10
|
+
// List of router functions that should have strongly typed handlers
|
|
11
|
+
const ROUTER_FUNCTIONS = ['route', 'middleFn', 'headersFn'] as const;
|
|
12
|
+
// List of handler types that can be used with type annotations
|
|
13
|
+
const HANDLER_TYPES = ['Handler', 'HeaderHandler'] as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Cache for imports from @mionjs/router - computed once per file
|
|
17
|
+
*/
|
|
18
|
+
interface MionRouterImports {
|
|
19
|
+
/** Set of function names imported from @mionjs/router (route, middleFn, headersFn) */
|
|
20
|
+
routerFunctions: Set<string>;
|
|
21
|
+
/** Set of type names imported from @mionjs/router (Handler, HeaderHandler) */
|
|
22
|
+
handlerTypes: Set<string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Builds a cache of all imports from @mionjs/router
|
|
27
|
+
* This is called once per file in the Program visitor
|
|
28
|
+
*/
|
|
29
|
+
function buildImportCache(program: TSESTree.Program): MionRouterImports {
|
|
30
|
+
const routerFunctions = new Set<string>();
|
|
31
|
+
const handlerTypes = new Set<string>();
|
|
32
|
+
|
|
33
|
+
for (const statement of program.body) {
|
|
34
|
+
if (statement.type === AST_NODE_TYPES.ImportDeclaration) {
|
|
35
|
+
const source = statement.source.value;
|
|
36
|
+
// Check for @mionjs/router package
|
|
37
|
+
if (source === '@mionjs/router' || source === '@mionjs/router/') {
|
|
38
|
+
for (const specifier of statement.specifiers) {
|
|
39
|
+
if (
|
|
40
|
+
specifier.type === AST_NODE_TYPES.ImportSpecifier &&
|
|
41
|
+
specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
42
|
+
) {
|
|
43
|
+
const name = specifier.imported.name;
|
|
44
|
+
if (ROUTER_FUNCTIONS.includes(name as (typeof ROUTER_FUNCTIONS)[number])) {
|
|
45
|
+
routerFunctions.add(name);
|
|
46
|
+
}
|
|
47
|
+
if (HANDLER_TYPES.includes(name as (typeof HANDLER_TYPES)[number])) {
|
|
48
|
+
handlerTypes.add(name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {routerFunctions, handlerTypes};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Checks if a call expression is calling router functions from @mionjs/router
|
|
61
|
+
* Uses the cached import information for performance
|
|
62
|
+
*/
|
|
63
|
+
function getRouterFunctionName(node: TSESTree.CallExpression, importCache: MionRouterImports): string | null {
|
|
64
|
+
// Check if the callee is an identifier with one of the router function names
|
|
65
|
+
if (node.callee.type !== AST_NODE_TYPES.Identifier) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const functionName = node.callee.name;
|
|
70
|
+
|
|
71
|
+
// Check if this function is imported from @mionjs/router
|
|
72
|
+
if (importCache.routerFunctions.has(functionName)) {
|
|
73
|
+
return functionName;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Cache for top-level function declarations and variable function expressions
|
|
81
|
+
* This avoids iterating through the program body for each function reference
|
|
82
|
+
*/
|
|
83
|
+
type FunctionCache = Map<string, TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Builds a cache of all top-level function declarations and function variable assignments
|
|
87
|
+
* This is called once per file in the Program visitor
|
|
88
|
+
*/
|
|
89
|
+
function buildFunctionCache(program: TSESTree.Program): FunctionCache {
|
|
90
|
+
const cache: FunctionCache = new Map();
|
|
91
|
+
|
|
92
|
+
for (const statement of program.body) {
|
|
93
|
+
// Check function declarations
|
|
94
|
+
if (statement.type === AST_NODE_TYPES.FunctionDeclaration && statement.id?.name) {
|
|
95
|
+
cache.set(statement.id.name, statement);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check variable declarations
|
|
99
|
+
if (statement.type === AST_NODE_TYPES.VariableDeclaration) {
|
|
100
|
+
for (const declarator of statement.declarations) {
|
|
101
|
+
if (declarator.id.type === AST_NODE_TYPES.Identifier) {
|
|
102
|
+
if (
|
|
103
|
+
declarator.init?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
104
|
+
declarator.init?.type === AST_NODE_TYPES.FunctionExpression
|
|
105
|
+
) {
|
|
106
|
+
cache.set(declarator.id.name, declarator.init);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return cache;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Gets the handler function from a router function call
|
|
118
|
+
* Uses the cached function information for performance
|
|
119
|
+
*/
|
|
120
|
+
function getHandlerFunction(
|
|
121
|
+
node: TSESTree.CallExpression,
|
|
122
|
+
functionCache: FunctionCache
|
|
123
|
+
): TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration | null {
|
|
124
|
+
// Handler is always the first parameter for route, middleFn, and headersFn
|
|
125
|
+
const handlerIndex = 0;
|
|
126
|
+
|
|
127
|
+
if (node.arguments.length <= handlerIndex) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const handlerArg = node.arguments[handlerIndex];
|
|
132
|
+
|
|
133
|
+
// Handle inline function expressions/arrow functions
|
|
134
|
+
if (handlerArg.type === AST_NODE_TYPES.ArrowFunctionExpression || handlerArg.type === AST_NODE_TYPES.FunctionExpression) {
|
|
135
|
+
return handlerArg;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Handle function references (identifiers) - use cached function lookup
|
|
139
|
+
if (handlerArg.type === AST_NODE_TYPES.Identifier) {
|
|
140
|
+
return functionCache.get(handlerArg.name) ?? null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Checks if a function has an explicit return type annotation
|
|
148
|
+
* @param func The function node
|
|
149
|
+
* @returns True if it has explicit return type, false otherwise
|
|
150
|
+
*/
|
|
151
|
+
function hasExplicitReturnType(
|
|
152
|
+
func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration
|
|
153
|
+
): boolean {
|
|
154
|
+
return func.returnType !== undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Checks if a node represents a primitive literal value
|
|
159
|
+
* @param node The node to check
|
|
160
|
+
* @returns True if the node is a primitive literal (boolean, string, number, null, undefined, bigint)
|
|
161
|
+
*/
|
|
162
|
+
function isPrimitiveLiteral(node: TSESTree.Node): boolean {
|
|
163
|
+
// Check for boolean, string, number, null, or bigint literals
|
|
164
|
+
if (node.type === AST_NODE_TYPES.Literal) {
|
|
165
|
+
const value = node.value;
|
|
166
|
+
// BigInt literals have a 'bigint' property and value is null
|
|
167
|
+
if ('bigint' in node) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return typeof value === 'boolean' || typeof value === 'string' || typeof value === 'number' || value === null;
|
|
171
|
+
}
|
|
172
|
+
// Check for undefined identifier
|
|
173
|
+
if (node.type === AST_NODE_TYPES.Identifier && node.name === 'undefined') {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
// Check for negative numbers (UnaryExpression with -)
|
|
177
|
+
if (node.type === AST_NODE_TYPES.UnaryExpression && node.operator === '-' && node.argument.type === AST_NODE_TYPES.Literal) {
|
|
178
|
+
return typeof node.argument.value === 'number';
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Checks if all parameters (except the first) have explicit type annotations
|
|
185
|
+
* @param func The function node
|
|
186
|
+
* @returns Object with validation results including the actual parameter nodes
|
|
187
|
+
*/
|
|
188
|
+
function validateParameterTypes(
|
|
189
|
+
func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration
|
|
190
|
+
): {
|
|
191
|
+
valid: boolean;
|
|
192
|
+
missingTypeParams: string[];
|
|
193
|
+
missingParamNodes: TSESTree.Parameter[];
|
|
194
|
+
} {
|
|
195
|
+
const missingTypeParams: string[] = [];
|
|
196
|
+
const missingParamNodes: TSESTree.Parameter[] = [];
|
|
197
|
+
|
|
198
|
+
// Skip the first parameter (context), check all others
|
|
199
|
+
for (let i = 1; i < func.params.length; i++) {
|
|
200
|
+
const param = func.params[i];
|
|
201
|
+
|
|
202
|
+
if (param.type === AST_NODE_TYPES.Identifier) {
|
|
203
|
+
if (!param.typeAnnotation) {
|
|
204
|
+
missingTypeParams.push(param.name);
|
|
205
|
+
missingParamNodes.push(param);
|
|
206
|
+
}
|
|
207
|
+
} else if (param.type === AST_NODE_TYPES.RestElement) {
|
|
208
|
+
if (param.argument.type === AST_NODE_TYPES.Identifier && !param.typeAnnotation) {
|
|
209
|
+
missingTypeParams.push(`...${param.argument.name}`);
|
|
210
|
+
missingParamNodes.push(param);
|
|
211
|
+
}
|
|
212
|
+
} else if (param.type === AST_NODE_TYPES.ArrayPattern || param.type === AST_NODE_TYPES.ObjectPattern) {
|
|
213
|
+
// For destructuring patterns, check if they have type annotations
|
|
214
|
+
if (!param.typeAnnotation) {
|
|
215
|
+
missingTypeParams.push(`parameter ${i + 1}`);
|
|
216
|
+
missingParamNodes.push(param);
|
|
217
|
+
}
|
|
218
|
+
} else if (param.type === AST_NODE_TYPES.AssignmentPattern) {
|
|
219
|
+
// For default parameters (e.g., `name = 'default'` or `name: string = 'default'`), check if:
|
|
220
|
+
// 1. There's an explicit type annotation on the AssignmentPattern itself, OR
|
|
221
|
+
// 2. There's an explicit type annotation on the left side (for `name: Type = value` pattern), OR
|
|
222
|
+
// 3. The default value is a primitive literal (type can be inferred)
|
|
223
|
+
const hasTypeAnnotationOnPattern = param.typeAnnotation !== undefined;
|
|
224
|
+
const hasTypeAnnotationOnLeft =
|
|
225
|
+
param.left.type === AST_NODE_TYPES.Identifier && param.left.typeAnnotation !== undefined;
|
|
226
|
+
const hasTypeAnnotation = hasTypeAnnotationOnPattern || hasTypeAnnotationOnLeft;
|
|
227
|
+
const hasPrimitiveDefault = isPrimitiveLiteral(param.right);
|
|
228
|
+
|
|
229
|
+
if (!hasTypeAnnotation && !hasPrimitiveDefault) {
|
|
230
|
+
// Need explicit type annotation since type cannot be inferred from default value
|
|
231
|
+
const paramName = param.left.type === AST_NODE_TYPES.Identifier ? param.left.name : `parameter ${i + 1}`;
|
|
232
|
+
missingTypeParams.push(paramName);
|
|
233
|
+
missingParamNodes.push(param);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// For other parameter types, we assume they need type annotations too
|
|
237
|
+
else if (!('typeAnnotation' in param) || !param.typeAnnotation) {
|
|
238
|
+
missingTypeParams.push(`parameter ${i + 1}`);
|
|
239
|
+
missingParamNodes.push(param);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
valid: missingTypeParams.length === 0,
|
|
245
|
+
missingTypeParams,
|
|
246
|
+
missingParamNodes,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Extracts a readable parameter name from a parameter node
|
|
252
|
+
*/
|
|
253
|
+
function getParameterName(param: TSESTree.Parameter): string {
|
|
254
|
+
if (param.type === AST_NODE_TYPES.Identifier) {
|
|
255
|
+
return param.name;
|
|
256
|
+
} else if (param.type === AST_NODE_TYPES.RestElement && param.argument.type === AST_NODE_TYPES.Identifier) {
|
|
257
|
+
return `...${param.argument.name}`;
|
|
258
|
+
} else if (param.type === AST_NODE_TYPES.ArrayPattern) {
|
|
259
|
+
return '[...]';
|
|
260
|
+
} else if (param.type === AST_NODE_TYPES.ObjectPattern) {
|
|
261
|
+
return '{...}';
|
|
262
|
+
} else if (param.type === AST_NODE_TYPES.AssignmentPattern) {
|
|
263
|
+
if (param.left.type === AST_NODE_TYPES.Identifier) {
|
|
264
|
+
return param.left.name;
|
|
265
|
+
}
|
|
266
|
+
return 'param';
|
|
267
|
+
}
|
|
268
|
+
return 'param';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Gets the node to report for missing return type
|
|
273
|
+
* For arrow functions, this is the arrow token area; for regular functions, the function keyword
|
|
274
|
+
*/
|
|
275
|
+
function getReturnTypeReportNode(
|
|
276
|
+
func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration
|
|
277
|
+
): TSESTree.Node {
|
|
278
|
+
// For function declarations/expressions with an id, report on the id
|
|
279
|
+
if ((func.type === AST_NODE_TYPES.FunctionDeclaration || func.type === AST_NODE_TYPES.FunctionExpression) && func.id) {
|
|
280
|
+
return func.id;
|
|
281
|
+
}
|
|
282
|
+
// Otherwise report on the function itself (will highlight just the signature area)
|
|
283
|
+
return func;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Checks if a type annotation references Handler or HeaderHandler from @mionjs/router
|
|
288
|
+
* Uses the cached import information for performance
|
|
289
|
+
*/
|
|
290
|
+
function getHandlerTypeFromAnnotation(
|
|
291
|
+
typeAnnotation: TSESTree.TSTypeAnnotation,
|
|
292
|
+
importCache: MionRouterImports
|
|
293
|
+
): 'Handler' | 'HeaderHandler' | null {
|
|
294
|
+
if (typeAnnotation.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {
|
|
295
|
+
const typeName = typeAnnotation.typeAnnotation.typeName;
|
|
296
|
+
if (typeName.type === AST_NODE_TYPES.Identifier) {
|
|
297
|
+
const name = typeName.name;
|
|
298
|
+
if ((name === 'Handler' || name === 'HeaderHandler') && importCache.handlerTypes.has(name)) {
|
|
299
|
+
return name as 'Handler' | 'HeaderHandler';
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Checks if a satisfies expression references Handler or HeaderHandler from @mionjs/router
|
|
308
|
+
* Uses the cached import information for performance
|
|
309
|
+
*/
|
|
310
|
+
function getHandlerTypeFromSatisfies(
|
|
311
|
+
satisfiesExpression: TSESTree.TSSatisfiesExpression,
|
|
312
|
+
importCache: MionRouterImports
|
|
313
|
+
): 'Handler' | 'HeaderHandler' | null {
|
|
314
|
+
if (satisfiesExpression.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {
|
|
315
|
+
const typeName = satisfiesExpression.typeAnnotation.typeName;
|
|
316
|
+
if (typeName.type === AST_NODE_TYPES.Identifier) {
|
|
317
|
+
const name = typeName.name;
|
|
318
|
+
if ((name === 'Handler' || name === 'HeaderHandler') && importCache.handlerTypes.has(name)) {
|
|
319
|
+
return name as 'Handler' | 'HeaderHandler';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Checks if a function has JSDoc tags indicating it should be type-checked
|
|
328
|
+
* @param node The node to check for JSDoc comments
|
|
329
|
+
* @param context The ESLint context
|
|
330
|
+
* @returns The handler type if JSDoc tag is found, null otherwise
|
|
331
|
+
*/
|
|
332
|
+
function getHandlerTypeFromJSDoc(
|
|
333
|
+
node:
|
|
334
|
+
| TSESTree.ArrowFunctionExpression
|
|
335
|
+
| TSESTree.FunctionExpression
|
|
336
|
+
| TSESTree.FunctionDeclaration
|
|
337
|
+
| TSESTree.VariableDeclaration,
|
|
338
|
+
context: TSESLint.RuleContext<any, any>
|
|
339
|
+
): 'Handler' | 'HeaderHandler' | 'MiddleFnHandler' | null {
|
|
340
|
+
const sourceCode = context.sourceCode;
|
|
341
|
+
const comments = sourceCode.getCommentsBefore(node);
|
|
342
|
+
|
|
343
|
+
for (const comment of comments) {
|
|
344
|
+
if (comment.type === 'Block') {
|
|
345
|
+
const commentText = comment.value;
|
|
346
|
+
if (commentText.includes('@mion:route')) {
|
|
347
|
+
return 'Handler';
|
|
348
|
+
}
|
|
349
|
+
if (commentText.includes('@mion:middleFn')) {
|
|
350
|
+
return 'MiddleFnHandler';
|
|
351
|
+
}
|
|
352
|
+
if (commentText.includes('@mion:headersFn')) {
|
|
353
|
+
return 'HeaderHandler';
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const rule: TSESLint.RuleModule<
|
|
361
|
+
'missingReturnType' | 'missingParamTypes' | 'missingReturnTypeRouter' | 'missingParamTypesRouter',
|
|
362
|
+
[]
|
|
363
|
+
> = {
|
|
364
|
+
meta: {
|
|
365
|
+
type: 'problem',
|
|
366
|
+
docs: {
|
|
367
|
+
description: 'Enforce explicit parameters and return type annotations for router handler functions',
|
|
368
|
+
},
|
|
369
|
+
messages: {
|
|
370
|
+
missingReturnType: 'mion {{handlerType}}() handler must define a return type.',
|
|
371
|
+
missingParamTypes: 'mion parameter "{{paramName}}" must have an explicit type definition.',
|
|
372
|
+
missingReturnTypeRouter: 'mion {{routerFunction}}() handler must define a return type.',
|
|
373
|
+
missingParamTypesRouter: 'mion parameter "{{paramName}}" must have an explicit type definition.',
|
|
374
|
+
},
|
|
375
|
+
schema: [], // No options
|
|
376
|
+
},
|
|
377
|
+
defaultOptions: [],
|
|
378
|
+
create(context) {
|
|
379
|
+
// Caches built once per file for performance
|
|
380
|
+
let importCache: MionRouterImports | null = null;
|
|
381
|
+
let functionCache: FunctionCache | null = null;
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
// Build caches once when we start processing the file
|
|
385
|
+
Program(node: TSESTree.Program) {
|
|
386
|
+
importCache = buildImportCache(node);
|
|
387
|
+
functionCache = buildFunctionCache(node);
|
|
388
|
+
},
|
|
389
|
+
CallExpression(node: TSESTree.CallExpression) {
|
|
390
|
+
if (!importCache || !functionCache) return;
|
|
391
|
+
|
|
392
|
+
const functionName = getRouterFunctionName(node, importCache);
|
|
393
|
+
if (!functionName) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const handlerFunc = getHandlerFunction(node, functionCache);
|
|
398
|
+
if (!handlerFunc) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const hasReturnType = hasExplicitReturnType(handlerFunc);
|
|
403
|
+
const paramValidation = validateParameterTypes(handlerFunc);
|
|
404
|
+
|
|
405
|
+
// Report errors on specific nodes for better highlighting
|
|
406
|
+
if (!hasReturnType) {
|
|
407
|
+
context.report({
|
|
408
|
+
node: getReturnTypeReportNode(handlerFunc),
|
|
409
|
+
messageId: 'missingReturnTypeRouter',
|
|
410
|
+
data: {
|
|
411
|
+
routerFunction: functionName,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Report each missing parameter type separately
|
|
417
|
+
for (const paramNode of paramValidation.missingParamNodes) {
|
|
418
|
+
context.report({
|
|
419
|
+
node: paramNode,
|
|
420
|
+
messageId: 'missingParamTypesRouter',
|
|
421
|
+
data: {
|
|
422
|
+
paramName: getParameterName(paramNode),
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
// Check variable declarations with type annotations or JSDoc tags
|
|
428
|
+
VariableDeclarator(node: TSESTree.VariableDeclarator) {
|
|
429
|
+
if (!importCache) return;
|
|
430
|
+
|
|
431
|
+
if (node.id.type === AST_NODE_TYPES.Identifier) {
|
|
432
|
+
let handlerType: 'Handler' | 'HeaderHandler' | 'MiddleFnHandler' | null = null;
|
|
433
|
+
|
|
434
|
+
// Check for type annotation
|
|
435
|
+
if (node.id.typeAnnotation) {
|
|
436
|
+
handlerType = getHandlerTypeFromAnnotation(node.id.typeAnnotation, importCache);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Check for JSDoc tags on the variable declaration
|
|
440
|
+
if (!handlerType && node.parent?.type === AST_NODE_TYPES.VariableDeclaration) {
|
|
441
|
+
handlerType = getHandlerTypeFromJSDoc(node.parent, context);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (
|
|
445
|
+
handlerType &&
|
|
446
|
+
(node.init?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
447
|
+
node.init?.type === AST_NODE_TYPES.FunctionExpression)
|
|
448
|
+
) {
|
|
449
|
+
checkHandlerFunction(node.init, handlerType, context);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
// Check satisfies expressions
|
|
454
|
+
TSSatisfiesExpression(node: TSESTree.TSSatisfiesExpression) {
|
|
455
|
+
if (!importCache) return;
|
|
456
|
+
|
|
457
|
+
const handlerType = getHandlerTypeFromSatisfies(node, importCache);
|
|
458
|
+
if (
|
|
459
|
+
handlerType &&
|
|
460
|
+
(node.expression.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
461
|
+
node.expression.type === AST_NODE_TYPES.FunctionExpression)
|
|
462
|
+
) {
|
|
463
|
+
checkHandlerFunction(node.expression, handlerType, context);
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
// Check function declarations with JSDoc tags
|
|
467
|
+
FunctionDeclaration(node: TSESTree.FunctionDeclaration) {
|
|
468
|
+
const handlerType = getHandlerTypeFromJSDoc(node, context);
|
|
469
|
+
if (handlerType) {
|
|
470
|
+
checkHandlerFunction(node, handlerType, context);
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
// Check arrow functions and function expressions with JSDoc tags
|
|
474
|
+
ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression) {
|
|
475
|
+
const handlerType = getHandlerTypeFromJSDoc(node, context);
|
|
476
|
+
if (handlerType) {
|
|
477
|
+
checkHandlerFunction(node, handlerType, context);
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
FunctionExpression(node: TSESTree.FunctionExpression) {
|
|
481
|
+
const handlerType = getHandlerTypeFromJSDoc(node, context);
|
|
482
|
+
if (handlerType) {
|
|
483
|
+
checkHandlerFunction(node, handlerType, context);
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Maps Handler/HeaderHandler/MiddleFnHandler type names to their corresponding router function names
|
|
492
|
+
*/
|
|
493
|
+
function handlerTypeToFunctionName(handlerType: 'Handler' | 'HeaderHandler' | 'MiddleFnHandler'): string {
|
|
494
|
+
if (handlerType === 'HeaderHandler') return 'headersFn';
|
|
495
|
+
if (handlerType === 'MiddleFnHandler') return 'middleFn';
|
|
496
|
+
return 'route';
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Helper function to check a handler function for type annotations
|
|
501
|
+
* @param func The function node to check
|
|
502
|
+
* @param handlerType The expected handler type
|
|
503
|
+
* @param context The ESLint context
|
|
504
|
+
*/
|
|
505
|
+
function checkHandlerFunction(
|
|
506
|
+
func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration,
|
|
507
|
+
handlerType: 'Handler' | 'HeaderHandler' | 'MiddleFnHandler',
|
|
508
|
+
context: TSESLint.RuleContext<any, any>
|
|
509
|
+
) {
|
|
510
|
+
const hasReturnType = hasExplicitReturnType(func);
|
|
511
|
+
const paramValidation = validateParameterTypes(func);
|
|
512
|
+
const functionName = handlerTypeToFunctionName(handlerType);
|
|
513
|
+
|
|
514
|
+
// Report errors on specific nodes for better highlighting
|
|
515
|
+
if (!hasReturnType) {
|
|
516
|
+
context.report({
|
|
517
|
+
node: getReturnTypeReportNode(func),
|
|
518
|
+
messageId: 'missingReturnType',
|
|
519
|
+
data: {
|
|
520
|
+
handlerType: functionName,
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Report each missing parameter type separately
|
|
526
|
+
for (const paramNode of paramValidation.missingParamNodes) {
|
|
527
|
+
context.report({
|
|
528
|
+
node: paramNode,
|
|
529
|
+
messageId: 'missingParamTypes',
|
|
530
|
+
data: {
|
|
531
|
+
paramName: getParameterName(paramNode),
|
|
532
|
+
},
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export default rule;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* ########
|
|
2
|
+
* 2026 mion
|
|
3
|
+
* Author: Ma-jerez
|
|
4
|
+
* License: MIT
|
|
5
|
+
* The software is provided "as is", without warranty of any kind.
|
|
6
|
+
* ######## */
|
|
7
|
+
|
|
8
|
+
import {TSESTree, TSESLint, AST_NODE_TYPES} from '@typescript-eslint/utils';
|
|
9
|
+
import {FORMAT_TYPES_BY_PACKAGE} from './formatTypeNames.ts';
|
|
10
|
+
|
|
11
|
+
/** Checks if source matches one of the format type packages (exact or subpath) */
|
|
12
|
+
function getFormatTypePackage(source: string): string | null {
|
|
13
|
+
for (const [pkg] of FORMAT_TYPES_BY_PACKAGE) {
|
|
14
|
+
if (source === pkg || source.startsWith(pkg + '/')) return pkg;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const rule: TSESLint.RuleModule<'typeFormatsImports', []> = {
|
|
20
|
+
meta: {
|
|
21
|
+
type: 'problem',
|
|
22
|
+
docs: {
|
|
23
|
+
description:
|
|
24
|
+
'Disallow type-only imports for TypeFormat types. Type-only imports strip type metadata needed by Deepkit for runtime type reflection, causing silent validation/serialization failures.',
|
|
25
|
+
},
|
|
26
|
+
messages: {
|
|
27
|
+
typeFormatsImports:
|
|
28
|
+
'Type "{{typeName}}" is imported as type-only from "{{source}}". Remove the "type" keyword from the import to preserve runtime type metadata for Deepkit reflection.',
|
|
29
|
+
},
|
|
30
|
+
schema: [],
|
|
31
|
+
},
|
|
32
|
+
defaultOptions: [],
|
|
33
|
+
create(context) {
|
|
34
|
+
return {
|
|
35
|
+
ImportDeclaration(node: TSESTree.ImportDeclaration) {
|
|
36
|
+
const source = node.source.value;
|
|
37
|
+
const matchedPkg = getFormatTypePackage(source);
|
|
38
|
+
if (!matchedPkg) return;
|
|
39
|
+
|
|
40
|
+
const formatTypes = FORMAT_TYPES_BY_PACKAGE.get(matchedPkg);
|
|
41
|
+
if (!formatTypes) return;
|
|
42
|
+
|
|
43
|
+
const isTypeOnlyImport = node.importKind === 'type';
|
|
44
|
+
|
|
45
|
+
for (const specifier of node.specifiers) {
|
|
46
|
+
if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) continue;
|
|
47
|
+
|
|
48
|
+
const importedName =
|
|
49
|
+
specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
50
|
+
? specifier.imported.name
|
|
51
|
+
: specifier.imported.value;
|
|
52
|
+
|
|
53
|
+
if (!formatTypes.has(importedName)) continue;
|
|
54
|
+
|
|
55
|
+
const isTypeOnlySpecifier = specifier.importKind === 'type';
|
|
56
|
+
|
|
57
|
+
if (isTypeOnlyImport || isTypeOnlySpecifier) {
|
|
58
|
+
context.report({
|
|
59
|
+
node: specifier,
|
|
60
|
+
messageId: 'typeFormatsImports',
|
|
61
|
+
data: {
|
|
62
|
+
typeName: importedName,
|
|
63
|
+
source,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default rule;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* ########
|
|
2
|
+
* 2026 mion
|
|
3
|
+
* Author: Ma-jerez
|
|
4
|
+
* License: MIT
|
|
5
|
+
* The software is provided "as is", without warranty of any kind.
|
|
6
|
+
* ######## */
|
|
7
|
+
|
|
8
|
+
/** Globals and built-ins that are allowed inside pure functions */
|
|
9
|
+
export const ALLOWED_GLOBALS = new Set([
|
|
10
|
+
// Value types
|
|
11
|
+
'undefined',
|
|
12
|
+
'null',
|
|
13
|
+
'NaN',
|
|
14
|
+
'Infinity',
|
|
15
|
+
'true',
|
|
16
|
+
'false',
|
|
17
|
+
// Built-in constructors/objects
|
|
18
|
+
'Object',
|
|
19
|
+
'Array',
|
|
20
|
+
'String',
|
|
21
|
+
'Number',
|
|
22
|
+
'Boolean',
|
|
23
|
+
'Math',
|
|
24
|
+
'JSON',
|
|
25
|
+
'Date',
|
|
26
|
+
'RegExp',
|
|
27
|
+
'Map',
|
|
28
|
+
'Set',
|
|
29
|
+
'WeakMap',
|
|
30
|
+
'WeakSet',
|
|
31
|
+
'Symbol',
|
|
32
|
+
'BigInt',
|
|
33
|
+
'Promise',
|
|
34
|
+
'Error',
|
|
35
|
+
'TypeError',
|
|
36
|
+
'RangeError',
|
|
37
|
+
'parseInt',
|
|
38
|
+
'parseFloat',
|
|
39
|
+
'isNaN',
|
|
40
|
+
'isFinite',
|
|
41
|
+
'encodeURIComponent',
|
|
42
|
+
'decodeURIComponent',
|
|
43
|
+
'encodeURI',
|
|
44
|
+
'decodeURI',
|
|
45
|
+
// Common safe globals
|
|
46
|
+
'console',
|
|
47
|
+
'globalThis',
|
|
48
|
+
'Bun',
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
/** Forbidden identifiers that indicate impure operations in pure functions */
|
|
52
|
+
export const FORBIDDEN_IDENTIFIERS = new Set([
|
|
53
|
+
'eval',
|
|
54
|
+
'Function',
|
|
55
|
+
'fetch',
|
|
56
|
+
'setTimeout',
|
|
57
|
+
'setInterval',
|
|
58
|
+
'clearTimeout',
|
|
59
|
+
'clearInterval',
|
|
60
|
+
'process',
|
|
61
|
+
'window',
|
|
62
|
+
'document',
|
|
63
|
+
'global',
|
|
64
|
+
'require',
|
|
65
|
+
'XMLHttpRequest',
|
|
66
|
+
'WebSocket',
|
|
67
|
+
'localStorage',
|
|
68
|
+
'sessionStorage',
|
|
69
|
+
'indexedDB',
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
/** The target function names that purity validation applies to */
|
|
73
|
+
export const PURE_FN_CALL_NAMES = ['pureServerFn', 'registerPureFnFactory', 'mapFrom'] as const;
|
|
74
|
+
|
|
75
|
+
/** The packages that export the target pure function APIs */
|
|
76
|
+
export const PURE_FN_SOURCE_PACKAGES = ['@mionjs/core', '@mionjs/client'] as const;
|