@pikku/inspector 0.11.2 → 0.12.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/CHANGELOG.md +36 -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 +81 -61
- package/dist/add/add-cli.d.ts +1 -1
- package/dist/add/add-cli.js +42 -19
- package/dist/add/add-file-extends-core-type.d.ts +1 -1
- package/dist/add/add-file-with-config.d.ts +1 -1
- package/dist/add/add-file-with-factory.d.ts +1 -1
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.d.ts +1 -1
- package/dist/add/add-functions.js +256 -82
- package/dist/add/add-http-route.d.ts +20 -10
- package/dist/add/add-http-route.js +156 -66
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +160 -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.d.ts +1 -1
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.d.ts +1 -1
- 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.d.ts +1 -1
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.d.ts +3 -3
- package/dist/add/add-rpc-invocations.js +24 -10
- package/dist/add/add-schedule.d.ts +1 -1
- 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-wire-addon.d.ts +7 -0
- package/dist/add/add-wire-addon.js +70 -0
- package/dist/add/add-workflow-graph.d.ts +3 -2
- package/dist/add/add-workflow-graph.js +143 -406
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/add/add-workflow.js +6 -4
- package/dist/error-codes.d.ts +15 -1
- package/dist/error-codes.js +20 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -4
- package/dist/inspector.d.ts +2 -2
- package/dist/inspector.js +95 -15
- 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 -50
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +40 -0
- package/dist/utils/contract-hashes.d.ts +52 -0
- package/dist/utils/contract-hashes.js +269 -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/does-type-extend-core-type.d.ts +1 -1
- package/dist/utils/ensure-function-metadata.d.ts +6 -3
- 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 +86 -291
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.d.ts +1 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/filter-utils.d.ts +2 -2
- package/dist/utils/get-files-and-methods.d.ts +1 -1
- package/dist/utils/get-property-value.d.ts +6 -1
- package/dist/utils/get-property-value.js +28 -3
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +9 -32
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +4 -4
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +11 -11
- package/dist/utils/post-process.js +247 -24
- package/dist/utils/resolve-addon-package.d.ts +16 -0
- package/dist/utils/resolve-addon-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 +70 -23
- package/dist/utils/serialize-inspector-state.js +98 -22
- 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/validate-auth-sessionless.d.ts +3 -0
- package/dist/utils/validate-auth-sessionless.js +14 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
- 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 +2 -0
- package/dist/utils/workflow/graph/index.js +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
- package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
- package/dist/visit.d.ts +1 -1
- package/dist/visit.js +13 -6
- package/package.json +14 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +103 -79
- package/src/add/add-cli.ts +68 -24
- package/src/add/add-file-extends-core-type.ts +1 -1
- package/src/add/add-file-with-config.ts +1 -1
- package/src/add/add-file-with-factory.ts +3 -1
- package/src/add/add-functions.ts +349 -103
- package/src/add/add-http-route.ts +263 -89
- package/src/add/add-http-routes.ts +229 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +27 -16
- package/src/add/add-mcp-resource.ts +28 -16
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +25 -20
- package/src/add/add-rpc-invocations.ts +28 -11
- package/src/add/add-schedule.ts +17 -12
- 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-wire-addon.ts +80 -0
- package/src/add/add-workflow-graph.ts +180 -522
- package/src/add/add-workflow.ts +7 -6
- package/src/error-codes.ts +25 -1
- package/src/index.ts +23 -13
- package/src/inspector.ts +139 -19
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +199 -69
- package/src/utils/compute-required-schemas.ts +48 -0
- package/src/utils/contract-hashes.test.ts +553 -0
- package/src/utils/contract-hashes.ts +386 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/does-type-extend-core-type.ts +1 -1
- package/src/utils/ensure-function-metadata.ts +325 -8
- package/src/utils/extract-function-name.ts +101 -351
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +37 -25
- package/src/utils/filter-inspector-state.ts +146 -32
- package/src/utils/filter-utils.test.ts +1 -1
- package/src/utils/filter-utils.ts +2 -2
- package/src/utils/get-files-and-methods.ts +1 -1
- package/src/utils/get-property-value.ts +42 -4
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +131 -69
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +12 -12
- package/src/utils/post-process.ts +306 -44
- package/src/utils/resolve-addon-package.ts +49 -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 +184 -43
- 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/validate-auth-sessionless.ts +29 -0
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
- 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 +5 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
- package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
- package/src/visit.ts +19 -7
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-forge-credential.d.ts +0 -8
- package/dist/add/add-forge-credential.js +0 -77
- package/dist/add/add-forge-node.d.ts +0 -7
- package/dist/add/add-forge-node.js +0 -77
- 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-forge-credential.ts +0 -119
- package/src/add/add-forge-node.ts +0 -132
- 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
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { resolveAIMiddleware } from './middleware.js'
|
|
4
|
+
import { getInitialInspectorState } from '../inspector.js'
|
|
5
|
+
import * as ts from 'typescript'
|
|
6
|
+
|
|
7
|
+
function createSourceFile(code: string): ts.SourceFile {
|
|
8
|
+
return ts.createSourceFile('test.ts', code, ts.ScriptTarget.ESNext, true)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getObjectLiteral(
|
|
12
|
+
sourceFile: ts.SourceFile
|
|
13
|
+
): ts.ObjectLiteralExpression {
|
|
14
|
+
let result: ts.ObjectLiteralExpression | undefined
|
|
15
|
+
ts.forEachChild(sourceFile, function visit(node) {
|
|
16
|
+
if (ts.isObjectLiteralExpression(node) && !result) {
|
|
17
|
+
result = node
|
|
18
|
+
}
|
|
19
|
+
ts.forEachChild(node, visit)
|
|
20
|
+
})
|
|
21
|
+
if (!result) throw new Error('No object literal found')
|
|
22
|
+
return result
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createMockChecker(): ts.TypeChecker {
|
|
26
|
+
return {} as any
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('resolveAIMiddleware', () => {
|
|
30
|
+
test('should return undefined when no aiMiddleware property exists', () => {
|
|
31
|
+
const state = getInitialInspectorState('/test')
|
|
32
|
+
const src = createSourceFile('const x = { name: "test" }')
|
|
33
|
+
const obj = getObjectLiteral(src)
|
|
34
|
+
const checker = createMockChecker()
|
|
35
|
+
|
|
36
|
+
const result = resolveAIMiddleware(state, obj, checker)
|
|
37
|
+
|
|
38
|
+
assert.strictEqual(result, undefined)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('should return undefined when aiMiddleware array is empty', () => {
|
|
42
|
+
const state = getInitialInspectorState('/test')
|
|
43
|
+
const src = createSourceFile('const x = { aiMiddleware: [] }')
|
|
44
|
+
const obj = getObjectLiteral(src)
|
|
45
|
+
const checker = createMockChecker()
|
|
46
|
+
|
|
47
|
+
const result = resolveAIMiddleware(state, obj, checker)
|
|
48
|
+
|
|
49
|
+
assert.strictEqual(result, undefined)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('should resolve aiMiddleware identifiers as wire metadata', () => {
|
|
53
|
+
const state = getInitialInspectorState('/test')
|
|
54
|
+
state.aiMiddleware.definitions['myAIMW'] = {
|
|
55
|
+
services: { optimized: true, services: ['logger'] },
|
|
56
|
+
sourceFile: '/test/middleware.ts',
|
|
57
|
+
position: 0,
|
|
58
|
+
exportedName: 'myAIMW',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const code = 'const x = { aiMiddleware: [myAIMW] }'
|
|
62
|
+
const program = ts.createProgram({
|
|
63
|
+
rootNames: ['test.ts'],
|
|
64
|
+
options: { target: ts.ScriptTarget.ESNext },
|
|
65
|
+
host: {
|
|
66
|
+
...ts.createCompilerHost({}),
|
|
67
|
+
getSourceFile: (name) => {
|
|
68
|
+
if (name === 'test.ts')
|
|
69
|
+
return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
|
|
70
|
+
return undefined
|
|
71
|
+
},
|
|
72
|
+
fileExists: (name) => name === 'test.ts',
|
|
73
|
+
readFile: (name) => (name === 'test.ts' ? code : undefined),
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
const checker = program.getTypeChecker()
|
|
77
|
+
const sourceFile = program.getSourceFile('test.ts')!
|
|
78
|
+
const obj = getObjectLiteral(sourceFile)
|
|
79
|
+
|
|
80
|
+
const result = resolveAIMiddleware(state, obj, checker)
|
|
81
|
+
|
|
82
|
+
assert.ok(result)
|
|
83
|
+
assert.strictEqual(result.length, 1)
|
|
84
|
+
assert.deepStrictEqual(result[0], {
|
|
85
|
+
type: 'wire',
|
|
86
|
+
name: 'myAIMW',
|
|
87
|
+
inline: false,
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('should resolve multiple aiMiddleware entries', () => {
|
|
92
|
+
const state = getInitialInspectorState('/test')
|
|
93
|
+
state.aiMiddleware.definitions['firstMW'] = {
|
|
94
|
+
services: { optimized: true, services: ['logger'] },
|
|
95
|
+
sourceFile: '/test/middleware.ts',
|
|
96
|
+
position: 0,
|
|
97
|
+
exportedName: 'firstMW',
|
|
98
|
+
}
|
|
99
|
+
state.aiMiddleware.definitions['secondMW'] = {
|
|
100
|
+
services: { optimized: true, services: [] },
|
|
101
|
+
sourceFile: '/test/middleware.ts',
|
|
102
|
+
position: 100,
|
|
103
|
+
exportedName: 'secondMW',
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const code = 'const x = { aiMiddleware: [firstMW, secondMW] }'
|
|
107
|
+
const program = ts.createProgram({
|
|
108
|
+
rootNames: ['test.ts'],
|
|
109
|
+
options: { target: ts.ScriptTarget.ESNext },
|
|
110
|
+
host: {
|
|
111
|
+
...ts.createCompilerHost({}),
|
|
112
|
+
getSourceFile: (name) => {
|
|
113
|
+
if (name === 'test.ts')
|
|
114
|
+
return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
|
|
115
|
+
return undefined
|
|
116
|
+
},
|
|
117
|
+
fileExists: (name) => name === 'test.ts',
|
|
118
|
+
readFile: (name) => (name === 'test.ts' ? code : undefined),
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
const checker = program.getTypeChecker()
|
|
122
|
+
const sourceFile = program.getSourceFile('test.ts')!
|
|
123
|
+
const obj = getObjectLiteral(sourceFile)
|
|
124
|
+
|
|
125
|
+
const result = resolveAIMiddleware(state, obj, checker)
|
|
126
|
+
|
|
127
|
+
assert.ok(result)
|
|
128
|
+
assert.strictEqual(result.length, 2)
|
|
129
|
+
assert.strictEqual(result[0].type, 'wire')
|
|
130
|
+
assert.strictEqual((result[0] as any).name, 'firstMW')
|
|
131
|
+
assert.strictEqual(result[1].type, 'wire')
|
|
132
|
+
assert.strictEqual((result[1] as any).name, 'secondMW')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('should mark inline aiMiddleware (null exportedName)', () => {
|
|
136
|
+
const state = getInitialInspectorState('/test')
|
|
137
|
+
state.aiMiddleware.definitions['inlineMW'] = {
|
|
138
|
+
services: { optimized: true, services: [] },
|
|
139
|
+
sourceFile: '/test/middleware.ts',
|
|
140
|
+
position: 0,
|
|
141
|
+
exportedName: null,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const code = 'const x = { aiMiddleware: [inlineMW] }'
|
|
145
|
+
const program = ts.createProgram({
|
|
146
|
+
rootNames: ['test.ts'],
|
|
147
|
+
options: { target: ts.ScriptTarget.ESNext },
|
|
148
|
+
host: {
|
|
149
|
+
...ts.createCompilerHost({}),
|
|
150
|
+
getSourceFile: (name) => {
|
|
151
|
+
if (name === 'test.ts')
|
|
152
|
+
return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
|
|
153
|
+
return undefined
|
|
154
|
+
},
|
|
155
|
+
fileExists: (name) => name === 'test.ts',
|
|
156
|
+
readFile: (name) => (name === 'test.ts' ? code : undefined),
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
const checker = program.getTypeChecker()
|
|
160
|
+
const sourceFile = program.getSourceFile('test.ts')!
|
|
161
|
+
const obj = getObjectLiteral(sourceFile)
|
|
162
|
+
|
|
163
|
+
const result = resolveAIMiddleware(state, obj, checker)
|
|
164
|
+
|
|
165
|
+
assert.ok(result)
|
|
166
|
+
assert.strictEqual(result.length, 1)
|
|
167
|
+
assert.deepStrictEqual(result[0], {
|
|
168
|
+
type: 'wire',
|
|
169
|
+
name: 'inlineMW',
|
|
170
|
+
inline: true,
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('should handle aiMiddleware referencing unknown definitions', () => {
|
|
175
|
+
const state = getInitialInspectorState('/test')
|
|
176
|
+
|
|
177
|
+
const code = 'const x = { aiMiddleware: [unknownMW] }'
|
|
178
|
+
const program = ts.createProgram({
|
|
179
|
+
rootNames: ['test.ts'],
|
|
180
|
+
options: { target: ts.ScriptTarget.ESNext },
|
|
181
|
+
host: {
|
|
182
|
+
...ts.createCompilerHost({}),
|
|
183
|
+
getSourceFile: (name) => {
|
|
184
|
+
if (name === 'test.ts')
|
|
185
|
+
return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
|
|
186
|
+
return undefined
|
|
187
|
+
},
|
|
188
|
+
fileExists: (name) => name === 'test.ts',
|
|
189
|
+
readFile: (name) => (name === 'test.ts' ? code : undefined),
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
const checker = program.getTypeChecker()
|
|
193
|
+
const sourceFile = program.getSourceFile('test.ts')!
|
|
194
|
+
const obj = getObjectLiteral(sourceFile)
|
|
195
|
+
|
|
196
|
+
const result = resolveAIMiddleware(state, obj, checker)
|
|
197
|
+
|
|
198
|
+
assert.ok(result)
|
|
199
|
+
assert.strictEqual(result.length, 1)
|
|
200
|
+
assert.strictEqual(result[0].type, 'wire')
|
|
201
|
+
assert.strictEqual((result[0] as any).name, 'unknownMW')
|
|
202
|
+
assert.strictEqual((result[0] as any).inline, false)
|
|
203
|
+
})
|
|
204
|
+
})
|
package/src/utils/middleware.ts
CHANGED
|
@@ -1,46 +1,61 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import { MiddlewareMetadata } from '@pikku/core'
|
|
2
|
+
import type { MiddlewareMetadata } from '@pikku/core'
|
|
3
3
|
import { extractFunctionName } from './extract-function-name.js'
|
|
4
|
-
import { InspectorState } from '../types.js'
|
|
4
|
+
import type { InspectorState } from '../types.js'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export function
|
|
6
|
+
export interface MiddlewareRef {
|
|
7
|
+
definitionId: string
|
|
8
|
+
isFactoryCall: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function extractMiddlewareRefs(
|
|
12
12
|
arrayNode: ts.Expression,
|
|
13
13
|
checker: ts.TypeChecker,
|
|
14
14
|
rootDir: string
|
|
15
|
-
):
|
|
15
|
+
): MiddlewareRef[] {
|
|
16
16
|
if (!ts.isArrayLiteralExpression(arrayNode)) {
|
|
17
17
|
return []
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const refs: MiddlewareRef[] = []
|
|
21
21
|
for (const element of arrayNode.elements) {
|
|
22
22
|
if (ts.isIdentifier(element)) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const { pikkuFuncId } = extractFunctionName(element, checker, rootDir)
|
|
24
|
+
refs.push({
|
|
25
|
+
definitionId: pikkuFuncId.startsWith('__temp_')
|
|
26
|
+
? element.text
|
|
27
|
+
: pikkuFuncId,
|
|
28
|
+
isFactoryCall: false,
|
|
29
|
+
})
|
|
26
30
|
} else if (ts.isCallExpression(element)) {
|
|
27
|
-
|
|
28
|
-
// Extract the function being called (e.g., 'rateLimiter' from 'rateLimiter(10)')
|
|
29
|
-
const { pikkuFuncName } = extractFunctionName(
|
|
31
|
+
const { pikkuFuncId } = extractFunctionName(
|
|
30
32
|
element.expression,
|
|
31
33
|
checker,
|
|
32
34
|
rootDir
|
|
33
35
|
)
|
|
34
|
-
|
|
36
|
+
refs.push({
|
|
37
|
+
definitionId:
|
|
38
|
+
pikkuFuncId.startsWith('__temp_') &&
|
|
39
|
+
ts.isIdentifier(element.expression)
|
|
40
|
+
? element.expression.text
|
|
41
|
+
: pikkuFuncId,
|
|
42
|
+
isFactoryCall: true,
|
|
43
|
+
})
|
|
35
44
|
}
|
|
36
45
|
}
|
|
37
|
-
return
|
|
46
|
+
return refs
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function extractMiddlewarePikkuNames(
|
|
50
|
+
arrayNode: ts.Expression,
|
|
51
|
+
checker: ts.TypeChecker,
|
|
52
|
+
rootDir: string
|
|
53
|
+
): string[] {
|
|
54
|
+
return extractMiddlewareRefs(arrayNode, checker, rootDir).map(
|
|
55
|
+
(r) => r.definitionId
|
|
56
|
+
)
|
|
38
57
|
}
|
|
39
58
|
|
|
40
|
-
/**
|
|
41
|
-
* Get middleware array from an object literal expression property
|
|
42
|
-
* Returns the initializer node for the 'middleware' property if it exists
|
|
43
|
-
*/
|
|
44
59
|
export function getMiddlewareNode(
|
|
45
60
|
obj: ts.ObjectLiteralExpression
|
|
46
61
|
): ts.Expression | undefined {
|
|
@@ -54,31 +69,17 @@ export function getMiddlewareNode(
|
|
|
54
69
|
return middlewareProp?.initializer
|
|
55
70
|
}
|
|
56
71
|
|
|
57
|
-
/**
|
|
58
|
-
* Check if a route matches a pattern with wildcards
|
|
59
|
-
* Pattern can be exact match or use * as wildcard
|
|
60
|
-
* e.g., '/api/*' matches '/api/users', '/api/posts/123', etc.
|
|
61
|
-
*/
|
|
62
72
|
export function routeMatchesPattern(route: string, pattern: string): boolean {
|
|
63
73
|
if (route === pattern) return true
|
|
64
74
|
|
|
65
|
-
// Convert pattern to regex: replace * with .*
|
|
66
75
|
const regexPattern = pattern
|
|
67
|
-
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
68
|
-
.replace(/\*/g, '.*')
|
|
76
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
77
|
+
.replace(/\*/g, '.*')
|
|
69
78
|
|
|
70
79
|
const regex = new RegExp(`^${regexPattern}$`)
|
|
71
80
|
return regex.test(route)
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
/**
|
|
75
|
-
* Resolve middleware for an HTTP wiring based on:
|
|
76
|
-
* 1. Global HTTP middleware (addd([...]))
|
|
77
|
-
* 2. Route-specific HTTP middleware (addHTTPMiddleware('/pattern', [...]))
|
|
78
|
-
* 3. Tag-based middleware (addMiddleware('tag', [...]))
|
|
79
|
-
* 4. Explicit wiring middleware (wireHTTP({ middleware: [...] }))
|
|
80
|
-
* Returns undefined if no middleware is found, otherwise returns array with at least one item
|
|
81
|
-
*/
|
|
82
83
|
export function resolveHTTPMiddleware(
|
|
83
84
|
state: InspectorState,
|
|
84
85
|
route: string,
|
|
@@ -88,10 +89,8 @@ export function resolveHTTPMiddleware(
|
|
|
88
89
|
): MiddlewareMetadata[] | undefined {
|
|
89
90
|
const resolved: MiddlewareMetadata[] = []
|
|
90
91
|
|
|
91
|
-
// 1. HTTP route middleware groups (includes '*' for global)
|
|
92
92
|
for (const [pattern, _groupMeta] of state.http.routeMiddleware.entries()) {
|
|
93
93
|
if (routeMatchesPattern(route, pattern)) {
|
|
94
|
-
// Just reference the group by route pattern
|
|
95
94
|
resolved.push({
|
|
96
95
|
type: 'http',
|
|
97
96
|
route: pattern,
|
|
@@ -99,11 +98,9 @@ export function resolveHTTPMiddleware(
|
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
// 2. Tag-based middleware groups
|
|
103
101
|
if (tags && tags.length > 0) {
|
|
104
102
|
for (const tag of tags) {
|
|
105
103
|
if (state.middleware.tagMiddleware.has(tag)) {
|
|
106
|
-
// Just reference the group by tag
|
|
107
104
|
resolved.push({
|
|
108
105
|
type: 'tag',
|
|
109
106
|
tag,
|
|
@@ -112,7 +109,6 @@ export function resolveHTTPMiddleware(
|
|
|
112
109
|
}
|
|
113
110
|
}
|
|
114
111
|
|
|
115
|
-
// 3. Explicit wire middleware (inline is OK here)
|
|
116
112
|
if (explicitMiddlewareNode) {
|
|
117
113
|
const middlewareNames = extractMiddlewarePikkuNames(
|
|
118
114
|
explicitMiddlewareNode,
|
|
@@ -120,11 +116,11 @@ export function resolveHTTPMiddleware(
|
|
|
120
116
|
state.rootDir
|
|
121
117
|
)
|
|
122
118
|
for (const name of middlewareNames) {
|
|
123
|
-
const
|
|
119
|
+
const def = state.middleware.definitions[name]
|
|
124
120
|
resolved.push({
|
|
125
121
|
type: 'wire',
|
|
126
122
|
name,
|
|
127
|
-
inline:
|
|
123
|
+
inline: def?.exportedName === null,
|
|
128
124
|
})
|
|
129
125
|
}
|
|
130
126
|
}
|
|
@@ -132,11 +128,6 @@ export function resolveHTTPMiddleware(
|
|
|
132
128
|
return resolved.length > 0 ? resolved : undefined
|
|
133
129
|
}
|
|
134
130
|
|
|
135
|
-
/**
|
|
136
|
-
* Resolve tag-based and explicit middleware (common logic for wires and functions)
|
|
137
|
-
* 1. Tag-based middleware (addMiddleware('tag', [...]))
|
|
138
|
-
* 2. Explicit middleware (wireHTTP/pikkuFunc({ middleware: [...] }))
|
|
139
|
-
*/
|
|
140
131
|
function resolveTagAndExplicitMiddleware(
|
|
141
132
|
state: InspectorState,
|
|
142
133
|
tags: string[] | undefined,
|
|
@@ -145,11 +136,9 @@ function resolveTagAndExplicitMiddleware(
|
|
|
145
136
|
): MiddlewareMetadata[] {
|
|
146
137
|
const resolved: MiddlewareMetadata[] = []
|
|
147
138
|
|
|
148
|
-
// 1. Tag-based middleware groups
|
|
149
139
|
if (tags && tags.length > 0) {
|
|
150
140
|
for (const tag of tags) {
|
|
151
141
|
if (state.middleware.tagMiddleware.has(tag)) {
|
|
152
|
-
// Just reference the group by tag
|
|
153
142
|
resolved.push({
|
|
154
143
|
type: 'tag',
|
|
155
144
|
tag,
|
|
@@ -158,7 +147,6 @@ function resolveTagAndExplicitMiddleware(
|
|
|
158
147
|
}
|
|
159
148
|
}
|
|
160
149
|
|
|
161
|
-
// 2. Explicit middleware (inline is OK here - used directly in wire/function)
|
|
162
150
|
if (explicitMiddlewareNode) {
|
|
163
151
|
const middlewareNames = extractMiddlewarePikkuNames(
|
|
164
152
|
explicitMiddlewareNode,
|
|
@@ -166,11 +154,11 @@ function resolveTagAndExplicitMiddleware(
|
|
|
166
154
|
state.rootDir
|
|
167
155
|
)
|
|
168
156
|
for (const name of middlewareNames) {
|
|
169
|
-
const
|
|
157
|
+
const def = state.middleware.definitions[name]
|
|
170
158
|
resolved.push({
|
|
171
159
|
type: 'wire',
|
|
172
160
|
name,
|
|
173
|
-
inline:
|
|
161
|
+
inline: def?.exportedName === null,
|
|
174
162
|
})
|
|
175
163
|
}
|
|
176
164
|
}
|
|
@@ -178,12 +166,6 @@ function resolveTagAndExplicitMiddleware(
|
|
|
178
166
|
return resolved
|
|
179
167
|
}
|
|
180
168
|
|
|
181
|
-
/**
|
|
182
|
-
* Resolve middleware for a function based on:
|
|
183
|
-
* 1. Tag-based middleware (addMiddleware('tag', [...]))
|
|
184
|
-
* 2. Explicit function middleware (pikkuFunc({ middleware: [...] }))
|
|
185
|
-
* Returns undefined if no middleware is found, otherwise returns array with at least one item
|
|
186
|
-
*/
|
|
187
169
|
function resolveFunctionMiddlewareInternal(
|
|
188
170
|
state: InspectorState,
|
|
189
171
|
tags: string[] | undefined,
|
|
@@ -200,10 +182,6 @@ function resolveFunctionMiddlewareInternal(
|
|
|
200
182
|
return resolved.length > 0 ? resolved : undefined
|
|
201
183
|
}
|
|
202
184
|
|
|
203
|
-
/**
|
|
204
|
-
* Convenience wrapper: Extract middleware node from object and resolve
|
|
205
|
-
* Use this in add-* files for cleaner code
|
|
206
|
-
*/
|
|
207
185
|
export function resolveMiddleware(
|
|
208
186
|
state: InspectorState,
|
|
209
187
|
obj: ts.ObjectLiteralExpression,
|
|
@@ -219,10 +197,6 @@ export function resolveMiddleware(
|
|
|
219
197
|
)
|
|
220
198
|
}
|
|
221
199
|
|
|
222
|
-
/**
|
|
223
|
-
* Convenience wrapper for HTTP: Extract middleware and resolve with HTTP-specific logic
|
|
224
|
-
* Use this in add-http-route.ts for cleaner code
|
|
225
|
-
*/
|
|
226
200
|
export function resolveHTTPMiddlewareFromObject(
|
|
227
201
|
state: InspectorState,
|
|
228
202
|
route: string,
|
|
@@ -239,3 +213,91 @@ export function resolveHTTPMiddlewareFromObject(
|
|
|
239
213
|
checker
|
|
240
214
|
)
|
|
241
215
|
}
|
|
216
|
+
|
|
217
|
+
function getAIMiddlewareNode(
|
|
218
|
+
obj: ts.ObjectLiteralExpression
|
|
219
|
+
): ts.Expression | undefined {
|
|
220
|
+
const prop = obj.properties.find(
|
|
221
|
+
(p) =>
|
|
222
|
+
ts.isPropertyAssignment(p) &&
|
|
223
|
+
ts.isIdentifier(p.name) &&
|
|
224
|
+
p.name.text === 'aiMiddleware'
|
|
225
|
+
) as ts.PropertyAssignment | undefined
|
|
226
|
+
return prop?.initializer
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function resolveAIMiddleware(
|
|
230
|
+
state: InspectorState,
|
|
231
|
+
obj: ts.ObjectLiteralExpression,
|
|
232
|
+
checker: ts.TypeChecker
|
|
233
|
+
): MiddlewareMetadata[] | undefined {
|
|
234
|
+
const explicitNode = getAIMiddlewareNode(obj)
|
|
235
|
+
if (!explicitNode) return undefined
|
|
236
|
+
|
|
237
|
+
const names = extractMiddlewarePikkuNames(
|
|
238
|
+
explicitNode,
|
|
239
|
+
checker,
|
|
240
|
+
state.rootDir
|
|
241
|
+
)
|
|
242
|
+
const resolved: MiddlewareMetadata[] = names.map((name) => {
|
|
243
|
+
const def = state.aiMiddleware.definitions[name]
|
|
244
|
+
return {
|
|
245
|
+
type: 'wire' as const,
|
|
246
|
+
name,
|
|
247
|
+
inline: def?.exportedName === null,
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
return resolved.length > 0 ? resolved : undefined
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function getChannelMiddlewareNode(
|
|
255
|
+
obj: ts.ObjectLiteralExpression
|
|
256
|
+
): ts.Expression | undefined {
|
|
257
|
+
const prop = obj.properties.find(
|
|
258
|
+
(p) =>
|
|
259
|
+
ts.isPropertyAssignment(p) &&
|
|
260
|
+
ts.isIdentifier(p.name) &&
|
|
261
|
+
p.name.text === 'channelMiddleware'
|
|
262
|
+
) as ts.PropertyAssignment | undefined
|
|
263
|
+
return prop?.initializer
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function resolveChannelMiddleware(
|
|
267
|
+
state: InspectorState,
|
|
268
|
+
obj: ts.ObjectLiteralExpression,
|
|
269
|
+
tags: string[] | undefined,
|
|
270
|
+
checker: ts.TypeChecker
|
|
271
|
+
): MiddlewareMetadata[] | undefined {
|
|
272
|
+
const resolved: MiddlewareMetadata[] = []
|
|
273
|
+
|
|
274
|
+
if (tags && tags.length > 0) {
|
|
275
|
+
for (const tag of tags) {
|
|
276
|
+
if (state.channelMiddleware.tagMiddleware.has(tag)) {
|
|
277
|
+
resolved.push({
|
|
278
|
+
type: 'tag',
|
|
279
|
+
tag,
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const explicitNode = getChannelMiddlewareNode(obj)
|
|
286
|
+
if (explicitNode) {
|
|
287
|
+
const names = extractMiddlewarePikkuNames(
|
|
288
|
+
explicitNode,
|
|
289
|
+
checker,
|
|
290
|
+
state.rootDir
|
|
291
|
+
)
|
|
292
|
+
for (const name of names) {
|
|
293
|
+
const def = state.channelMiddleware.definitions[name]
|
|
294
|
+
resolved.push({
|
|
295
|
+
type: 'wire',
|
|
296
|
+
name,
|
|
297
|
+
inline: def?.exportedName === null,
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return resolved.length > 0 ? resolved : undefined
|
|
303
|
+
}
|
|
@@ -54,7 +54,8 @@ describe('resolveHTTPPermissions', () => {
|
|
|
54
54
|
sourceFile: '/test.ts',
|
|
55
55
|
position: 0,
|
|
56
56
|
services: { services: [], singletons: [] },
|
|
57
|
-
|
|
57
|
+
count: 1,
|
|
58
|
+
instanceIds: [],
|
|
58
59
|
isFactory: true,
|
|
59
60
|
}
|
|
60
61
|
state.http.routePermissions.set('*', globalPermission)
|
|
@@ -79,7 +80,8 @@ describe('resolveHTTPPermissions', () => {
|
|
|
79
80
|
sourceFile: '/test.ts',
|
|
80
81
|
position: 0,
|
|
81
82
|
services: { services: [], singletons: [] },
|
|
82
|
-
|
|
83
|
+
count: 1,
|
|
84
|
+
instanceIds: [],
|
|
83
85
|
isFactory: true,
|
|
84
86
|
}
|
|
85
87
|
state.http.routePermissions.set('/api/*', apiPermission)
|
|
@@ -104,7 +106,8 @@ describe('resolveHTTPPermissions', () => {
|
|
|
104
106
|
sourceFile: '/test.ts',
|
|
105
107
|
position: 0,
|
|
106
108
|
services: { services: [], singletons: [] },
|
|
107
|
-
|
|
109
|
+
count: 1,
|
|
110
|
+
instanceIds: [],
|
|
108
111
|
isFactory: true,
|
|
109
112
|
}
|
|
110
113
|
state.permissions.tagPermissions.set('admin', adminPermission)
|
|
@@ -131,7 +134,8 @@ describe('resolveHTTPPermissions', () => {
|
|
|
131
134
|
sourceFile: '/test.ts',
|
|
132
135
|
position: 0,
|
|
133
136
|
services: { services: [], singletons: [] },
|
|
134
|
-
|
|
137
|
+
count: 1,
|
|
138
|
+
instanceIds: [],
|
|
135
139
|
isFactory: true,
|
|
136
140
|
})
|
|
137
141
|
|
|
@@ -141,7 +145,8 @@ describe('resolveHTTPPermissions', () => {
|
|
|
141
145
|
sourceFile: '/test.ts',
|
|
142
146
|
position: 0,
|
|
143
147
|
services: { services: [], singletons: [] },
|
|
144
|
-
|
|
148
|
+
count: 1,
|
|
149
|
+
instanceIds: [],
|
|
145
150
|
isFactory: true,
|
|
146
151
|
})
|
|
147
152
|
|
|
@@ -151,12 +156,13 @@ describe('resolveHTTPPermissions', () => {
|
|
|
151
156
|
sourceFile: '/test.ts',
|
|
152
157
|
position: 0,
|
|
153
158
|
services: { services: [], singletons: [] },
|
|
154
|
-
|
|
159
|
+
count: 1,
|
|
160
|
+
instanceIds: [],
|
|
155
161
|
isFactory: true,
|
|
156
162
|
})
|
|
157
163
|
|
|
158
164
|
// Setup explicit permission
|
|
159
|
-
state.permissions.
|
|
165
|
+
state.permissions.definitions['testPermission'] = {
|
|
160
166
|
services: { services: [], singletons: [] },
|
|
161
167
|
sourceFile: '/test.ts',
|
|
162
168
|
position: 0,
|
|
@@ -197,7 +203,8 @@ describe('resolvePermissions', () => {
|
|
|
197
203
|
sourceFile: '/test.ts',
|
|
198
204
|
position: 0,
|
|
199
205
|
services: { services: [], singletons: [] },
|
|
200
|
-
|
|
206
|
+
count: 1,
|
|
207
|
+
instanceIds: [],
|
|
201
208
|
isFactory: true,
|
|
202
209
|
}
|
|
203
210
|
state.permissions.tagPermissions.set('mcp', mcpPermission)
|
|
@@ -217,7 +224,8 @@ describe('resolvePermissions', () => {
|
|
|
217
224
|
sourceFile: '/test.ts',
|
|
218
225
|
position: 0,
|
|
219
226
|
services: { services: [], singletons: [] },
|
|
220
|
-
|
|
227
|
+
count: 1,
|
|
228
|
+
instanceIds: [],
|
|
221
229
|
isFactory: true,
|
|
222
230
|
})
|
|
223
231
|
state.permissions.tagPermissions.set('admin', {
|
|
@@ -225,7 +233,8 @@ describe('resolvePermissions', () => {
|
|
|
225
233
|
sourceFile: '/test.ts',
|
|
226
234
|
position: 0,
|
|
227
235
|
services: { services: [], singletons: [] },
|
|
228
|
-
|
|
236
|
+
count: 1,
|
|
237
|
+
instanceIds: [],
|
|
229
238
|
isFactory: true,
|
|
230
239
|
})
|
|
231
240
|
const mockObj = createMockObjectLiteral()
|
|
@@ -298,6 +307,13 @@ function createMockState(): InspectorState {
|
|
|
298
307
|
meta: {},
|
|
299
308
|
files: new Set(),
|
|
300
309
|
},
|
|
310
|
+
workflows: {
|
|
311
|
+
meta: {},
|
|
312
|
+
files: new Map(),
|
|
313
|
+
graphMeta: {},
|
|
314
|
+
graphFiles: new Map(),
|
|
315
|
+
invokedWorkflows: new Set(),
|
|
316
|
+
},
|
|
301
317
|
rpc: {
|
|
302
318
|
internalMeta: {},
|
|
303
319
|
internalFiles: new Map(),
|
|
@@ -316,11 +332,18 @@ function createMockState(): InspectorState {
|
|
|
316
332
|
files: new Set(),
|
|
317
333
|
},
|
|
318
334
|
middleware: {
|
|
319
|
-
|
|
335
|
+
definitions: {},
|
|
336
|
+
instances: {},
|
|
337
|
+
tagMiddleware: new Map(),
|
|
338
|
+
},
|
|
339
|
+
channelMiddleware: {
|
|
340
|
+
definitions: {},
|
|
341
|
+
instances: {},
|
|
320
342
|
tagMiddleware: new Map(),
|
|
321
343
|
},
|
|
322
344
|
permissions: {
|
|
323
|
-
|
|
345
|
+
definitions: {},
|
|
346
|
+
instances: {},
|
|
324
347
|
tagPermissions: new Map(),
|
|
325
348
|
},
|
|
326
349
|
}
|