moonflower 1.4.8 → 1.4.9
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/openapi/analyzerModule/analyzerModule.cjs +1 -1
- package/dist/openapi/analyzerModule/analyzerModule.cjs.map +1 -1
- package/dist/openapi/analyzerModule/analyzerModule.d.ts +16 -2
- package/dist/openapi/analyzerModule/analyzerModule.d.ts.map +1 -1
- package/dist/openapi/analyzerModule/analyzerModule.mjs +123 -105
- package/dist/openapi/analyzerModule/analyzerModule.mjs.map +1 -1
- package/dist/openapi/analyzerModule/parseEndpoint.cjs +1 -1
- package/dist/openapi/analyzerModule/parseEndpoint.cjs.map +1 -1
- package/dist/openapi/analyzerModule/parseEndpoint.d.ts +8 -1
- package/dist/openapi/analyzerModule/parseEndpoint.d.ts.map +1 -1
- package/dist/openapi/analyzerModule/parseEndpoint.mjs +121 -106
- package/dist/openapi/analyzerModule/parseEndpoint.mjs.map +1 -1
- package/package.json +1 -1
- package/src/openapi/analyzerModule/analyzerModule.ts +63 -20
- package/src/openapi/analyzerModule/parseEndpoint.ts +31 -9
- package/src/openapi/analyzerModule/test/openApiAnalyzer.spec.ts +2 -2
- package/src/openapi/analyzerModule/test/openApiAnalyzer.zod.spec.ts +1 -1
|
@@ -13,7 +13,7 @@ import { ApiDocsHeader, OpenApiManager } from '../manager/OpenApiManager'
|
|
|
13
13
|
import { EndpointData, ExposedModelData } from '../types'
|
|
14
14
|
import { getSourceFileTimestamp, TimestampCache } from './getSourceFileTimestamp'
|
|
15
15
|
import { getValuesOfObjectLiteral, resolveEndpointPath } from './nodeParsers'
|
|
16
|
-
import { parseEndpoint } from './parseEndpoint'
|
|
16
|
+
import { parseEndpoint, SectionTiming } from './parseEndpoint'
|
|
17
17
|
import { parseExposedModel, parseNamedExposedModels } from './parseExposedModels'
|
|
18
18
|
import { SourceFileCache } from './sourceFileCache'
|
|
19
19
|
|
|
@@ -27,12 +27,15 @@ type Props = {
|
|
|
27
27
|
| {
|
|
28
28
|
cachePath: string
|
|
29
29
|
}
|
|
30
|
+
profiling?: 'stats' | 'off' | 'debug'
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
type FileDiscoveryConfig = {
|
|
33
34
|
rootPath: string
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
type EndpointTiming = { method: string; path: string; timing: number; sectionTimings: SectionTiming[] }
|
|
38
|
+
|
|
36
39
|
/**
|
|
37
40
|
* @param tsconfigPath Path to tsconfig file relative to project root
|
|
38
41
|
* @param sourceFilePaths Array of router source files relative to project root
|
|
@@ -43,6 +46,7 @@ export const prepareOpenApiSpec = ({
|
|
|
43
46
|
sourceFilePaths,
|
|
44
47
|
sourceFileDiscovery,
|
|
45
48
|
incremental,
|
|
49
|
+
profiling = 'stats',
|
|
46
50
|
}: Props) => {
|
|
47
51
|
const openApiManager = OpenApiManager.getInstance()
|
|
48
52
|
|
|
@@ -81,7 +85,9 @@ export const prepareOpenApiSpec = ({
|
|
|
81
85
|
targetPath: typeof sourceFileDiscovery === 'object' ? sourceFileDiscovery.rootPath : '.',
|
|
82
86
|
tsConfigPath: tsconfigPath,
|
|
83
87
|
})
|
|
84
|
-
|
|
88
|
+
if (profiling !== 'off') {
|
|
89
|
+
Logger.info(`File discovery took ${Math.round(performance.now() - startTime)}ms`)
|
|
90
|
+
}
|
|
85
91
|
return files
|
|
86
92
|
})()
|
|
87
93
|
|
|
@@ -120,6 +126,7 @@ export const prepareOpenApiSpec = ({
|
|
|
120
126
|
incremental: incremental !== false,
|
|
121
127
|
cachePath,
|
|
122
128
|
timestampCache: {},
|
|
129
|
+
profiling,
|
|
123
130
|
})
|
|
124
131
|
|
|
125
132
|
openApiManager.setStats({
|
|
@@ -153,23 +160,49 @@ export const analyzeMultipleSourceFiles = (
|
|
|
153
160
|
incremental: boolean
|
|
154
161
|
cachePath: string
|
|
155
162
|
timestampCache: TimestampCache
|
|
163
|
+
profiling?: 'stats' | 'off' | 'debug'
|
|
156
164
|
},
|
|
157
165
|
filterEndpointPaths?: string[],
|
|
158
166
|
): EndpointData[] => {
|
|
167
|
+
const profiling = config.profiling ?? 'stats'
|
|
159
168
|
const startTime = performance.now()
|
|
160
169
|
const analyzedFiles = files.map((file) => analyzeSourceFileWithCache(file, config, filterEndpointPaths))
|
|
161
|
-
Logger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)
|
|
162
170
|
|
|
163
|
-
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
if (profiling !== 'off') {
|
|
172
|
+
Logger.info(`Router analysis took ${Math.round(performance.now() - startTime)}ms`)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (profiling === 'stats') {
|
|
176
|
+
analyzedFiles
|
|
177
|
+
.map((f, index) => ({ fileName: files[index].fileName, timeTaken: f.timing }))
|
|
178
|
+
.sort((a, b) => b.timeTaken - a.timeTaken)
|
|
179
|
+
.filter((t) => t.timeTaken > 500)
|
|
180
|
+
.forEach((t) => {
|
|
181
|
+
Logger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)
|
|
182
|
+
})
|
|
183
|
+
} else if (profiling === 'debug') {
|
|
184
|
+
analyzedFiles
|
|
185
|
+
.map((f, index) => ({
|
|
186
|
+
fileName: files[index].fileName,
|
|
187
|
+
timeTaken: f.timing,
|
|
188
|
+
endpointTimings: f.endpointTimings,
|
|
189
|
+
}))
|
|
190
|
+
.sort((a, b) => b.timeTaken - a.timeTaken)
|
|
191
|
+
.forEach((t) => {
|
|
192
|
+
Logger.info(`- [${t.fileName}] Took ${Math.round(t.timeTaken)}ms to analyze`)
|
|
193
|
+
t.endpointTimings
|
|
194
|
+
.sort((a, b) => b.timing - a.timing)
|
|
195
|
+
.forEach((ep) => {
|
|
196
|
+
Logger.info(` - ${ep.method} ${ep.path} (${Math.round(ep.timing)}ms)`)
|
|
197
|
+
ep.sectionTimings
|
|
198
|
+
.filter((s) => s.timing >= 1)
|
|
199
|
+
.sort((a, b) => b.timing - a.timing)
|
|
200
|
+
.forEach((s) => {
|
|
201
|
+
Logger.info(` - ${s.section}: ${Math.round(s.timing)}ms`)
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
}
|
|
173
206
|
|
|
174
207
|
return analyzedFiles.flatMap((f) => f.endpoints)
|
|
175
208
|
}
|
|
@@ -180,31 +213,33 @@ export const analyzeSourceFileWithCache = (
|
|
|
180
213
|
incremental: boolean
|
|
181
214
|
cachePath: string
|
|
182
215
|
timestampCache: TimestampCache
|
|
216
|
+
profiling?: 'stats' | 'off' | 'debug'
|
|
183
217
|
},
|
|
184
218
|
filterEndpointPaths?: string[],
|
|
185
|
-
): { endpoints: EndpointData[]; timing: number } => {
|
|
219
|
+
): { endpoints: EndpointData[]; timing: number; endpointTimings: EndpointTiming[] } => {
|
|
186
220
|
const timestamp = getSourceFileTimestamp(file.sourceFile, config.timestampCache)
|
|
187
221
|
const cachedResults = SourceFileCache.getCachedResults(file.sourceFile, timestamp, config.cachePath)
|
|
188
222
|
|
|
189
223
|
if (cachedResults) {
|
|
190
224
|
Logger.debug(`[${file.fileName}] Found cached results`)
|
|
191
|
-
return { endpoints: cachedResults.endpoints, timing: 0 }
|
|
225
|
+
return { endpoints: cachedResults.endpoints, timing: 0, endpointTimings: [] }
|
|
192
226
|
}
|
|
193
227
|
Logger.debug(`[${file.fileName}] Analyzing...`)
|
|
194
228
|
|
|
195
229
|
const t1 = performance.now()
|
|
196
|
-
const endpoints = analyzeSourceFileEndpoints(file, filterEndpointPaths)
|
|
230
|
+
const { endpoints, endpointTimings } = analyzeSourceFileEndpoints(file, filterEndpointPaths)
|
|
197
231
|
const t2 = performance.now()
|
|
198
232
|
Logger.debug(`[${file.fileName}] Analyzed in ${t2 - t1}ms`)
|
|
199
233
|
SourceFileCache.cacheResults(file.sourceFile, timestamp, config.cachePath, endpoints)
|
|
200
|
-
return { endpoints, timing: t2 - t1 }
|
|
234
|
+
return { endpoints, timing: t2 - t1, endpointTimings }
|
|
201
235
|
}
|
|
202
236
|
|
|
203
237
|
export const analyzeSourceFileEndpoints = (
|
|
204
238
|
file: DiscoveredSourceFile,
|
|
205
239
|
filterEndpointPaths?: string[],
|
|
206
|
-
): EndpointData[] => {
|
|
240
|
+
): { endpoints: EndpointData[]; endpointTimings: EndpointTiming[] } => {
|
|
207
241
|
const endpoints: EndpointData[] = []
|
|
242
|
+
const endpointTimings: EndpointTiming[] = []
|
|
208
243
|
const operations = ['get', 'post', 'put', 'delete', 'del', 'patch']
|
|
209
244
|
const joinedOperations = operations.join('|')
|
|
210
245
|
|
|
@@ -220,12 +255,20 @@ export const analyzeSourceFileEndpoints = (
|
|
|
220
255
|
return
|
|
221
256
|
}
|
|
222
257
|
|
|
223
|
-
|
|
258
|
+
const t1 = performance.now()
|
|
259
|
+
const { endpoint, sectionTimings } = parseEndpoint(node, file.fileName)
|
|
260
|
+
endpointTimings.push({
|
|
261
|
+
method: endpoint.method,
|
|
262
|
+
path: endpoint.path,
|
|
263
|
+
timing: performance.now() - t1,
|
|
264
|
+
sectionTimings,
|
|
265
|
+
})
|
|
266
|
+
endpoints.push(endpoint)
|
|
224
267
|
}
|
|
225
268
|
})
|
|
226
269
|
})
|
|
227
270
|
|
|
228
|
-
return endpoints
|
|
271
|
+
return { endpoints, endpointTimings }
|
|
229
272
|
}
|
|
230
273
|
|
|
231
274
|
export const analyzeSourceFileApiHeader = (sourceFile: SourceFile): ApiDocsHeader | null => {
|
|
@@ -14,7 +14,12 @@ import {
|
|
|
14
14
|
resolveEndpointPath,
|
|
15
15
|
} from './nodeParsers'
|
|
16
16
|
|
|
17
|
-
export
|
|
17
|
+
export type SectionTiming = { section: string; timing: number }
|
|
18
|
+
|
|
19
|
+
export const parseEndpoint = (
|
|
20
|
+
node: Node<ts.Node>,
|
|
21
|
+
sourceFilePath: string,
|
|
22
|
+
): { endpoint: EndpointData; sectionTimings: SectionTiming[] } => {
|
|
18
23
|
const parsedEndpointMethod = node
|
|
19
24
|
.getFirstDescendantByKind(SyntaxKind.PropertyAccessExpression)!
|
|
20
25
|
.getText()
|
|
@@ -46,11 +51,20 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
46
51
|
error: Error
|
|
47
52
|
}[] = []
|
|
48
53
|
|
|
54
|
+
const sectionTimings: SectionTiming[] = []
|
|
55
|
+
|
|
56
|
+
const time = <T>(section: string, fn: () => T): T => {
|
|
57
|
+
const t1 = performance.now()
|
|
58
|
+
const result = fn()
|
|
59
|
+
sectionTimings.push({ section, timing: performance.now() - t1 })
|
|
60
|
+
return result
|
|
61
|
+
}
|
|
62
|
+
|
|
49
63
|
const hookNodes = getHookNodes(node)
|
|
50
64
|
|
|
51
65
|
// API documentation
|
|
52
66
|
try {
|
|
53
|
-
const entries = parseApiDocumentation(hookNodes.useApiEndpoint)
|
|
67
|
+
const entries = time('useApiEndpoint', () => parseApiDocumentation(hookNodes.useApiEndpoint))
|
|
54
68
|
entries.forEach((param) => {
|
|
55
69
|
endpointData[param.identifier] = param.value as string & string[]
|
|
56
70
|
})
|
|
@@ -64,7 +78,9 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
64
78
|
|
|
65
79
|
// Request params
|
|
66
80
|
try {
|
|
67
|
-
endpointData.requestPathParams =
|
|
81
|
+
endpointData.requestPathParams = time('usePathParams', () =>
|
|
82
|
+
parseRequestParams(hookNodes.usePathParams, endpointPath),
|
|
83
|
+
)
|
|
68
84
|
} catch (err) {
|
|
69
85
|
warningData.push({
|
|
70
86
|
segment: 'path',
|
|
@@ -75,7 +91,9 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
75
91
|
|
|
76
92
|
// Request query
|
|
77
93
|
try {
|
|
78
|
-
endpointData.requestQuery =
|
|
94
|
+
endpointData.requestQuery = time('useQueryParams', () =>
|
|
95
|
+
parseRequestObjectInput(hookNodes.useQueryParams, 'useQueryParams'),
|
|
96
|
+
)
|
|
79
97
|
} catch (err) {
|
|
80
98
|
warningData.push({
|
|
81
99
|
segment: 'query',
|
|
@@ -86,7 +104,9 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
86
104
|
|
|
87
105
|
// Request headers
|
|
88
106
|
try {
|
|
89
|
-
endpointData.requestHeaders =
|
|
107
|
+
endpointData.requestHeaders = time('useHeaderParams', () =>
|
|
108
|
+
parseRequestObjectInput(hookNodes.useHeaderParams, 'useHeaderParams'),
|
|
109
|
+
)
|
|
90
110
|
} catch (err) {
|
|
91
111
|
warningData.push({
|
|
92
112
|
segment: 'headers',
|
|
@@ -97,7 +117,7 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
97
117
|
|
|
98
118
|
// Raw request body
|
|
99
119
|
try {
|
|
100
|
-
const parsedBody = parseRequestRawBody(hookNodes.useRequestRawBody)
|
|
120
|
+
const parsedBody = time('useRequestRawBody', () => parseRequestRawBody(hookNodes.useRequestRawBody))
|
|
101
121
|
if (parsedBody) {
|
|
102
122
|
endpointData.rawBody = parsedBody
|
|
103
123
|
}
|
|
@@ -111,7 +131,9 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
111
131
|
|
|
112
132
|
// Object request body
|
|
113
133
|
try {
|
|
114
|
-
endpointData.objectBody =
|
|
134
|
+
endpointData.objectBody = time('useRequestBody', () =>
|
|
135
|
+
parseRequestObjectInput(hookNodes.useRequestBody, 'useRequestBody'),
|
|
136
|
+
)
|
|
115
137
|
} catch (err) {
|
|
116
138
|
warningData.push({
|
|
117
139
|
segment: 'objectBody',
|
|
@@ -122,7 +144,7 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
122
144
|
|
|
123
145
|
// Request response
|
|
124
146
|
try {
|
|
125
|
-
endpointData.responses = parseRequestResponse(node)
|
|
147
|
+
endpointData.responses = time('response', () => parseRequestResponse(node))
|
|
126
148
|
} catch (err) {
|
|
127
149
|
warningData.push({
|
|
128
150
|
segment: 'response',
|
|
@@ -131,7 +153,7 @@ export const parseEndpoint = (node: Node<ts.Node>, sourceFilePath: string) => {
|
|
|
131
153
|
Logger.error('Error', err)
|
|
132
154
|
}
|
|
133
155
|
|
|
134
|
-
return endpointData
|
|
156
|
+
return { endpoint: endpointData, sectionTimings }
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
type HookName =
|
|
@@ -23,7 +23,7 @@ describe('OpenApi Analyzer', () => {
|
|
|
23
23
|
},
|
|
24
24
|
[`/test/${id}`],
|
|
25
25
|
)
|
|
26
|
-
const endpoint = analysisResult.find((endpoint) => endpoint.path.startsWith(`/test/${id}`))
|
|
26
|
+
const endpoint = analysisResult.endpoints.find((endpoint) => endpoint.path.startsWith(`/test/${id}`))
|
|
27
27
|
if (!endpoint) {
|
|
28
28
|
throw new Error(`No endpoint with id ${id} found!`)
|
|
29
29
|
}
|
|
@@ -39,7 +39,7 @@ describe('OpenApi Analyzer', () => {
|
|
|
39
39
|
},
|
|
40
40
|
[`/test/${id}`],
|
|
41
41
|
)
|
|
42
|
-
const endpoints = analysisResult.filter((endpoint) => endpoint.path.startsWith(`/test/${id}`))
|
|
42
|
+
const endpoints = analysisResult.endpoints.filter((endpoint) => endpoint.path.startsWith(`/test/${id}`))
|
|
43
43
|
if (endpoints.length === 0) {
|
|
44
44
|
throw new Error(`No endpoint with id ${id} found!`)
|
|
45
45
|
}
|
|
@@ -19,7 +19,7 @@ describe('OpenApi Analyzer (Zod Validator)', () => {
|
|
|
19
19
|
},
|
|
20
20
|
[`/test/${id}`],
|
|
21
21
|
)
|
|
22
|
-
const endpoint = analysisResult.find((endpoint) => endpoint.path.startsWith(`/test/${id}`))
|
|
22
|
+
const endpoint = analysisResult.endpoints.find((endpoint) => endpoint.path.startsWith(`/test/${id}`))
|
|
23
23
|
if (!endpoint) {
|
|
24
24
|
throw new Error(`No endpoint with id ${id} found!`)
|
|
25
25
|
}
|