@pikku/inspector 0.7.0 → 0.7.1

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/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { inspect } from './inspector.js'
2
+ export type { TypesMap } from './types-map.js'
3
+ export type * from './types.js'
4
+ export type { InspectorState } from './types.js'
@@ -0,0 +1,75 @@
1
+ import * as ts from 'typescript'
2
+ import { visitSetup, visitRoutes } 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
+ functions: {
35
+ typesMap: new TypesMap(),
36
+ meta: {},
37
+ files: new Set(),
38
+ },
39
+ http: {
40
+ typesMap: new TypesMap(),
41
+ metaInputTypes: new Map(),
42
+ meta: [],
43
+ files: new Set(),
44
+ },
45
+ channels: {
46
+ typesMap: new TypesMap(),
47
+ metaInputTypes: new Map(),
48
+ files: new Set(),
49
+ meta: {},
50
+ },
51
+ scheduledTasks: {
52
+ meta: {},
53
+ files: new Set(),
54
+ },
55
+ }
56
+
57
+ // First sweep: add all functions
58
+ for (const sourceFile of sourceFiles) {
59
+ ts.forEachChild(sourceFile, (child) =>
60
+ visitSetup(checker, child, state, filters)
61
+ )
62
+ }
63
+
64
+ // Second sweep: add all transports
65
+ for (const sourceFile of sourceFiles) {
66
+ ts.forEachChild(sourceFile, (child) =>
67
+ visitRoutes(checker, child, state, filters)
68
+ )
69
+ }
70
+
71
+ // Normalise the typesMap
72
+ state.http = normalizeHTTPTypes(state.http)
73
+
74
+ return state
75
+ }
@@ -0,0 +1,130 @@
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 ADDED
@@ -0,0 +1,58 @@
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
+ import { FunctionsMeta } from '@pikku/core'
6
+
7
+ export type PathToNameAndType = Map<
8
+ string,
9
+ { variable: string; type: string | null; typePath: string | null }[]
10
+ >
11
+
12
+ export type MetaInputTypes = Map<
13
+ string,
14
+ {
15
+ query: string[] | undefined
16
+ params: string[] | undefined
17
+ body: string[] | undefined
18
+ }
19
+ >
20
+
21
+ export interface InspectorHTTPState {
22
+ typesMap: TypesMap
23
+ metaInputTypes: MetaInputTypes
24
+ meta: HTTPRoutesMeta
25
+ files: Set<string>
26
+ }
27
+
28
+ export interface InspectorFunctionState {
29
+ typesMap: TypesMap
30
+ meta: FunctionsMeta
31
+ files: Set<string>
32
+ }
33
+
34
+ export interface InspectorChannelState {
35
+ typesMap: TypesMap
36
+ metaInputTypes: MetaInputTypes
37
+ meta: ChannelsMeta
38
+ files: Set<string>
39
+ }
40
+
41
+ export type InspectorFilters = {
42
+ tags?: string[]
43
+ }
44
+ export interface InspectorState {
45
+ singletonServicesTypeImportMap: PathToNameAndType
46
+ sessionServicesTypeImportMap: PathToNameAndType
47
+ userSessionTypeImportMap: PathToNameAndType
48
+ singletonServicesFactories: PathToNameAndType
49
+ sessionServicesFactories: PathToNameAndType
50
+ configFactories: PathToNameAndType
51
+ http: InspectorHTTPState
52
+ functions: InspectorFunctionState
53
+ channels: InspectorChannelState
54
+ scheduledTasks: {
55
+ meta: ScheduledTasksMeta
56
+ files: Set<string>
57
+ }
58
+ }