@pikku/inspector 0.12.39 → 0.12.40
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 +13 -0
- package/dist/add/add-functions.js +25 -0
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +1 -0
- package/dist/inspector.js +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/utils/post-process.js +8 -0
- package/dist/utils/serialize-inspector-state.js +1 -0
- package/package.json +3 -3
- package/src/add/add-functions.ts +31 -0
- package/src/add/dynamic-import-check.test.ts +119 -0
- package/src/error-codes.ts +1 -0
- package/src/inspector.ts +1 -0
- package/src/types.ts +6 -0
- package/src/utils/compute-diagnostics.test.ts +43 -6
- package/src/utils/post-process.ts +8 -0
- package/src/utils/serialize-inspector-state.ts +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 0.12.40
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 1f3f510: Warn when a Pikku function body performs a runtime dynamic `import(...)`.
|
|
6
|
+
|
|
7
|
+
The inspector now flags any `pikkuFunc`/`pikkuSessionlessFunc` (and friends) whose handler body contains a dynamic `import(...)` call — including nested callbacks — with the new `PKU498` diagnostic. Function bodies run on every invocation, so a dynamic import there adds per-call latency and defeats bundling/tree-shaking; the import belongs at the top of the module or in your services/`wireServices` setup instead.
|
|
8
|
+
|
|
9
|
+
Type-only positions like `import('x').Foo` are not flagged. The rule defaults to `warn` — a printed yellow warning that does not fail the build — and is configurable via `lint.functionDynamicImport` in `pikku.config.json` (`'off'` to silence, `'error'` to make it a hard build failure), matching the existing `servicesNotDestructured`/`wiresNotDestructured` lints.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [1f3f510]
|
|
12
|
+
- @pikku/core@0.12.59
|
|
13
|
+
|
|
1
14
|
## 0.12.39
|
|
2
15
|
|
|
3
16
|
### Patch Changes
|
|
@@ -15,6 +15,28 @@ const isValidVariableName = (name) => {
|
|
|
15
15
|
const regex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
16
16
|
return regex.test(name);
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* True when the handler body contains a runtime dynamic `import(...)` call
|
|
20
|
+
* anywhere (including nested callbacks). Type-only positions like
|
|
21
|
+
* `import('x').Foo` are `ImportTypeNode`s, not `CallExpression`s with an
|
|
22
|
+
* `ImportKeyword` callee, so they are intentionally not matched.
|
|
23
|
+
*/
|
|
24
|
+
const handlerHasDynamicImport = (handler) => {
|
|
25
|
+
let found = false;
|
|
26
|
+
const visit = (node) => {
|
|
27
|
+
if (found) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (ts.isCallExpression(node) &&
|
|
31
|
+
node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
32
|
+
found = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
ts.forEachChild(node, visit);
|
|
36
|
+
};
|
|
37
|
+
visit(handler.body);
|
|
38
|
+
return found;
|
|
39
|
+
};
|
|
18
40
|
const nullifyTypes = (type) => {
|
|
19
41
|
if (type === 'void' ||
|
|
20
42
|
type === 'undefined' ||
|
|
@@ -779,6 +801,9 @@ export const addFunctions = (logger, node, checker, state, options) => {
|
|
|
779
801
|
exportedName: exportedName || undefined,
|
|
780
802
|
...bodySpan,
|
|
781
803
|
};
|
|
804
|
+
if (handlerHasDynamicImport(handler)) {
|
|
805
|
+
state.functions.dynamicImportIds.add(pikkuFuncId);
|
|
806
|
+
}
|
|
782
807
|
// Populate node metadata if node config is present
|
|
783
808
|
if (nodeDisplayName && nodeCategory && nodeType) {
|
|
784
809
|
state.nodes.files.add(node.getSourceFile().fileName);
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export declare enum ErrorCode {
|
|
|
46
46
|
SCHEMA_AND_WIRING_COLOCATED = "PKU490",
|
|
47
47
|
SERVICES_NOT_DESTRUCTURED = "PKU410",
|
|
48
48
|
WIRES_NOT_DESTRUCTURED = "PKU411",
|
|
49
|
+
FUNCTION_DYNAMIC_IMPORT = "PKU498",
|
|
49
50
|
DUPLICATE_CORE_VERSION = "PKU717",
|
|
50
51
|
PII_IN_OUTPUT = "PKU910",
|
|
51
52
|
ADDON_WIRING_NOT_ALLOWED = "PKU920",
|
package/dist/error-codes.js
CHANGED
|
@@ -58,6 +58,7 @@ export var ErrorCode;
|
|
|
58
58
|
// Optimization diagnostics
|
|
59
59
|
ErrorCode["SERVICES_NOT_DESTRUCTURED"] = "PKU410";
|
|
60
60
|
ErrorCode["WIRES_NOT_DESTRUCTURED"] = "PKU411";
|
|
61
|
+
ErrorCode["FUNCTION_DYNAMIC_IMPORT"] = "PKU498";
|
|
61
62
|
// Dependency integrity errors
|
|
62
63
|
ErrorCode["DUPLICATE_CORE_VERSION"] = "PKU717";
|
|
63
64
|
// Data classification errors
|
package/dist/inspector.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -76,6 +76,12 @@ export interface InspectorFunctionState {
|
|
|
76
76
|
exportedName: string;
|
|
77
77
|
}>;
|
|
78
78
|
approvalDescriptions: Record<string, InspectorApprovalDescriptionDefinition>;
|
|
79
|
+
/**
|
|
80
|
+
* pikkuFuncIds whose handler body performs a runtime dynamic `import(...)`.
|
|
81
|
+
* Transient lint signal consumed by computeDiagnostics (PKU498) in the same
|
|
82
|
+
* inspection pass — deliberately not part of the serialized function meta.
|
|
83
|
+
*/
|
|
84
|
+
dynamicImportIds: Set<string>;
|
|
79
85
|
}
|
|
80
86
|
export interface InspectorChannelState {
|
|
81
87
|
meta: ChannelsMeta;
|
|
@@ -581,6 +581,14 @@ export function computeDiagnostics(state) {
|
|
|
581
581
|
position: 0,
|
|
582
582
|
});
|
|
583
583
|
}
|
|
584
|
+
if (state.functions.dynamicImportIds.has(id)) {
|
|
585
|
+
diagnostics.push({
|
|
586
|
+
code: ErrorCode.FUNCTION_DYNAMIC_IMPORT,
|
|
587
|
+
message: `Function '${id}' performs a runtime dynamic 'import(...)' in its body. Move the import to the top of the module (static import) or into your services/wireServices setup — function bodies run on every invocation, so a dynamic import there adds latency and defeats bundling/tree-shaking.`,
|
|
588
|
+
sourceFile: meta.pikkuFuncId,
|
|
589
|
+
position: 0,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
584
592
|
}
|
|
585
593
|
for (const [id, def] of Object.entries(state.middleware.definitions)) {
|
|
586
594
|
if (def.services && !def.services.optimized) {
|
|
@@ -194,6 +194,7 @@ export function deserializeInspectorState(data) {
|
|
|
194
194
|
meta: data.functions.meta,
|
|
195
195
|
files: new Map(data.functions.files),
|
|
196
196
|
approvalDescriptions: data.functions.approvalDescriptions || {},
|
|
197
|
+
dynamicImportIds: new Set(),
|
|
197
198
|
},
|
|
198
199
|
http: {
|
|
199
200
|
metaInputTypes: new Map(data.http.metaInputTypes),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.40",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"zod-to-ts": "^2.1.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@pikku/core": "^0.12.
|
|
47
|
+
"@pikku/core": "^0.12.59"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@pikku/core": "^0.12.
|
|
50
|
+
"@pikku/core": "^0.12.59",
|
|
51
51
|
"@types/node": "^24.11.0"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
package/src/add/add-functions.ts
CHANGED
|
@@ -27,6 +27,33 @@ const isValidVariableName = (name: string) => {
|
|
|
27
27
|
return regex.test(name)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* True when the handler body contains a runtime dynamic `import(...)` call
|
|
32
|
+
* anywhere (including nested callbacks). Type-only positions like
|
|
33
|
+
* `import('x').Foo` are `ImportTypeNode`s, not `CallExpression`s with an
|
|
34
|
+
* `ImportKeyword` callee, so they are intentionally not matched.
|
|
35
|
+
*/
|
|
36
|
+
const handlerHasDynamicImport = (
|
|
37
|
+
handler: ts.ArrowFunction | ts.FunctionExpression
|
|
38
|
+
): boolean => {
|
|
39
|
+
let found = false
|
|
40
|
+
const visit = (node: ts.Node) => {
|
|
41
|
+
if (found) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
ts.isCallExpression(node) &&
|
|
46
|
+
node.expression.kind === ts.SyntaxKind.ImportKeyword
|
|
47
|
+
) {
|
|
48
|
+
found = true
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
ts.forEachChild(node, visit)
|
|
52
|
+
}
|
|
53
|
+
visit(handler.body)
|
|
54
|
+
return found
|
|
55
|
+
}
|
|
56
|
+
|
|
30
57
|
const nullifyTypes = (type: string | null) => {
|
|
31
58
|
if (
|
|
32
59
|
type === 'void' ||
|
|
@@ -1083,6 +1110,10 @@ export const addFunctions: AddWiring = (
|
|
|
1083
1110
|
...bodySpan,
|
|
1084
1111
|
}
|
|
1085
1112
|
|
|
1113
|
+
if (handlerHasDynamicImport(handler)) {
|
|
1114
|
+
state.functions.dynamicImportIds.add(pikkuFuncId)
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1086
1117
|
// Populate node metadata if node config is present
|
|
1087
1118
|
if (nodeDisplayName && nodeCategory && nodeType) {
|
|
1088
1119
|
state.nodes.files.add(node.getSourceFile().fileName)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { strict as assert } from 'assert'
|
|
2
|
+
import { describe, test } from 'node:test'
|
|
3
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
import { inspect } from '../inspector.js'
|
|
7
|
+
import { ErrorCode } from '../error-codes.js'
|
|
8
|
+
import type { InspectorLogger } from '../types.js'
|
|
9
|
+
|
|
10
|
+
const logger: InspectorLogger = {
|
|
11
|
+
debug: () => {},
|
|
12
|
+
info: () => {},
|
|
13
|
+
warn: () => {},
|
|
14
|
+
error: () => {},
|
|
15
|
+
diagnostic: () => {},
|
|
16
|
+
critical: () => {},
|
|
17
|
+
hasCriticalErrors: () => false,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function inspectSource(source: string) {
|
|
21
|
+
const rootDir = await mkdtemp(join(tmpdir(), 'pikku-dynimport-'))
|
|
22
|
+
const file = join(rootDir, 'my.function.ts')
|
|
23
|
+
await writeFile(file, source)
|
|
24
|
+
try {
|
|
25
|
+
return await inspect(logger, [file], { rootDir })
|
|
26
|
+
} finally {
|
|
27
|
+
await rm(rootDir, { recursive: true, force: true })
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('dynamic import in function bodies', () => {
|
|
32
|
+
test('flags a function whose body does `await import(...)`', async () => {
|
|
33
|
+
const state = await inspectSource(
|
|
34
|
+
[
|
|
35
|
+
"import { pikkuSessionlessFunc } from '@pikku/core'",
|
|
36
|
+
'',
|
|
37
|
+
'export const greedy = pikkuSessionlessFunc({',
|
|
38
|
+
' func: async ({ logger }) => {',
|
|
39
|
+
" const { readFile } = await import('node:fs/promises')",
|
|
40
|
+
' return { readFile: typeof readFile }',
|
|
41
|
+
' },',
|
|
42
|
+
'})',
|
|
43
|
+
].join('\n')
|
|
44
|
+
)
|
|
45
|
+
const meta = (state.functions.meta as any).greedy
|
|
46
|
+
assert.ok(meta, 'greedy should be inspected')
|
|
47
|
+
assert.ok(state.functions.dynamicImportIds.has(meta.pikkuFuncId))
|
|
48
|
+
const diagnostic = state.diagnostics.find(
|
|
49
|
+
(d) => d.code === ErrorCode.FUNCTION_DYNAMIC_IMPORT
|
|
50
|
+
)
|
|
51
|
+
assert.ok(
|
|
52
|
+
diagnostic,
|
|
53
|
+
'expected a FUNCTION_DYNAMIC_IMPORT diagnostic for the function'
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('flags a nested `import(...)` call inside a callback', async () => {
|
|
58
|
+
const state = await inspectSource(
|
|
59
|
+
[
|
|
60
|
+
"import { pikkuSessionlessFunc } from '@pikku/core'",
|
|
61
|
+
'',
|
|
62
|
+
'export const nested = pikkuSessionlessFunc({',
|
|
63
|
+
' func: async ({ logger }) => {',
|
|
64
|
+
' await Promise.all([',
|
|
65
|
+
" import('node:os').then((m) => m.hostname()),",
|
|
66
|
+
' ])',
|
|
67
|
+
' return { ok: true }',
|
|
68
|
+
' },',
|
|
69
|
+
'})',
|
|
70
|
+
].join('\n')
|
|
71
|
+
)
|
|
72
|
+
const meta = (state.functions.meta as any).nested
|
|
73
|
+
assert.ok(state.functions.dynamicImportIds.has(meta.pikkuFuncId))
|
|
74
|
+
assert.ok(
|
|
75
|
+
state.diagnostics.some(
|
|
76
|
+
(d) => d.code === ErrorCode.FUNCTION_DYNAMIC_IMPORT
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('does NOT flag a function with only static imports', async () => {
|
|
82
|
+
const state = await inspectSource(
|
|
83
|
+
[
|
|
84
|
+
"import { pikkuSessionlessFunc } from '@pikku/core'",
|
|
85
|
+
'',
|
|
86
|
+
'export const clean = pikkuSessionlessFunc({',
|
|
87
|
+
' func: async ({ logger }, data: { n: number }) => {',
|
|
88
|
+
' return { doubled: data.n * 2 }',
|
|
89
|
+
' },',
|
|
90
|
+
'})',
|
|
91
|
+
].join('\n')
|
|
92
|
+
)
|
|
93
|
+
const meta = (state.functions.meta as any).clean
|
|
94
|
+
assert.ok(meta, 'clean should be inspected')
|
|
95
|
+
assert.ok(!state.functions.dynamicImportIds.has(meta.pikkuFuncId))
|
|
96
|
+
assert.ok(
|
|
97
|
+
!state.diagnostics.some(
|
|
98
|
+
(d) => d.code === ErrorCode.FUNCTION_DYNAMIC_IMPORT
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('does NOT flag a type-only import position `import("x").T`', async () => {
|
|
104
|
+
const state = await inspectSource(
|
|
105
|
+
[
|
|
106
|
+
"import { pikkuSessionlessFunc } from '@pikku/core'",
|
|
107
|
+
'',
|
|
108
|
+
'export const typeOnly = pikkuSessionlessFunc({',
|
|
109
|
+
" func: async ({ logger }, data: import('node:buffer').Blob) => {",
|
|
110
|
+
' return { size: data.size }',
|
|
111
|
+
' },',
|
|
112
|
+
'})',
|
|
113
|
+
].join('\n')
|
|
114
|
+
)
|
|
115
|
+
const meta = (state.functions.meta as any).typeOnly
|
|
116
|
+
assert.ok(meta, 'typeOnly should be inspected')
|
|
117
|
+
assert.ok(!state.functions.dynamicImportIds.has(meta.pikkuFuncId))
|
|
118
|
+
})
|
|
119
|
+
})
|
package/src/error-codes.ts
CHANGED
package/src/inspector.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -101,6 +101,12 @@ export interface InspectorFunctionState {
|
|
|
101
101
|
meta: FunctionsMeta
|
|
102
102
|
files: Map<string, { path: string; exportedName: string }>
|
|
103
103
|
approvalDescriptions: Record<string, InspectorApprovalDescriptionDefinition>
|
|
104
|
+
/**
|
|
105
|
+
* pikkuFuncIds whose handler body performs a runtime dynamic `import(...)`.
|
|
106
|
+
* Transient lint signal consumed by computeDiagnostics (PKU498) in the same
|
|
107
|
+
* inspection pass — deliberately not part of the serialized function meta.
|
|
108
|
+
*/
|
|
109
|
+
dynamicImportIds: Set<string>
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
export interface InspectorChannelState {
|
|
@@ -5,10 +5,11 @@ import type { InspectorState } from '../types.js'
|
|
|
5
5
|
import { ErrorCode } from '../error-codes.js'
|
|
6
6
|
|
|
7
7
|
function stateWithFunctions(
|
|
8
|
-
meta: InspectorState['functions']['meta']
|
|
8
|
+
meta: InspectorState['functions']['meta'],
|
|
9
|
+
dynamicImportIds: string[] = []
|
|
9
10
|
): InspectorState {
|
|
10
11
|
return {
|
|
11
|
-
functions: { meta },
|
|
12
|
+
functions: { meta, dynamicImportIds: new Set(dynamicImportIds) },
|
|
12
13
|
middleware: { definitions: {} },
|
|
13
14
|
permissions: { definitions: {} },
|
|
14
15
|
} as unknown as InspectorState
|
|
@@ -27,10 +28,7 @@ describe('computeDiagnostics', () => {
|
|
|
27
28
|
})
|
|
28
29
|
computeDiagnostics(state)
|
|
29
30
|
assert.equal(state.diagnostics.length, 1)
|
|
30
|
-
assert.equal(
|
|
31
|
-
state.diagnostics[0].code,
|
|
32
|
-
ErrorCode.SERVICES_NOT_DESTRUCTURED
|
|
33
|
-
)
|
|
31
|
+
assert.equal(state.diagnostics[0].code, ErrorCode.SERVICES_NOT_DESTRUCTURED)
|
|
34
32
|
})
|
|
35
33
|
|
|
36
34
|
test('does NOT flag a generated .gen.ts function (user cannot edit it)', () => {
|
|
@@ -66,4 +64,43 @@ describe('computeDiagnostics', () => {
|
|
|
66
64
|
computeDiagnostics(state)
|
|
67
65
|
assert.equal(state.diagnostics.length, 0)
|
|
68
66
|
})
|
|
67
|
+
|
|
68
|
+
test('flags a user-authored function that does a dynamic import in its body', () => {
|
|
69
|
+
const state = stateWithFunctions(
|
|
70
|
+
{
|
|
71
|
+
greedy: {
|
|
72
|
+
pikkuFuncId: 'greedy',
|
|
73
|
+
inputSchemaName: null,
|
|
74
|
+
outputSchemaName: null,
|
|
75
|
+
sourceFile: '/project/src/greedy.ts',
|
|
76
|
+
services: { optimized: true, services: ['logger'] },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
['greedy']
|
|
80
|
+
)
|
|
81
|
+
computeDiagnostics(state)
|
|
82
|
+
assert.equal(
|
|
83
|
+
state.diagnostics.filter(
|
|
84
|
+
(d) => d.code === ErrorCode.FUNCTION_DYNAMIC_IMPORT
|
|
85
|
+
).length,
|
|
86
|
+
1
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('does NOT flag a dynamic import in a generated .gen.ts function', () => {
|
|
91
|
+
const state = stateWithFunctions(
|
|
92
|
+
{
|
|
93
|
+
authHandler: {
|
|
94
|
+
pikkuFuncId: 'authHandler',
|
|
95
|
+
inputSchemaName: null,
|
|
96
|
+
outputSchemaName: null,
|
|
97
|
+
sourceFile: '/project/.pikku/auth.gen.ts',
|
|
98
|
+
services: { optimized: true, services: [] },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
['authHandler']
|
|
102
|
+
)
|
|
103
|
+
computeDiagnostics(state)
|
|
104
|
+
assert.equal(state.diagnostics.length, 0)
|
|
105
|
+
})
|
|
69
106
|
})
|
|
@@ -719,6 +719,14 @@ export function computeDiagnostics(state: InspectorState): void {
|
|
|
719
719
|
position: 0,
|
|
720
720
|
})
|
|
721
721
|
}
|
|
722
|
+
if (state.functions.dynamicImportIds.has(id)) {
|
|
723
|
+
diagnostics.push({
|
|
724
|
+
code: ErrorCode.FUNCTION_DYNAMIC_IMPORT,
|
|
725
|
+
message: `Function '${id}' performs a runtime dynamic 'import(...)' in its body. Move the import to the top of the module (static import) or into your services/wireServices setup — function bodies run on every invocation, so a dynamic import there adds latency and defeats bundling/tree-shaking.`,
|
|
726
|
+
sourceFile: meta.pikkuFuncId,
|
|
727
|
+
position: 0,
|
|
728
|
+
})
|
|
729
|
+
}
|
|
722
730
|
}
|
|
723
731
|
|
|
724
732
|
for (const [id, def] of Object.entries(state.middleware.definitions)) {
|
|
@@ -529,6 +529,7 @@ export function deserializeInspectorState(
|
|
|
529
529
|
meta: data.functions.meta,
|
|
530
530
|
files: new Map(data.functions.files),
|
|
531
531
|
approvalDescriptions: (data.functions as any).approvalDescriptions || {},
|
|
532
|
+
dynamicImportIds: new Set(),
|
|
532
533
|
},
|
|
533
534
|
http: {
|
|
534
535
|
metaInputTypes: new Map(data.http.metaInputTypes),
|