@pikku/inspector 0.6.3 → 0.7.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 +18 -0
- package/dist/add-channel.d.ts +12 -2
- package/dist/add-channel.js +336 -108
- package/dist/add-functions.d.ts +7 -0
- package/dist/add-functions.js +260 -0
- package/dist/add-http-route.d.ts +15 -3
- package/dist/add-http-route.js +69 -80
- package/dist/add-schedule.d.ts +1 -1
- package/dist/add-schedule.js +14 -4
- package/dist/events/add-channel.d.ts +1 -0
- package/dist/events/add-channel.js +170 -0
- package/dist/events/add-http-route.d.ts +16 -0
- package/dist/events/add-http-route.js +83 -0
- package/dist/events/add-schedule.d.ts +3 -0
- package/dist/events/add-schedule.js +38 -0
- package/dist/inspector.js +14 -4
- package/dist/types.d.ts +7 -10
- package/dist/utils.d.ts +21 -26
- package/dist/utils.js +635 -213
- package/dist/visit.d.ts +2 -1
- package/dist/visit.js +9 -4
- package/package.json +2 -2
- package/src/add-channel.ts +0 -179
- package/src/add-file-extends-core-type.ts +0 -50
- package/src/add-file-with-config.ts +0 -45
- package/src/add-file-with-factory.ts +0 -65
- package/src/add-http-route.ts +0 -138
- package/src/add-schedule.ts +0 -56
- package/src/does-type-extend-core-type.ts +0 -53
- package/src/get-property-value.ts +0 -84
- package/src/index.ts +0 -4
- package/src/inspector.ts +0 -63
- package/src/types-map.ts +0 -130
- package/src/types.ts +0 -62
- package/src/utils.ts +0 -367
- package/src/visit.ts +0 -57
- package/tsconfig.json +0 -19
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { APIDocs } from '@pikku/core'
|
|
2
|
-
import * as ts from 'typescript'
|
|
3
|
-
|
|
4
|
-
export const getPropertyValue = (
|
|
5
|
-
obj: ts.ObjectLiteralExpression,
|
|
6
|
-
propertyName: string
|
|
7
|
-
): string | string[] | null | APIDocs => {
|
|
8
|
-
const property = obj.properties.find(
|
|
9
|
-
(p) =>
|
|
10
|
-
ts.isPropertyAssignment(p) &&
|
|
11
|
-
ts.isIdentifier(p.name) &&
|
|
12
|
-
p.name.text === propertyName
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
if (property && ts.isPropertyAssignment(property)) {
|
|
16
|
-
const initializer = property.initializer
|
|
17
|
-
|
|
18
|
-
// Special handling for 'query' -> expect an array of strings
|
|
19
|
-
if (
|
|
20
|
-
['query', 'tags'].includes(propertyName) &&
|
|
21
|
-
ts.isArrayLiteralExpression(initializer)
|
|
22
|
-
) {
|
|
23
|
-
const stringArray = initializer.elements
|
|
24
|
-
.map((element) => {
|
|
25
|
-
if (ts.isStringLiteral(element)) {
|
|
26
|
-
return element.text
|
|
27
|
-
}
|
|
28
|
-
return null
|
|
29
|
-
})
|
|
30
|
-
.filter((item) => item !== null) as string[] // Filter non-null and assert type
|
|
31
|
-
|
|
32
|
-
return stringArray.length > 0 ? stringArray : null
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Special handling for 'docs' -> expect RouteDocs
|
|
36
|
-
if (propertyName === 'docs' && ts.isObjectLiteralExpression(initializer)) {
|
|
37
|
-
const docs: APIDocs = {}
|
|
38
|
-
|
|
39
|
-
initializer.properties.forEach((prop) => {
|
|
40
|
-
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
41
|
-
const propName = prop.name.text
|
|
42
|
-
|
|
43
|
-
if (propName === 'summary' && ts.isStringLiteral(prop.initializer)) {
|
|
44
|
-
docs.summary = prop.initializer.text
|
|
45
|
-
} else if (
|
|
46
|
-
propName === 'description' &&
|
|
47
|
-
ts.isStringLiteral(prop.initializer)
|
|
48
|
-
) {
|
|
49
|
-
docs.description = prop.initializer.text
|
|
50
|
-
} else if (
|
|
51
|
-
propName === 'tags' &&
|
|
52
|
-
ts.isArrayLiteralExpression(prop.initializer)
|
|
53
|
-
) {
|
|
54
|
-
docs.tags = prop.initializer.elements
|
|
55
|
-
.filter(ts.isStringLiteral)
|
|
56
|
-
.map((element) => element.text)
|
|
57
|
-
} else if (
|
|
58
|
-
propName === 'errors' &&
|
|
59
|
-
ts.isArrayLiteralExpression(prop.initializer)
|
|
60
|
-
) {
|
|
61
|
-
docs.errors = prop.initializer.elements
|
|
62
|
-
.filter(ts.isIdentifier)
|
|
63
|
-
.map((element) => element.text as unknown as string)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
return docs
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Handle string literals for other properties
|
|
72
|
-
if (
|
|
73
|
-
ts.isStringLiteral(initializer) ||
|
|
74
|
-
ts.isNoSubstitutionTemplateLiteral(initializer)
|
|
75
|
-
) {
|
|
76
|
-
return initializer.text
|
|
77
|
-
} else {
|
|
78
|
-
// Handle other initializer types if necessary
|
|
79
|
-
return initializer.getText()
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return null
|
|
84
|
-
}
|
package/src/index.ts
DELETED
package/src/inspector.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { visit } from './visit.js'
|
|
3
|
-
import { TypesMap } from './types-map.js'
|
|
4
|
-
import {
|
|
5
|
-
InspectorState,
|
|
6
|
-
InspectorHTTPState,
|
|
7
|
-
InspectorFilters,
|
|
8
|
-
} from './types.js'
|
|
9
|
-
|
|
10
|
-
export const normalizeHTTPTypes = (
|
|
11
|
-
httpState: InspectorHTTPState
|
|
12
|
-
): InspectorHTTPState => {
|
|
13
|
-
return httpState
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const inspect = (
|
|
17
|
-
routeFiles: string[],
|
|
18
|
-
filters: InspectorFilters
|
|
19
|
-
): InspectorState => {
|
|
20
|
-
const program = ts.createProgram(routeFiles, {
|
|
21
|
-
target: ts.ScriptTarget.ESNext,
|
|
22
|
-
module: ts.ModuleKind.CommonJS,
|
|
23
|
-
})
|
|
24
|
-
const checker = program.getTypeChecker()
|
|
25
|
-
const sourceFiles = program.getSourceFiles()
|
|
26
|
-
|
|
27
|
-
const state: InspectorState = {
|
|
28
|
-
singletonServicesTypeImportMap: new Map(),
|
|
29
|
-
sessionServicesTypeImportMap: new Map(),
|
|
30
|
-
userSessionTypeImportMap: new Map(),
|
|
31
|
-
singletonServicesFactories: new Map(),
|
|
32
|
-
sessionServicesFactories: new Map(),
|
|
33
|
-
configFactories: new Map(),
|
|
34
|
-
http: {
|
|
35
|
-
typesMap: new TypesMap(),
|
|
36
|
-
metaInputTypes: new Map(),
|
|
37
|
-
meta: [],
|
|
38
|
-
files: new Set(),
|
|
39
|
-
},
|
|
40
|
-
channels: {
|
|
41
|
-
typesMap: new TypesMap(),
|
|
42
|
-
metaInputTypes: new Map(),
|
|
43
|
-
files: new Set(),
|
|
44
|
-
meta: [],
|
|
45
|
-
},
|
|
46
|
-
scheduledTasks: {
|
|
47
|
-
meta: [],
|
|
48
|
-
files: new Set(),
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
for (const sourceFile of sourceFiles) {
|
|
53
|
-
ts.forEachChild(sourceFile, (child) =>
|
|
54
|
-
visit(checker, child, state, filters)
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Normalise the typesMap
|
|
59
|
-
|
|
60
|
-
state.http = normalizeHTTPTypes(state.http)
|
|
61
|
-
|
|
62
|
-
return state
|
|
63
|
-
}
|
package/src/types-map.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
export class TypesMap {
|
|
2
|
-
private map: Map<string, { originalName: string; path: string | null }> =
|
|
3
|
-
new Map()
|
|
4
|
-
public customTypes: Map<string, { type: string; references: string[] }> =
|
|
5
|
-
new Map()
|
|
6
|
-
|
|
7
|
-
public addCustomType(name: string, type: string, references: string[]) {
|
|
8
|
-
this.customTypes.set(name, { type, references })
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
public addType(originalName: string, path: string) {
|
|
12
|
-
this.map.set(originalName, { originalName, path })
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public addUniqueType(originalName: string, path: string): string {
|
|
16
|
-
const uniqueName = `${originalName}_${Math.random().toString(36).substring(7)}`
|
|
17
|
-
this.map.set(uniqueName, { originalName, path })
|
|
18
|
-
return uniqueName
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public getUniqueName(name: string): string {
|
|
22
|
-
const meta = this.getTypeMeta(name)
|
|
23
|
-
return meta.uniqueName
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public getTypeMeta(name: string): {
|
|
27
|
-
originalName: string
|
|
28
|
-
uniqueName: string
|
|
29
|
-
path: string | null
|
|
30
|
-
} {
|
|
31
|
-
if (['string', 'number', 'boolean', 'null'].includes(name)) {
|
|
32
|
-
return {
|
|
33
|
-
originalName: name,
|
|
34
|
-
uniqueName: name,
|
|
35
|
-
path: null,
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (this.customTypes.has(name)) {
|
|
40
|
-
return {
|
|
41
|
-
originalName: name,
|
|
42
|
-
uniqueName: name,
|
|
43
|
-
path: null,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let meta = this.map.get(name)
|
|
48
|
-
if (!meta) {
|
|
49
|
-
meta = Array.from(this.map.entries()).find(
|
|
50
|
-
([_, { originalName }]) => originalName === name
|
|
51
|
-
)?.[1]
|
|
52
|
-
}
|
|
53
|
-
if (!meta) {
|
|
54
|
-
throw new Error(`Type ${name} not found in typesMap`)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const getName = this.squash()
|
|
58
|
-
return {
|
|
59
|
-
uniqueName: getName(name),
|
|
60
|
-
originalName: meta.originalName,
|
|
61
|
-
path: meta?.path,
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public exists(originalName: string, path: string): string | undefined {
|
|
66
|
-
const found = Array.from(this.map.entries()).find(([_, type]) => {
|
|
67
|
-
return type.path === path && type.originalName === originalName
|
|
68
|
-
})
|
|
69
|
-
return found ? found[0] : undefined
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private squash() {
|
|
73
|
-
const duplicateNames = new Set<string>()
|
|
74
|
-
const pathToNamesMap = new Map<string, Map<string, string>>()
|
|
75
|
-
const nameOccurrences = new Map<string, Set<string>>()
|
|
76
|
-
|
|
77
|
-
// First pass: Track occurrences of each original name across paths
|
|
78
|
-
this.map.forEach(({ path, originalName }) => {
|
|
79
|
-
if (path) {
|
|
80
|
-
if (!nameOccurrences.has(originalName)) {
|
|
81
|
-
nameOccurrences.set(originalName, new Set())
|
|
82
|
-
}
|
|
83
|
-
nameOccurrences.get(originalName)!.add(path)
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
// Second pass: Populate pathToNamesMap
|
|
88
|
-
this.map.forEach(({ path, originalName }, uniqueName) => {
|
|
89
|
-
if (!path) return
|
|
90
|
-
|
|
91
|
-
if (!pathToNamesMap.has(path)) {
|
|
92
|
-
pathToNamesMap.set(path, new Map())
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const isDuplicate = nameOccurrences.get(originalName)!.size > 1
|
|
96
|
-
if (isDuplicate) {
|
|
97
|
-
duplicateNames.add(uniqueName)
|
|
98
|
-
}
|
|
99
|
-
// Use uniqueName only if the originalName is duplicated across files
|
|
100
|
-
const nameToUse = isDuplicate ? uniqueName : originalName
|
|
101
|
-
pathToNamesMap.get(path)!.set(nameToUse, originalName)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
const getName = (uniqueName: string) => {
|
|
105
|
-
if (duplicateNames.has(uniqueName)) {
|
|
106
|
-
return uniqueName
|
|
107
|
-
}
|
|
108
|
-
if (
|
|
109
|
-
uniqueName === 'string' ||
|
|
110
|
-
uniqueName === 'number' ||
|
|
111
|
-
uniqueName === 'boolean' ||
|
|
112
|
-
uniqueName === 'null'
|
|
113
|
-
) {
|
|
114
|
-
return uniqueName
|
|
115
|
-
}
|
|
116
|
-
if (!this.map.has(uniqueName)) {
|
|
117
|
-
const found = Array.from(this.map.entries()).find(
|
|
118
|
-
([_, { originalName }]) => originalName === uniqueName
|
|
119
|
-
)?.[1]
|
|
120
|
-
if (!found) {
|
|
121
|
-
throw new Error(`Type ${uniqueName} not found in typesMap`)
|
|
122
|
-
}
|
|
123
|
-
return found.originalName
|
|
124
|
-
}
|
|
125
|
-
return this.map.get(uniqueName)!.originalName
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return getName
|
|
129
|
-
}
|
|
130
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { ChannelsMeta } from '@pikku/core/channel'
|
|
2
|
-
import { HTTPRoutesMeta } from '@pikku/core/http'
|
|
3
|
-
import { ScheduledTasksMeta } from '@pikku/core/scheduler'
|
|
4
|
-
import { TypesMap } from './types-map.js'
|
|
5
|
-
|
|
6
|
-
export type PathToNameAndType = Map<
|
|
7
|
-
string,
|
|
8
|
-
{ variable: string; type: string | null; typePath: string | null }[]
|
|
9
|
-
>
|
|
10
|
-
|
|
11
|
-
export type MetaInputTypes = Map<
|
|
12
|
-
string,
|
|
13
|
-
{
|
|
14
|
-
query: string[] | undefined
|
|
15
|
-
params: string[] | undefined
|
|
16
|
-
body: string[] | undefined
|
|
17
|
-
}
|
|
18
|
-
>
|
|
19
|
-
|
|
20
|
-
export type APIFunctionMeta = Array<{
|
|
21
|
-
name: string
|
|
22
|
-
input: string
|
|
23
|
-
output: string
|
|
24
|
-
file: string
|
|
25
|
-
}>
|
|
26
|
-
|
|
27
|
-
export type InspectorAPIFunction = {
|
|
28
|
-
typesMap: TypesMap
|
|
29
|
-
meta: APIFunctionMeta
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface InspectorHTTPState {
|
|
33
|
-
typesMap: TypesMap
|
|
34
|
-
metaInputTypes: MetaInputTypes
|
|
35
|
-
meta: HTTPRoutesMeta
|
|
36
|
-
files: Set<string>
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface InspectorChannelState {
|
|
40
|
-
typesMap: TypesMap
|
|
41
|
-
metaInputTypes: MetaInputTypes
|
|
42
|
-
meta: ChannelsMeta
|
|
43
|
-
files: Set<string>
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export type InspectorFilters = {
|
|
47
|
-
tags?: string[]
|
|
48
|
-
}
|
|
49
|
-
export interface InspectorState {
|
|
50
|
-
singletonServicesTypeImportMap: PathToNameAndType
|
|
51
|
-
sessionServicesTypeImportMap: PathToNameAndType
|
|
52
|
-
userSessionTypeImportMap: PathToNameAndType
|
|
53
|
-
singletonServicesFactories: PathToNameAndType
|
|
54
|
-
sessionServicesFactories: PathToNameAndType
|
|
55
|
-
configFactories: PathToNameAndType
|
|
56
|
-
http: InspectorHTTPState
|
|
57
|
-
channels: InspectorChannelState
|
|
58
|
-
scheduledTasks: {
|
|
59
|
-
meta: ScheduledTasksMeta
|
|
60
|
-
files: Set<string>
|
|
61
|
-
}
|
|
62
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { TypesMap } from './types-map.js'
|
|
3
|
-
import { InspectorFilters } from './types.js'
|
|
4
|
-
|
|
5
|
-
type FunctionTypes = {
|
|
6
|
-
inputTypes: ts.Type[]
|
|
7
|
-
inputs: null | string[]
|
|
8
|
-
outputTypes: ts.Type[]
|
|
9
|
-
outputs: null | string[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const extractTypeKeys = (type: ts.Type): string[] => {
|
|
13
|
-
return type.getProperties().map((symbol) => symbol.getName())
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const nullifyTypes = (type: string | null) => {
|
|
17
|
-
if (
|
|
18
|
-
type === 'void' ||
|
|
19
|
-
type === 'undefined' ||
|
|
20
|
-
type === 'unknown' ||
|
|
21
|
-
type === 'any'
|
|
22
|
-
) {
|
|
23
|
-
return null
|
|
24
|
-
}
|
|
25
|
-
return type
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const isValidVariableName = (name: string) => {
|
|
29
|
-
const regex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/
|
|
30
|
-
return regex.test(name)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const getNamesAndTypes = (
|
|
34
|
-
checker: ts.TypeChecker,
|
|
35
|
-
typesMap: TypesMap,
|
|
36
|
-
direction: 'Input' | 'Output',
|
|
37
|
-
funcName: string,
|
|
38
|
-
type: ts.Type
|
|
39
|
-
) => {
|
|
40
|
-
const result: {
|
|
41
|
-
names: Set<string>
|
|
42
|
-
types: ts.Type[]
|
|
43
|
-
} = {
|
|
44
|
-
names: new Set(),
|
|
45
|
-
types: [],
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const { names, types } = resolveUnionTypes(checker, type)
|
|
49
|
-
const firstName = names[0]
|
|
50
|
-
if (names.length > 1 || (firstName && !isValidVariableName(firstName))) {
|
|
51
|
-
const aliasType = names.join(' | ')
|
|
52
|
-
const aliasName = `${funcName.charAt(0).toUpperCase()}${funcName.slice(1)}${direction}`
|
|
53
|
-
|
|
54
|
-
result.names = new Set([aliasName])
|
|
55
|
-
result.types = types
|
|
56
|
-
|
|
57
|
-
const references = types
|
|
58
|
-
.map((t) => resolveTypeImports(t, typesMap, true))
|
|
59
|
-
.flat()
|
|
60
|
-
typesMap.addCustomType(aliasName, aliasType, references)
|
|
61
|
-
} else {
|
|
62
|
-
const uniqueNames = names
|
|
63
|
-
.map((name, i) => {
|
|
64
|
-
const type = types[i]
|
|
65
|
-
if (!type) {
|
|
66
|
-
throw new Error('TODO: Expected a type here to match name')
|
|
67
|
-
}
|
|
68
|
-
if (isPrimitiveType(type)) {
|
|
69
|
-
return name
|
|
70
|
-
}
|
|
71
|
-
return resolveTypeImports(type, typesMap, false)
|
|
72
|
-
})
|
|
73
|
-
.flat()
|
|
74
|
-
result.names = new Set(uniqueNames)
|
|
75
|
-
result.types = types
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
names: Array.from(result.names),
|
|
80
|
-
types: result.types,
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export const isPrimitiveType = (type: ts.Type): boolean => {
|
|
85
|
-
const primitiveFlags =
|
|
86
|
-
ts.TypeFlags.Number |
|
|
87
|
-
ts.TypeFlags.String |
|
|
88
|
-
ts.TypeFlags.Boolean |
|
|
89
|
-
ts.TypeFlags.BigInt |
|
|
90
|
-
ts.TypeFlags.ESSymbol |
|
|
91
|
-
ts.TypeFlags.Void |
|
|
92
|
-
ts.TypeFlags.Undefined |
|
|
93
|
-
ts.TypeFlags.Null |
|
|
94
|
-
ts.TypeFlags.Any |
|
|
95
|
-
ts.TypeFlags.Unknown
|
|
96
|
-
|
|
97
|
-
return (type.flags & primitiveFlags) !== 0
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export const resolveUnionTypes = (
|
|
101
|
-
checker: ts.TypeChecker,
|
|
102
|
-
type: ts.Type
|
|
103
|
-
): { types: ts.Type[]; names: string[] } => {
|
|
104
|
-
const types: ts.Type[] = []
|
|
105
|
-
const names: string[] = []
|
|
106
|
-
|
|
107
|
-
// Check if it's a union type AND not part of an intersection
|
|
108
|
-
if (type.isUnion() && !(type.flags & ts.TypeFlags.Intersection)) {
|
|
109
|
-
for (const t of type.types) {
|
|
110
|
-
const name = nullifyTypes(checker.typeToString(t))
|
|
111
|
-
if (name) {
|
|
112
|
-
types.push(t)
|
|
113
|
-
names.push(name)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
} else {
|
|
117
|
-
const name = nullifyTypes(checker.typeToString(type))
|
|
118
|
-
if (name) {
|
|
119
|
-
types.push(type)
|
|
120
|
-
names.push(name)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { types, names }
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export const resolveTypeImports = (
|
|
128
|
-
type: ts.Type,
|
|
129
|
-
resolvedTypes: TypesMap,
|
|
130
|
-
isCustom: boolean
|
|
131
|
-
): string[] => {
|
|
132
|
-
const types: string[] = []
|
|
133
|
-
|
|
134
|
-
const visitType = (currentType: ts.Type) => {
|
|
135
|
-
const symbol = currentType.aliasSymbol || currentType.getSymbol()
|
|
136
|
-
|
|
137
|
-
if (symbol) {
|
|
138
|
-
const declarations = symbol.getDeclarations()
|
|
139
|
-
const declaration = declarations?.[0]
|
|
140
|
-
if (declaration) {
|
|
141
|
-
const sourceFile = declaration.getSourceFile()
|
|
142
|
-
const path = sourceFile.fileName
|
|
143
|
-
|
|
144
|
-
// Skip built-in utility types or TypeScript lib types
|
|
145
|
-
if (
|
|
146
|
-
!path.includes('node_modules/typescript') &&
|
|
147
|
-
symbol.getName() !== '__type' &&
|
|
148
|
-
!isPrimitiveType(currentType)
|
|
149
|
-
) {
|
|
150
|
-
const originalName = symbol.getName()
|
|
151
|
-
// Check if the type is already in the map
|
|
152
|
-
let uniqueName = resolvedTypes.exists(originalName, path)
|
|
153
|
-
if (!uniqueName) {
|
|
154
|
-
if (isCustom) {
|
|
155
|
-
uniqueName = resolvedTypes.addUniqueType(originalName, path)
|
|
156
|
-
} else {
|
|
157
|
-
resolvedTypes.addType(originalName, path)
|
|
158
|
-
uniqueName = originalName
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
types.push(uniqueName)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (isCustom) {
|
|
167
|
-
// Handle nested utility types like Partial, Pick, etc.
|
|
168
|
-
if (currentType.aliasTypeArguments) {
|
|
169
|
-
currentType.aliasTypeArguments.forEach(visitType)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Handle intersections and unions
|
|
173
|
-
if (currentType.isUnionOrIntersection()) {
|
|
174
|
-
currentType.types.forEach(visitType)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Handle object types with type arguments
|
|
178
|
-
if (
|
|
179
|
-
currentType.flags & ts.TypeFlags.Object &&
|
|
180
|
-
(currentType as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference
|
|
181
|
-
) {
|
|
182
|
-
const typeRef = currentType as ts.TypeReference
|
|
183
|
-
typeRef.typeArguments?.forEach(visitType)
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
visitType(type)
|
|
189
|
-
return types
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export const getPropertyAssignment = (
|
|
193
|
-
obj: ts.ObjectLiteralExpression,
|
|
194
|
-
name: string
|
|
195
|
-
) => {
|
|
196
|
-
const property = obj.properties.find(
|
|
197
|
-
(p) =>
|
|
198
|
-
(ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
|
|
199
|
-
ts.isIdentifier(p.name) &&
|
|
200
|
-
p.name.text === name
|
|
201
|
-
)
|
|
202
|
-
if (!property) {
|
|
203
|
-
console.error(`Missing property '${name}' in object`)
|
|
204
|
-
return null
|
|
205
|
-
}
|
|
206
|
-
return property
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export const getTypeArgumentsOfType = (
|
|
210
|
-
checker: ts.TypeChecker,
|
|
211
|
-
type: ts.Type
|
|
212
|
-
): readonly ts.Type[] | null => {
|
|
213
|
-
if (type.isUnionOrIntersection()) {
|
|
214
|
-
const types: ts.Type[] = []
|
|
215
|
-
for (const subType of type.types) {
|
|
216
|
-
const subTypeArgs = getTypeArgumentsOfType(checker, subType)
|
|
217
|
-
if (subTypeArgs) {
|
|
218
|
-
types.push(...subTypeArgs)
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return types.length > 0 ? types : null
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// If the type is a TypeReference with typeArguments, return them
|
|
225
|
-
if (
|
|
226
|
-
type.flags & ts.TypeFlags.Object &&
|
|
227
|
-
(type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference
|
|
228
|
-
) {
|
|
229
|
-
const typeRef = type as ts.TypeReference
|
|
230
|
-
if (typeRef.typeArguments && typeRef.typeArguments.length > 0) {
|
|
231
|
-
return typeRef.typeArguments
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// If the type is an alias with aliasTypeArguments, return them
|
|
236
|
-
if (type.aliasTypeArguments && type.aliasTypeArguments.length > 0) {
|
|
237
|
-
return type.aliasTypeArguments as ts.Type[]
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return null
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export const getFunctionTypes = (
|
|
244
|
-
checker: ts.TypeChecker,
|
|
245
|
-
obj: ts.ObjectLiteralExpression,
|
|
246
|
-
{
|
|
247
|
-
typesMap,
|
|
248
|
-
funcName,
|
|
249
|
-
subFunctionName = funcName,
|
|
250
|
-
inputIndex,
|
|
251
|
-
outputIndex,
|
|
252
|
-
}: {
|
|
253
|
-
typesMap: TypesMap
|
|
254
|
-
subFunctionName?: string
|
|
255
|
-
funcName: string
|
|
256
|
-
inputIndex: number
|
|
257
|
-
outputIndex: number
|
|
258
|
-
}
|
|
259
|
-
): FunctionTypes => {
|
|
260
|
-
const result: FunctionTypes = {
|
|
261
|
-
inputTypes: [],
|
|
262
|
-
inputs: null,
|
|
263
|
-
outputTypes: [],
|
|
264
|
-
outputs: null,
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const property = getPropertyAssignment(obj, subFunctionName)
|
|
268
|
-
if (!property) {
|
|
269
|
-
return result
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
let type: ts.Type | undefined
|
|
273
|
-
|
|
274
|
-
// Handle shorthand property assignment
|
|
275
|
-
if (ts.isShorthandPropertyAssignment(property)) {
|
|
276
|
-
const symbol = checker.getShorthandAssignmentValueSymbol(property)
|
|
277
|
-
if (symbol) {
|
|
278
|
-
type = checker.getTypeOfSymbolAtLocation(symbol, property)
|
|
279
|
-
if (funcName === 'func') {
|
|
280
|
-
funcName = symbol.name
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// Handle regular property assignment
|
|
285
|
-
else if (ts.isPropertyAssignment(property)) {
|
|
286
|
-
if (ts.isObjectLiteralExpression(property.initializer)) {
|
|
287
|
-
return getFunctionTypes(checker, property.initializer, {
|
|
288
|
-
typesMap,
|
|
289
|
-
funcName,
|
|
290
|
-
subFunctionName: 'func',
|
|
291
|
-
inputIndex,
|
|
292
|
-
outputIndex,
|
|
293
|
-
})
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (property.initializer) {
|
|
297
|
-
type = checker.getTypeAtLocation(property.initializer)
|
|
298
|
-
if (funcName === 'func') {
|
|
299
|
-
funcName = property.initializer.getText()
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (!type) {
|
|
305
|
-
console.error(`Unable to resolve type for property '${funcName}'`)
|
|
306
|
-
return result
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Access type arguments from TypeReference
|
|
310
|
-
const typeArguments = getTypeArgumentsOfType(checker, type)
|
|
311
|
-
|
|
312
|
-
if (!typeArguments || typeArguments.length === 0) {
|
|
313
|
-
// This is the case for inline functions. In this case we would want to
|
|
314
|
-
// get the types from the second argument of the function...
|
|
315
|
-
console.error(
|
|
316
|
-
`\x1b[31m• No generic type arguments found for ${funcName}. Support for inline functions is not yet implemented.\x1b[0m`
|
|
317
|
-
)
|
|
318
|
-
return result
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (inputIndex !== undefined && inputIndex < typeArguments.length) {
|
|
322
|
-
const { names, types } = getNamesAndTypes(
|
|
323
|
-
checker,
|
|
324
|
-
typesMap,
|
|
325
|
-
'Input',
|
|
326
|
-
funcName,
|
|
327
|
-
typeArguments[inputIndex]!
|
|
328
|
-
)
|
|
329
|
-
result.inputs = names
|
|
330
|
-
result.inputTypes = types
|
|
331
|
-
} else {
|
|
332
|
-
console.log(`No input defined for ${funcName}`)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (outputIndex !== undefined && outputIndex < typeArguments.length) {
|
|
336
|
-
const { names, types } = getNamesAndTypes(
|
|
337
|
-
checker,
|
|
338
|
-
typesMap,
|
|
339
|
-
'Output',
|
|
340
|
-
funcName,
|
|
341
|
-
typeArguments[outputIndex]!
|
|
342
|
-
)
|
|
343
|
-
result.outputs = names
|
|
344
|
-
result.outputTypes = types
|
|
345
|
-
} else {
|
|
346
|
-
console.info(`No output defined for ${funcName}`)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return result
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
export const matchesFilters = (
|
|
353
|
-
filters: InspectorFilters,
|
|
354
|
-
params: { tags?: string[] },
|
|
355
|
-
meta: { type: 'schedule' | 'http' | 'channel'; name: string }
|
|
356
|
-
) => {
|
|
357
|
-
if (Object.keys(filters).length === 0 || filters.tags?.length === 0) {
|
|
358
|
-
return true
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (filters.tags?.some((tag) => params.tags?.includes(tag))) {
|
|
362
|
-
return true
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
console.debug(`⒡ Filtered: ${meta.type}:${meta.name}`)
|
|
366
|
-
return false
|
|
367
|
-
}
|