@pikku/inspector 0.11.1 → 0.12.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 +26 -1
- package/OPTIMIZATION-PLAN.md +195 -0
- package/dist/add/add-ai-agent.d.ts +2 -0
- package/dist/add/add-ai-agent.js +314 -0
- package/dist/add/add-channel.js +69 -61
- package/dist/add/add-cli.js +36 -18
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.js +327 -59
- package/dist/add/add-http-route.d.ts +19 -10
- package/dist/add/add-http-route.js +153 -44
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +159 -0
- package/dist/add/add-keyed-wiring.d.ts +12 -0
- package/dist/add/add-keyed-wiring.js +97 -0
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.js +14 -9
- package/dist/add/add-middleware.d.ts +1 -4
- package/dist/add/add-middleware.js +364 -79
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +152 -40
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.d.ts +3 -0
- package/dist/add/add-rpc-invocations.js +65 -25
- package/dist/add/add-schedule.js +11 -5
- package/dist/add/add-secret.d.ts +3 -0
- package/dist/add/add-secret.js +82 -0
- package/dist/add/add-trigger.d.ts +2 -0
- package/dist/add/add-trigger.js +87 -0
- package/dist/add/add-variable.d.ts +1 -0
- package/dist/add/add-variable.js +8 -0
- package/dist/add/add-workflow-graph.d.ts +7 -0
- package/dist/add/add-workflow-graph.js +396 -0
- package/dist/add/add-workflow.js +124 -26
- package/dist/error-codes.d.ts +16 -1
- package/dist/error-codes.js +21 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +5 -2
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +106 -13
- package/dist/schema-generator.d.ts +1 -0
- package/dist/schema-generator.js +1 -0
- package/dist/types-map.js +10 -1
- package/dist/types.d.ts +180 -30
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +41 -0
- package/dist/utils/contract-hashes.d.ts +35 -0
- package/dist/utils/contract-hashes.js +202 -0
- package/dist/utils/custom-types-generator.d.ts +9 -0
- package/dist/utils/custom-types-generator.js +71 -0
- package/dist/utils/detect-schema-vendor.d.ts +22 -0
- package/dist/utils/detect-schema-vendor.js +76 -0
- package/dist/utils/ensure-function-metadata.d.ts +5 -2
- package/dist/utils/ensure-function-metadata.js +220 -6
- package/dist/utils/extract-function-name.d.ts +5 -16
- package/dist/utils/extract-function-name.js +93 -298
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/get-property-value.d.ts +8 -2
- package/dist/utils/get-property-value.js +33 -4
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +7 -30
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +2 -2
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +9 -10
- package/dist/utils/post-process.js +231 -24
- package/dist/utils/resolve-external-package.d.ts +12 -0
- package/dist/utils/resolve-external-package.js +34 -0
- package/dist/utils/resolve-function-types.d.ts +6 -0
- package/dist/utils/resolve-function-types.js +29 -0
- package/dist/utils/resolve-identifier.d.ts +10 -0
- package/dist/utils/resolve-identifier.js +36 -0
- package/dist/utils/resolve-versions.d.ts +2 -0
- package/dist/utils/resolve-versions.js +78 -0
- package/dist/utils/schema-generator.d.ts +9 -0
- package/dist/utils/schema-generator.js +209 -0
- package/dist/utils/serialize-inspector-state.d.ts +73 -13
- package/dist/utils/serialize-inspector-state.js +102 -6
- package/dist/utils/serialize-mcp-json.d.ts +2 -0
- package/dist/utils/serialize-mcp-json.js +99 -0
- package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
- package/dist/utils/serialize-middleware-groups-meta.js +28 -0
- package/dist/utils/serialize-openapi-json.d.ts +85 -0
- package/dist/utils/serialize-openapi-json.js +151 -0
- package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
- package/dist/utils/serialize-permissions-groups-meta.js +31 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +830 -0
- package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
- package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +572 -72
- package/dist/utils/workflow/dsl/index.d.ts +7 -0
- package/dist/utils/workflow/dsl/index.js +7 -0
- package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
- package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
- package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
- package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
- package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +318 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
- package/dist/utils/workflow/graph/index.d.ts +8 -0
- package/dist/utils/workflow/graph/index.js +8 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +35 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +150 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +203 -0
- package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
- package/dist/visit.js +13 -2
- package/package.json +26 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +82 -79
- package/src/add/add-cli.ts +49 -20
- package/src/add/add-file-with-factory.ts +2 -0
- package/src/add/add-functions.ts +429 -71
- package/src/add/add-http-route.ts +246 -65
- package/src/add/add-http-routes.ts +228 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +26 -15
- package/src/add/add-mcp-resource.ts +27 -15
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +24 -19
- package/src/add/add-rpc-invocations.ts +78 -31
- package/src/add/add-schedule.ts +16 -11
- package/src/add/add-secret.ts +140 -0
- package/src/add/add-trigger.ts +154 -0
- package/src/add/add-variable.ts +9 -0
- package/src/add/add-workflow-graph.ts +522 -0
- package/src/add/add-workflow.ts +117 -30
- package/src/error-codes.ts +26 -1
- package/src/index.ts +27 -8
- package/src/inspector.ts +145 -17
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +192 -51
- package/src/utils/compute-required-schemas.ts +49 -0
- package/src/utils/contract-hashes.test.ts +528 -0
- package/src/utils/contract-hashes.ts +290 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/ensure-function-metadata.ts +324 -7
- package/src/utils/extract-function-name.ts +108 -358
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +34 -20
- package/src/utils/filter-inspector-state.ts +140 -31
- package/src/utils/get-property-value.ts +50 -5
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +129 -67
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +10 -10
- package/src/utils/post-process.ts +283 -43
- package/src/utils/resolve-external-package.ts +42 -0
- package/src/utils/resolve-function-types.ts +42 -0
- package/src/utils/resolve-identifier.ts +46 -0
- package/src/utils/resolve-versions.test.ts +249 -0
- package/src/utils/resolve-versions.ts +105 -0
- package/src/utils/schema-generator.ts +329 -0
- package/src/utils/serialize-inspector-state.ts +181 -20
- package/src/utils/serialize-mcp-json.ts +145 -0
- package/src/utils/serialize-middleware-groups-meta.ts +33 -0
- package/src/utils/serialize-openapi-json.ts +277 -0
- package/src/utils/serialize-permissions-groups-meta.ts +35 -0
- package/src/utils/test-data/inspector-state.json +69 -66
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1104 -0
- package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +678 -85
- package/src/utils/workflow/dsl/index.ts +11 -0
- package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
- package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +422 -0
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
- package/src/utils/workflow/graph/index.ts +11 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +216 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +231 -0
- package/src/visit.ts +14 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-mcp-tool.d.ts +0 -2
- package/dist/add/add-mcp-tool.js +0 -81
- package/dist/utils/extract-service-metadata.d.ts +0 -19
- package/dist/utils/extract-service-metadata.js +0 -244
- package/dist/utils/write-service-metadata.d.ts +0 -13
- package/dist/utils/write-service-metadata.js +0 -37
- package/src/add/add-mcp-tool.ts +0 -141
- package/src/utils/extract-service-metadata.ts +0 -353
- package/src/utils/write-service-metadata.ts +0 -51
|
@@ -1,28 +1,53 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import { AddWiring } from '../types.js'
|
|
2
|
+
import type { AddWiring, InspectorState } from '../types.js'
|
|
3
3
|
import {
|
|
4
4
|
extractFunctionName,
|
|
5
5
|
isNamedExport,
|
|
6
|
+
makeContextBasedId,
|
|
6
7
|
} from '../utils/extract-function-name.js'
|
|
7
|
-
import {
|
|
8
|
-
|
|
8
|
+
import {
|
|
9
|
+
extractServicesFromFunction,
|
|
10
|
+
extractUsedWires,
|
|
11
|
+
} from '../utils/extract-services.js'
|
|
12
|
+
import { extractMiddlewareRefs } from '../utils/middleware.js'
|
|
9
13
|
import { getPropertyValue } from '../utils/get-property-value.js'
|
|
10
14
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
function renameTempDefinitions(
|
|
17
|
+
state: InspectorState,
|
|
18
|
+
definitionIds: string[],
|
|
19
|
+
groupType: string,
|
|
20
|
+
groupKey: string,
|
|
21
|
+
storeKey: 'middleware' | 'channelMiddleware' = 'middleware'
|
|
22
|
+
): void {
|
|
23
|
+
const tempIndices = definitionIds
|
|
24
|
+
.map((name, i) => (name.startsWith('__temp_') ? i : -1))
|
|
25
|
+
.filter((i) => i >= 0)
|
|
26
|
+
|
|
27
|
+
for (const idx of tempIndices) {
|
|
28
|
+
const oldId = definitionIds[idx]
|
|
29
|
+
const newId =
|
|
30
|
+
tempIndices.length === 1
|
|
31
|
+
? makeContextBasedId(groupType, groupKey)
|
|
32
|
+
: makeContextBasedId(groupType, groupKey, String(idx))
|
|
33
|
+
const existing = state[storeKey].definitions[oldId]
|
|
34
|
+
if (existing) {
|
|
35
|
+
delete state[storeKey].definitions[oldId]
|
|
36
|
+
state[storeKey].definitions[newId] = existing
|
|
37
|
+
}
|
|
38
|
+
definitionIds[idx] = newId
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
15
42
|
export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
16
43
|
if (!ts.isCallExpression(node)) return
|
|
17
44
|
|
|
18
45
|
const { expression, arguments: args } = node
|
|
19
46
|
|
|
20
|
-
// only handle specific function calls
|
|
21
47
|
if (!ts.isIdentifier(expression)) {
|
|
22
48
|
return
|
|
23
49
|
}
|
|
24
50
|
|
|
25
|
-
// Handle pikkuMiddleware(...) - individual middleware function definition
|
|
26
51
|
if (expression.text === 'pikkuMiddleware') {
|
|
27
52
|
const arg = args[0]
|
|
28
53
|
if (!arg) return
|
|
@@ -31,15 +56,12 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
31
56
|
let name: string | undefined
|
|
32
57
|
let description: string | undefined
|
|
33
58
|
|
|
34
|
-
// Check if using object syntax: pikkuMiddleware({ func: ..., name: '...', description: '...' })
|
|
35
59
|
if (ts.isObjectLiteralExpression(arg)) {
|
|
36
|
-
// Extract name and description metadata
|
|
37
60
|
const nameValue = getPropertyValue(arg, 'name')
|
|
38
61
|
const descValue = getPropertyValue(arg, 'description')
|
|
39
62
|
name = typeof nameValue === 'string' ? nameValue : undefined
|
|
40
63
|
description = typeof descValue === 'string' ? descValue : undefined
|
|
41
64
|
|
|
42
|
-
// Extract the func property
|
|
43
65
|
const fnProp = getPropertyAssignmentInitializer(
|
|
44
66
|
arg,
|
|
45
67
|
'func',
|
|
@@ -64,13 +86,34 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
64
86
|
}
|
|
65
87
|
|
|
66
88
|
const services = extractServicesFromFunction(actualHandler)
|
|
67
|
-
const
|
|
89
|
+
const wires = extractUsedWires(actualHandler, 1)
|
|
90
|
+
let { pikkuFuncId, exportedName } = extractFunctionName(
|
|
68
91
|
node,
|
|
69
92
|
checker,
|
|
70
93
|
state.rootDir
|
|
71
94
|
)
|
|
72
|
-
|
|
95
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
96
|
+
if (
|
|
97
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
98
|
+
ts.isIdentifier(node.parent.name)
|
|
99
|
+
) {
|
|
100
|
+
pikkuFuncId = node.parent.name.text
|
|
101
|
+
} else if (
|
|
102
|
+
ts.isPropertyAssignment(node.parent) &&
|
|
103
|
+
ts.isIdentifier(node.parent.name)
|
|
104
|
+
) {
|
|
105
|
+
pikkuFuncId = node.parent.name.text
|
|
106
|
+
} else {
|
|
107
|
+
logger.error(
|
|
108
|
+
`• pikkuMiddleware() must be assigned to a variable or object property. ` +
|
|
109
|
+
`Extract it to a const: const myMiddleware = pikkuMiddleware(...)`
|
|
110
|
+
)
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
state.middleware.definitions[pikkuFuncId] = {
|
|
73
115
|
services,
|
|
116
|
+
wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
|
|
74
117
|
sourceFile: node.getSourceFile().fileName,
|
|
75
118
|
position: node.getStart(),
|
|
76
119
|
exportedName,
|
|
@@ -84,7 +127,6 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
84
127
|
return
|
|
85
128
|
}
|
|
86
129
|
|
|
87
|
-
// Handle pikkuMiddlewareFactory(...) - middleware factory function
|
|
88
130
|
if (expression.text === 'pikkuMiddlewareFactory') {
|
|
89
131
|
const factoryNode = args[0]
|
|
90
132
|
if (!factoryNode) return
|
|
@@ -97,10 +139,13 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
97
139
|
return
|
|
98
140
|
}
|
|
99
141
|
|
|
100
|
-
// Extract services by looking inside the factory function body
|
|
101
|
-
// The factory should return pikkuMiddleware(...), so we need to find that call
|
|
102
|
-
// If no wrapper is found, extract from the factory's returned function directly
|
|
103
142
|
let services = { optimized: false, services: [] as string[] }
|
|
143
|
+
let wires: ReturnType<typeof extractUsedWires> = {
|
|
144
|
+
optimized: true,
|
|
145
|
+
wires: [],
|
|
146
|
+
}
|
|
147
|
+
let name: string | undefined
|
|
148
|
+
let description: string | undefined
|
|
104
149
|
|
|
105
150
|
const findPikkuMiddlewareCall = (
|
|
106
151
|
node: ts.Node
|
|
@@ -116,61 +161,96 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
116
161
|
|
|
117
162
|
const pikkuMiddlewareCall = findPikkuMiddlewareCall(factoryNode)
|
|
118
163
|
if (pikkuMiddlewareCall && pikkuMiddlewareCall.arguments[0]) {
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
164
|
+
const middlewareArg = pikkuMiddlewareCall.arguments[0]
|
|
165
|
+
if (ts.isObjectLiteralExpression(middlewareArg)) {
|
|
166
|
+
const nameValue = getPropertyValue(middlewareArg, 'name')
|
|
167
|
+
const descValue = getPropertyValue(middlewareArg, 'description')
|
|
168
|
+
name = typeof nameValue === 'string' ? nameValue : undefined
|
|
169
|
+
description = typeof descValue === 'string' ? descValue : undefined
|
|
170
|
+
|
|
171
|
+
const fnProp = getPropertyAssignmentInitializer(
|
|
172
|
+
middlewareArg,
|
|
173
|
+
'func',
|
|
174
|
+
true,
|
|
175
|
+
checker
|
|
176
|
+
)
|
|
177
|
+
if (
|
|
178
|
+
fnProp &&
|
|
179
|
+
(ts.isArrowFunction(fnProp) || ts.isFunctionExpression(fnProp))
|
|
180
|
+
) {
|
|
181
|
+
services = extractServicesFromFunction(fnProp)
|
|
182
|
+
wires = extractUsedWires(fnProp, 1)
|
|
183
|
+
}
|
|
184
|
+
} else if (
|
|
185
|
+
ts.isArrowFunction(middlewareArg) ||
|
|
186
|
+
ts.isFunctionExpression(middlewareArg)
|
|
123
187
|
) {
|
|
124
|
-
services = extractServicesFromFunction(
|
|
188
|
+
services = extractServicesFromFunction(middlewareArg)
|
|
189
|
+
wires = extractUsedWires(middlewareArg, 1)
|
|
125
190
|
}
|
|
126
191
|
} else {
|
|
127
|
-
// No pikkuMiddleware wrapper found - extract from factory's return value directly
|
|
128
|
-
// Factory pattern: (config) => (services, wire, next) => { ... }
|
|
129
192
|
if (
|
|
130
193
|
ts.isArrowFunction(factoryNode) ||
|
|
131
194
|
ts.isFunctionExpression(factoryNode)
|
|
132
195
|
) {
|
|
133
196
|
const factoryBody = factoryNode.body
|
|
134
|
-
// Check if the body is an arrow function (direct return)
|
|
135
197
|
if (
|
|
136
198
|
ts.isArrowFunction(factoryBody) ||
|
|
137
199
|
ts.isFunctionExpression(factoryBody)
|
|
138
200
|
) {
|
|
139
201
|
services = extractServicesFromFunction(factoryBody)
|
|
202
|
+
wires = extractUsedWires(factoryBody, 1)
|
|
140
203
|
}
|
|
141
204
|
}
|
|
142
205
|
}
|
|
143
206
|
|
|
144
|
-
|
|
207
|
+
let { pikkuFuncId, exportedName } = extractFunctionName(
|
|
145
208
|
node,
|
|
146
209
|
checker,
|
|
147
210
|
state.rootDir
|
|
148
211
|
)
|
|
149
|
-
|
|
212
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
213
|
+
if (
|
|
214
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
215
|
+
ts.isIdentifier(node.parent.name)
|
|
216
|
+
) {
|
|
217
|
+
pikkuFuncId = node.parent.name.text
|
|
218
|
+
} else if (
|
|
219
|
+
ts.isPropertyAssignment(node.parent) &&
|
|
220
|
+
ts.isIdentifier(node.parent.name)
|
|
221
|
+
) {
|
|
222
|
+
pikkuFuncId = node.parent.name.text
|
|
223
|
+
} else {
|
|
224
|
+
logger.error(
|
|
225
|
+
`• pikkuMiddlewareFactory() must be assigned to a variable or object property. ` +
|
|
226
|
+
`Extract it to a const: const myMiddleware = pikkuMiddlewareFactory(...)`
|
|
227
|
+
)
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
state.middleware.definitions[pikkuFuncId] = {
|
|
150
232
|
services,
|
|
233
|
+
wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
|
|
151
234
|
sourceFile: node.getSourceFile().fileName,
|
|
152
235
|
position: node.getStart(),
|
|
153
236
|
exportedName,
|
|
154
237
|
factory: true,
|
|
238
|
+
name,
|
|
239
|
+
description,
|
|
155
240
|
}
|
|
156
241
|
|
|
157
242
|
logger.debug(
|
|
158
|
-
`• Found middleware factory with services: ${services.services.join(', ')}`
|
|
243
|
+
`• Found middleware factory with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}`
|
|
159
244
|
)
|
|
160
245
|
return
|
|
161
246
|
}
|
|
162
247
|
|
|
163
|
-
// Handle addMiddleware('tag', [middleware1, middleware2])
|
|
164
|
-
// Supports two patterns:
|
|
165
|
-
// 1. export const x = () => addMiddleware('tag', [...]) (factory - tree-shakeable)
|
|
166
|
-
// 2. export const x = addMiddleware('tag', [...]) (direct - no tree-shaking)
|
|
167
248
|
if (expression.text === 'addMiddleware') {
|
|
168
249
|
const tagArg = args[0]
|
|
169
250
|
const middlewareArrayArg = args[1]
|
|
170
251
|
|
|
171
252
|
if (!tagArg || !middlewareArrayArg) return
|
|
172
253
|
|
|
173
|
-
// Extract tag name
|
|
174
254
|
let tag: string | undefined
|
|
175
255
|
if (ts.isStringLiteral(tagArg)) {
|
|
176
256
|
tag = tagArg.text
|
|
@@ -181,7 +261,6 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
181
261
|
return
|
|
182
262
|
}
|
|
183
263
|
|
|
184
|
-
// Check if middleware array is a literal array
|
|
185
264
|
if (!ts.isArrayLiteralExpression(middlewareArrayArg)) {
|
|
186
265
|
logger.error(
|
|
187
266
|
`• addMiddleware('${tag}', ...) must have a literal array as second argument`
|
|
@@ -189,47 +268,54 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
189
268
|
return
|
|
190
269
|
}
|
|
191
270
|
|
|
192
|
-
|
|
193
|
-
const middlewareNames = extractMiddlewarePikkuNames(
|
|
271
|
+
const refs = extractMiddlewareRefs(
|
|
194
272
|
middlewareArrayArg,
|
|
195
273
|
checker,
|
|
196
274
|
state.rootDir
|
|
197
275
|
)
|
|
198
276
|
|
|
199
|
-
if (
|
|
277
|
+
if (refs.length === 0) {
|
|
200
278
|
logger.warn(`• addMiddleware('${tag}', ...) has empty middleware array`)
|
|
201
279
|
return
|
|
202
280
|
}
|
|
203
281
|
|
|
204
|
-
|
|
282
|
+
const definitionIds = refs.map((r) => r.definitionId)
|
|
283
|
+
renameTempDefinitions(state, definitionIds, 'tag', tag)
|
|
284
|
+
|
|
285
|
+
const sourceFile = node.getSourceFile().fileName
|
|
286
|
+
const instanceIds: string[] = []
|
|
287
|
+
for (let i = 0; i < refs.length; i++) {
|
|
288
|
+
const instanceId = makeContextBasedId('tag', tag, String(i))
|
|
289
|
+
state.middleware.instances[instanceId] = {
|
|
290
|
+
definitionId: definitionIds[i],
|
|
291
|
+
sourceFile,
|
|
292
|
+
position: node.getStart(),
|
|
293
|
+
isFactoryCall: refs[i].isFactoryCall,
|
|
294
|
+
}
|
|
295
|
+
instanceIds.push(instanceId)
|
|
296
|
+
}
|
|
297
|
+
|
|
205
298
|
const allServices = new Set<string>()
|
|
206
|
-
for (const
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
for (const service of
|
|
299
|
+
for (const defId of definitionIds) {
|
|
300
|
+
const def = state.middleware.definitions[defId]
|
|
301
|
+
if (def?.services) {
|
|
302
|
+
for (const service of def.services.services) {
|
|
210
303
|
allServices.add(service)
|
|
211
304
|
}
|
|
212
305
|
}
|
|
213
306
|
}
|
|
214
307
|
|
|
215
|
-
// Check if this call is wrapped in a factory function
|
|
216
|
-
// We need to walk up the tree to see if the parent is: const x = () => addMiddleware(...)
|
|
217
308
|
let isFactory = false
|
|
218
309
|
let exportedName: string | null = null
|
|
219
310
|
let parent = node.parent
|
|
220
311
|
|
|
221
|
-
// Check if parent is arrow function: () => addMiddleware(...)
|
|
222
312
|
if (parent && ts.isArrowFunction(parent)) {
|
|
223
|
-
// Check if arrow function has no parameters
|
|
224
313
|
if (parent.parameters.length === 0) {
|
|
225
314
|
isFactory = true
|
|
226
315
|
|
|
227
|
-
// For factories, we need to check the arrow function's parent for the export name
|
|
228
|
-
// const apiTagMiddleware = () => addMiddleware(...)
|
|
229
316
|
const arrowParent = parent.parent
|
|
230
317
|
if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
|
|
231
318
|
if (ts.isIdentifier(arrowParent.name)) {
|
|
232
|
-
// Check if it's exported
|
|
233
319
|
if (isNamedExport(arrowParent)) {
|
|
234
320
|
exportedName = arrowParent.name.text
|
|
235
321
|
}
|
|
@@ -238,13 +324,11 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
238
324
|
}
|
|
239
325
|
}
|
|
240
326
|
|
|
241
|
-
// If not a factory, get export name from the call expression itself
|
|
242
327
|
if (!isFactory) {
|
|
243
328
|
const extracted = extractFunctionName(node, checker, state.rootDir)
|
|
244
329
|
exportedName = extracted.exportedName
|
|
245
330
|
}
|
|
246
331
|
|
|
247
|
-
// Log warning if not using factory pattern
|
|
248
332
|
if (!isFactory && exportedName) {
|
|
249
333
|
logger.warn(
|
|
250
334
|
`• Middleware group '${exportedName}' for tag '${tag}' is not wrapped in a factory function. ` +
|
|
@@ -252,36 +336,31 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
252
336
|
)
|
|
253
337
|
}
|
|
254
338
|
|
|
255
|
-
// Store group metadata
|
|
256
339
|
state.middleware.tagMiddleware.set(tag, {
|
|
257
340
|
exportName: exportedName,
|
|
258
|
-
sourceFile
|
|
341
|
+
sourceFile,
|
|
259
342
|
position: node.getStart(),
|
|
260
343
|
services: {
|
|
261
344
|
optimized: false,
|
|
262
345
|
services: Array.from(allServices),
|
|
263
346
|
},
|
|
264
|
-
|
|
347
|
+
count: refs.length,
|
|
348
|
+
instanceIds,
|
|
265
349
|
isFactory,
|
|
266
350
|
})
|
|
267
351
|
|
|
268
352
|
logger.debug(
|
|
269
|
-
`• Found tag middleware group: ${tag} -> [${
|
|
353
|
+
`• Found tag middleware group: ${tag} -> [${instanceIds.join(', ')}] (${isFactory ? 'factory' : 'direct'})`
|
|
270
354
|
)
|
|
271
355
|
return
|
|
272
356
|
}
|
|
273
357
|
|
|
274
|
-
// Handle addHTTPMiddleware(pattern, [middleware1, middleware2])
|
|
275
|
-
// Supports two patterns:
|
|
276
|
-
// 1. export const x = () => addHTTPMiddleware('*', [...]) (factory - tree-shakeable)
|
|
277
|
-
// 2. export const x = addHTTPMiddleware('*', [...]) (direct - no tree-shaking)
|
|
278
358
|
if (expression.text === 'addHTTPMiddleware') {
|
|
279
359
|
const patternArg = args[0]
|
|
280
360
|
const middlewareArrayArg = args[1]
|
|
281
361
|
|
|
282
362
|
if (!patternArg || !middlewareArrayArg) return
|
|
283
363
|
|
|
284
|
-
// Extract route pattern
|
|
285
364
|
let pattern: string | undefined
|
|
286
365
|
if (ts.isStringLiteral(patternArg)) {
|
|
287
366
|
pattern = patternArg.text
|
|
@@ -292,7 +371,6 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
292
371
|
return
|
|
293
372
|
}
|
|
294
373
|
|
|
295
|
-
// Check if middleware array is a literal array
|
|
296
374
|
if (!ts.isArrayLiteralExpression(middlewareArrayArg)) {
|
|
297
375
|
logger.error(
|
|
298
376
|
`• addHTTPMiddleware('${pattern}', ...) must have a literal array as second argument`
|
|
@@ -300,48 +378,56 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
300
378
|
return
|
|
301
379
|
}
|
|
302
380
|
|
|
303
|
-
|
|
304
|
-
const middlewareNames = extractMiddlewarePikkuNames(
|
|
381
|
+
const refs = extractMiddlewareRefs(
|
|
305
382
|
middlewareArrayArg,
|
|
306
383
|
checker,
|
|
307
384
|
state.rootDir
|
|
308
385
|
)
|
|
309
386
|
|
|
310
|
-
if (
|
|
387
|
+
if (refs.length === 0) {
|
|
311
388
|
logger.warn(
|
|
312
389
|
`• addHTTPMiddleware('${pattern}', ...) has empty middleware array`
|
|
313
390
|
)
|
|
314
391
|
return
|
|
315
392
|
}
|
|
316
393
|
|
|
317
|
-
|
|
394
|
+
const definitionIds = refs.map((r) => r.definitionId)
|
|
395
|
+
renameTempDefinitions(state, definitionIds, 'http', pattern)
|
|
396
|
+
|
|
397
|
+
const sourceFile = node.getSourceFile().fileName
|
|
398
|
+
const instanceIds: string[] = []
|
|
399
|
+
for (let i = 0; i < refs.length; i++) {
|
|
400
|
+
const instanceId = makeContextBasedId('http', pattern, String(i))
|
|
401
|
+
state.middleware.instances[instanceId] = {
|
|
402
|
+
definitionId: definitionIds[i],
|
|
403
|
+
sourceFile,
|
|
404
|
+
position: node.getStart(),
|
|
405
|
+
isFactoryCall: refs[i].isFactoryCall,
|
|
406
|
+
}
|
|
407
|
+
instanceIds.push(instanceId)
|
|
408
|
+
}
|
|
409
|
+
|
|
318
410
|
const allServices = new Set<string>()
|
|
319
|
-
for (const
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
for (const service of
|
|
411
|
+
for (const defId of definitionIds) {
|
|
412
|
+
const def = state.middleware.definitions[defId]
|
|
413
|
+
if (def?.services) {
|
|
414
|
+
for (const service of def.services.services) {
|
|
323
415
|
allServices.add(service)
|
|
324
416
|
}
|
|
325
417
|
}
|
|
326
418
|
}
|
|
327
419
|
|
|
328
|
-
// Check if this call is wrapped in a factory function
|
|
329
420
|
let isFactory = false
|
|
330
421
|
let exportedName: string | null = null
|
|
331
422
|
let parent = node.parent
|
|
332
423
|
|
|
333
|
-
// Check if parent is arrow function: () => addHTTPMiddleware(...)
|
|
334
424
|
if (parent && ts.isArrowFunction(parent)) {
|
|
335
|
-
// Check if arrow function has no parameters
|
|
336
425
|
if (parent.parameters.length === 0) {
|
|
337
426
|
isFactory = true
|
|
338
427
|
|
|
339
|
-
// For factories, we need to check the arrow function's parent for the export name
|
|
340
|
-
// const apiRouteMiddleware = () => addHTTPMiddleware(...)
|
|
341
428
|
const arrowParent = parent.parent
|
|
342
429
|
if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
|
|
343
430
|
if (ts.isIdentifier(arrowParent.name)) {
|
|
344
|
-
// Check if it's exported
|
|
345
431
|
if (isNamedExport(arrowParent)) {
|
|
346
432
|
exportedName = arrowParent.name.text
|
|
347
433
|
}
|
|
@@ -350,13 +436,11 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
350
436
|
}
|
|
351
437
|
}
|
|
352
438
|
|
|
353
|
-
// If not a factory, get export name from the call expression itself
|
|
354
439
|
if (!isFactory) {
|
|
355
440
|
const extracted = extractFunctionName(node, checker, state.rootDir)
|
|
356
441
|
exportedName = extracted.exportedName
|
|
357
442
|
}
|
|
358
443
|
|
|
359
|
-
// Log warning if not using factory pattern
|
|
360
444
|
if (!isFactory && exportedName) {
|
|
361
445
|
logger.warn(
|
|
362
446
|
`• HTTP middleware group '${exportedName}' for pattern '${pattern}' is not wrapped in a factory function. ` +
|
|
@@ -364,21 +448,339 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
364
448
|
)
|
|
365
449
|
}
|
|
366
450
|
|
|
367
|
-
// Store group metadata
|
|
368
451
|
state.http.routeMiddleware.set(pattern, {
|
|
369
452
|
exportName: exportedName,
|
|
453
|
+
sourceFile,
|
|
454
|
+
position: node.getStart(),
|
|
455
|
+
services: {
|
|
456
|
+
optimized: false,
|
|
457
|
+
services: Array.from(allServices),
|
|
458
|
+
},
|
|
459
|
+
count: refs.length,
|
|
460
|
+
instanceIds,
|
|
461
|
+
isFactory,
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
logger.debug(
|
|
465
|
+
`• Found HTTP route middleware group: ${pattern} -> [${instanceIds.join(', ')}] (${isFactory ? 'factory' : 'direct'})`
|
|
466
|
+
)
|
|
467
|
+
return
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (expression.text === 'pikkuChannelMiddleware') {
|
|
471
|
+
const arg = args[0]
|
|
472
|
+
if (!arg) return
|
|
473
|
+
|
|
474
|
+
let actualHandler: ts.ArrowFunction | ts.FunctionExpression
|
|
475
|
+
|
|
476
|
+
if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
|
|
477
|
+
actualHandler = arg
|
|
478
|
+
} else {
|
|
479
|
+
logger.error(`• Handler for pikkuChannelMiddleware is not a function.`)
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const services = extractServicesFromFunction(actualHandler)
|
|
484
|
+
let { pikkuFuncId, exportedName } = extractFunctionName(
|
|
485
|
+
node,
|
|
486
|
+
checker,
|
|
487
|
+
state.rootDir
|
|
488
|
+
)
|
|
489
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
490
|
+
if (
|
|
491
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
492
|
+
ts.isIdentifier(node.parent.name)
|
|
493
|
+
) {
|
|
494
|
+
pikkuFuncId = node.parent.name.text
|
|
495
|
+
} else if (
|
|
496
|
+
ts.isPropertyAssignment(node.parent) &&
|
|
497
|
+
ts.isIdentifier(node.parent.name)
|
|
498
|
+
) {
|
|
499
|
+
pikkuFuncId = node.parent.name.text
|
|
500
|
+
} else {
|
|
501
|
+
logger.error(
|
|
502
|
+
`• pikkuChannelMiddleware() must be assigned to a variable or object property. ` +
|
|
503
|
+
`Extract it to a const: const myMiddleware = pikkuChannelMiddleware(...)`
|
|
504
|
+
)
|
|
505
|
+
return
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
state.channelMiddleware.definitions[pikkuFuncId] = {
|
|
509
|
+
services,
|
|
510
|
+
sourceFile: node.getSourceFile().fileName,
|
|
511
|
+
position: node.getStart(),
|
|
512
|
+
exportedName,
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
logger.debug(
|
|
516
|
+
`• Found channel middleware with services: ${services.services.join(', ')}`
|
|
517
|
+
)
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (expression.text === 'pikkuChannelMiddlewareFactory') {
|
|
522
|
+
const factoryNode = args[0]
|
|
523
|
+
if (!factoryNode) return
|
|
524
|
+
|
|
525
|
+
if (
|
|
526
|
+
!ts.isArrowFunction(factoryNode) &&
|
|
527
|
+
!ts.isFunctionExpression(factoryNode)
|
|
528
|
+
) {
|
|
529
|
+
logger.error(
|
|
530
|
+
`• Handler for pikkuChannelMiddlewareFactory is not a function.`
|
|
531
|
+
)
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
let services = { optimized: false, services: [] as string[] }
|
|
536
|
+
|
|
537
|
+
const findPikkuChannelMiddlewareCall = (
|
|
538
|
+
n: ts.Node
|
|
539
|
+
): ts.CallExpression | undefined => {
|
|
540
|
+
if (ts.isCallExpression(n)) {
|
|
541
|
+
const expr = n.expression
|
|
542
|
+
if (ts.isIdentifier(expr) && expr.text === 'pikkuChannelMiddleware') {
|
|
543
|
+
return n
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return ts.forEachChild(n, findPikkuChannelMiddlewareCall)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const channelMiddlewareCall = findPikkuChannelMiddlewareCall(factoryNode)
|
|
550
|
+
if (channelMiddlewareCall && channelMiddlewareCall.arguments[0]) {
|
|
551
|
+
const middlewareHandler = channelMiddlewareCall.arguments[0]
|
|
552
|
+
if (
|
|
553
|
+
ts.isArrowFunction(middlewareHandler) ||
|
|
554
|
+
ts.isFunctionExpression(middlewareHandler)
|
|
555
|
+
) {
|
|
556
|
+
services = extractServicesFromFunction(middlewareHandler)
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
if (
|
|
560
|
+
ts.isArrowFunction(factoryNode) ||
|
|
561
|
+
ts.isFunctionExpression(factoryNode)
|
|
562
|
+
) {
|
|
563
|
+
const factoryBody = factoryNode.body
|
|
564
|
+
if (
|
|
565
|
+
ts.isArrowFunction(factoryBody) ||
|
|
566
|
+
ts.isFunctionExpression(factoryBody)
|
|
567
|
+
) {
|
|
568
|
+
services = extractServicesFromFunction(factoryBody)
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
let { pikkuFuncId, exportedName } = extractFunctionName(
|
|
574
|
+
node,
|
|
575
|
+
checker,
|
|
576
|
+
state.rootDir
|
|
577
|
+
)
|
|
578
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
579
|
+
if (
|
|
580
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
581
|
+
ts.isIdentifier(node.parent.name)
|
|
582
|
+
) {
|
|
583
|
+
pikkuFuncId = node.parent.name.text
|
|
584
|
+
} else if (
|
|
585
|
+
ts.isPropertyAssignment(node.parent) &&
|
|
586
|
+
ts.isIdentifier(node.parent.name)
|
|
587
|
+
) {
|
|
588
|
+
pikkuFuncId = node.parent.name.text
|
|
589
|
+
} else {
|
|
590
|
+
logger.error(
|
|
591
|
+
`• pikkuChannelMiddlewareFactory() must be assigned to a variable or object property. ` +
|
|
592
|
+
`Extract it to a const: const myMiddleware = pikkuChannelMiddlewareFactory(...)`
|
|
593
|
+
)
|
|
594
|
+
return
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
state.channelMiddleware.definitions[pikkuFuncId] = {
|
|
598
|
+
services,
|
|
370
599
|
sourceFile: node.getSourceFile().fileName,
|
|
371
600
|
position: node.getStart(),
|
|
601
|
+
exportedName,
|
|
602
|
+
factory: true,
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
logger.debug(
|
|
606
|
+
`• Found channel middleware factory with services: ${services.services.join(', ')}`
|
|
607
|
+
)
|
|
608
|
+
return
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (expression.text === 'pikkuAIMiddleware') {
|
|
612
|
+
const arg = args[0]
|
|
613
|
+
if (!arg) return
|
|
614
|
+
|
|
615
|
+
if (!ts.isObjectLiteralExpression(arg)) {
|
|
616
|
+
logger.error(`• pikkuAIMiddleware() requires an object literal argument.`)
|
|
617
|
+
return
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const allServices = new Set<string>()
|
|
621
|
+
for (const prop of arg.properties) {
|
|
622
|
+
if (
|
|
623
|
+
ts.isPropertyAssignment(prop) &&
|
|
624
|
+
(ts.isArrowFunction(prop.initializer) ||
|
|
625
|
+
ts.isFunctionExpression(prop.initializer))
|
|
626
|
+
) {
|
|
627
|
+
const hookServices = extractServicesFromFunction(prop.initializer)
|
|
628
|
+
for (const s of hookServices.services) {
|
|
629
|
+
allServices.add(s)
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const services = {
|
|
635
|
+
optimized: allServices.size > 0,
|
|
636
|
+
services: Array.from(allServices),
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
let { pikkuFuncId, exportedName } = extractFunctionName(
|
|
640
|
+
node,
|
|
641
|
+
checker,
|
|
642
|
+
state.rootDir
|
|
643
|
+
)
|
|
644
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
645
|
+
if (
|
|
646
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
647
|
+
ts.isIdentifier(node.parent.name)
|
|
648
|
+
) {
|
|
649
|
+
pikkuFuncId = node.parent.name.text
|
|
650
|
+
} else if (
|
|
651
|
+
ts.isPropertyAssignment(node.parent) &&
|
|
652
|
+
ts.isIdentifier(node.parent.name)
|
|
653
|
+
) {
|
|
654
|
+
pikkuFuncId = node.parent.name.text
|
|
655
|
+
} else {
|
|
656
|
+
logger.error(
|
|
657
|
+
`• pikkuAIMiddleware() must be assigned to a variable or object property. ` +
|
|
658
|
+
`Extract it to a const: const myMiddleware = pikkuAIMiddleware(...)`
|
|
659
|
+
)
|
|
660
|
+
return
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
state.aiMiddleware.definitions[pikkuFuncId] = {
|
|
664
|
+
services,
|
|
665
|
+
sourceFile: node.getSourceFile().fileName,
|
|
666
|
+
position: node.getStart(),
|
|
667
|
+
exportedName,
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
logger.debug(
|
|
671
|
+
`• Found AI middleware with services: ${services.services.join(', ')}`
|
|
672
|
+
)
|
|
673
|
+
return
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (expression.text === 'addChannelMiddleware') {
|
|
677
|
+
const tagArg = args[0]
|
|
678
|
+
const middlewareArrayArg = args[1]
|
|
679
|
+
|
|
680
|
+
if (!tagArg || !middlewareArrayArg) return
|
|
681
|
+
|
|
682
|
+
let tag: string | undefined
|
|
683
|
+
if (ts.isStringLiteral(tagArg)) {
|
|
684
|
+
tag = tagArg.text
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (!tag) {
|
|
688
|
+
logger.warn(`• addChannelMiddleware call without valid tag string`)
|
|
689
|
+
return
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (!ts.isArrayLiteralExpression(middlewareArrayArg)) {
|
|
693
|
+
logger.error(
|
|
694
|
+
`• addChannelMiddleware('${tag}', ...) must have a literal array as second argument`
|
|
695
|
+
)
|
|
696
|
+
return
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const refs = extractMiddlewareRefs(
|
|
700
|
+
middlewareArrayArg,
|
|
701
|
+
checker,
|
|
702
|
+
state.rootDir
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
if (refs.length === 0) {
|
|
706
|
+
logger.warn(
|
|
707
|
+
`• addChannelMiddleware('${tag}', ...) has empty middleware array`
|
|
708
|
+
)
|
|
709
|
+
return
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const definitionIds = refs.map((r) => r.definitionId)
|
|
713
|
+
renameTempDefinitions(state, definitionIds, 'tag', tag, 'channelMiddleware')
|
|
714
|
+
|
|
715
|
+
const sourceFile = node.getSourceFile().fileName
|
|
716
|
+
const instanceIds: string[] = []
|
|
717
|
+
for (let i = 0; i < refs.length; i++) {
|
|
718
|
+
const instanceId = makeContextBasedId('tag', tag, String(i))
|
|
719
|
+
state.channelMiddleware.instances[instanceId] = {
|
|
720
|
+
definitionId: definitionIds[i],
|
|
721
|
+
sourceFile,
|
|
722
|
+
position: node.getStart(),
|
|
723
|
+
isFactoryCall: refs[i].isFactoryCall,
|
|
724
|
+
}
|
|
725
|
+
instanceIds.push(instanceId)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const allServices = new Set<string>()
|
|
729
|
+
for (const defId of definitionIds) {
|
|
730
|
+
const def = state.channelMiddleware.definitions[defId]
|
|
731
|
+
if (def?.services) {
|
|
732
|
+
for (const service of def.services.services) {
|
|
733
|
+
allServices.add(service)
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
let isFactory = false
|
|
739
|
+
let exportedName: string | null = null
|
|
740
|
+
let parent = node.parent
|
|
741
|
+
|
|
742
|
+
if (parent && ts.isArrowFunction(parent)) {
|
|
743
|
+
if (parent.parameters.length === 0) {
|
|
744
|
+
isFactory = true
|
|
745
|
+
|
|
746
|
+
const arrowParent = parent.parent
|
|
747
|
+
if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
|
|
748
|
+
if (ts.isIdentifier(arrowParent.name)) {
|
|
749
|
+
if (isNamedExport(arrowParent)) {
|
|
750
|
+
exportedName = arrowParent.name.text
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (!isFactory) {
|
|
758
|
+
const extracted = extractFunctionName(node, checker, state.rootDir)
|
|
759
|
+
exportedName = extracted.exportedName
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (!isFactory && exportedName) {
|
|
763
|
+
logger.warn(
|
|
764
|
+
`• Channel middleware group '${exportedName}' for tag '${tag}' is not wrapped in a factory function. ` +
|
|
765
|
+
`For tree-shaking, use: export const ${exportedName} = () => addChannelMiddleware('${tag}', [...])`
|
|
766
|
+
)
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
state.channelMiddleware.tagMiddleware.set(tag, {
|
|
770
|
+
exportName: exportedName,
|
|
771
|
+
sourceFile,
|
|
772
|
+
position: node.getStart(),
|
|
372
773
|
services: {
|
|
373
774
|
optimized: false,
|
|
374
775
|
services: Array.from(allServices),
|
|
375
776
|
},
|
|
376
|
-
|
|
777
|
+
count: refs.length,
|
|
778
|
+
instanceIds,
|
|
377
779
|
isFactory,
|
|
378
780
|
})
|
|
379
781
|
|
|
380
782
|
logger.debug(
|
|
381
|
-
`• Found
|
|
783
|
+
`• Found tag channel middleware group: ${tag} -> [${instanceIds.join(', ')}] (${isFactory ? 'factory' : 'direct'})`
|
|
382
784
|
)
|
|
383
785
|
return
|
|
384
786
|
}
|