@pikku/inspector 0.12.74 → 0.12.81
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 +347 -0
- package/dist/add/add-analytics.d.ts +9 -0
- package/dist/add/add-analytics.js +178 -0
- package/dist/add/add-auth.js +0 -54
- package/dist/add/add-feature-flag.d.ts +2 -0
- package/dist/add/add-feature-flag.js +162 -0
- package/dist/add/add-wire-addon.js +1 -0
- package/dist/add/add-wire-remote-addon.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/inspector.js +4 -0
- package/dist/surface/collect-surface.d.ts +58 -0
- package/dist/surface/collect-surface.js +613 -0
- package/dist/types.d.ts +20 -11
- package/dist/utils/filter-inspector-state.js +23 -13
- package/dist/utils/post-process.js +15 -0
- package/dist/utils/read-module-specifiers.d.ts +8 -0
- package/dist/utils/read-module-specifiers.js +23 -0
- package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
- package/dist/utils/read-tsconfig-out-dir.js +12 -0
- package/dist/utils/resolve-deploy-target.d.ts +7 -0
- package/dist/utils/resolve-deploy-target.js +15 -7
- package/dist/utils/serialize-inspector-state.d.ts +5 -0
- package/dist/utils/serialize-inspector-state.js +8 -0
- package/dist/visit.js +4 -0
- package/package.json +16 -6
- package/run-tests.sh +21 -6
- package/src/add/add-addon-bans.ts +0 -85
- package/src/add/add-agent-scorer.test.ts +0 -197
- package/src/add/add-agent-scorer.ts +0 -83
- package/src/add/add-agent.test.ts +0 -87
- package/src/add/add-agent.ts +0 -638
- package/src/add/add-approval-description.ts +0 -76
- package/src/add/add-auth.test.ts +0 -791
- package/src/add/add-auth.ts +0 -424
- package/src/add/add-channel.ts +0 -778
- package/src/add/add-cli-renderers.test.ts +0 -74
- package/src/add/add-cli.ts +0 -1006
- package/src/add/add-credential.ts +0 -186
- package/src/add/add-feature.test.ts +0 -306
- package/src/add/add-feature.ts +0 -253
- package/src/add/add-file-extends-core-type.ts +0 -66
- package/src/add/add-file-with-config.ts +0 -45
- package/src/add/add-file-with-factory.test.ts +0 -134
- package/src/add/add-file-with-factory.ts +0 -288
- package/src/add/add-functions.test.ts +0 -743
- package/src/add/add-functions.ts +0 -1548
- package/src/add/add-gateway.ts +0 -143
- package/src/add/add-http-route-ref.test.ts +0 -149
- package/src/add/add-http-route.ts +0 -530
- package/src/add/add-http-routes.ts +0 -471
- package/src/add/add-keyed-wiring.ts +0 -180
- package/src/add/add-mcp-prompt.ts +0 -157
- package/src/add/add-mcp-resource.ts +0 -175
- package/src/add/add-middleware.test.ts +0 -196
- package/src/add/add-middleware.ts +0 -879
- package/src/add/add-permission-auth.test.ts +0 -59
- package/src/add/add-permission.ts +0 -311
- package/src/add/add-personas.test.ts +0 -406
- package/src/add/add-personas.ts +0 -287
- package/src/add/add-queue-worker.ts +0 -117
- package/src/add/add-rpc-invocations.test.ts +0 -287
- package/src/add/add-rpc-invocations.ts +0 -221
- package/src/add/add-scenario-step.test.ts +0 -509
- package/src/add/add-schedule.ts +0 -120
- package/src/add/add-scope.test.ts +0 -419
- package/src/add/add-scope.ts +0 -221
- package/src/add/add-secret.ts +0 -10
- package/src/add/add-subject-scenario-steps.test.ts +0 -161
- package/src/add/add-system-role.test.ts +0 -297
- package/src/add/add-system-role.ts +0 -162
- package/src/add/add-trigger.ts +0 -165
- package/src/add/add-variable.ts +0 -10
- package/src/add/add-wire-addon.test.ts +0 -185
- package/src/add/add-wire-addon.ts +0 -146
- package/src/add/add-wire-remote-addon.ts +0 -80
- package/src/add/add-workflow-edge-cases.test.ts +0 -201
- package/src/add/add-workflow-fanout.test.ts +0 -166
- package/src/add/add-workflow-graph-foreach.test.ts +0 -174
- package/src/add/add-workflow-graph-input.test.ts +0 -94
- package/src/add/add-workflow-graph-notes.test.ts +0 -74
- package/src/add/add-workflow-graph.ts +0 -644
- package/src/add/add-workflow-on-error.test.ts +0 -118
- package/src/add/add-workflow-return-switch.test.ts +0 -121
- package/src/add/add-workflow-silent-drops.test.ts +0 -261
- package/src/add/add-workflow-sleep-expression.test.ts +0 -96
- package/src/add/add-workflow.test.ts +0 -155
- package/src/add/add-workflow.ts +0 -482
- package/src/add/addon-bans.test.ts +0 -121
- package/src/add/addon-contracts.test.ts +0 -221
- package/src/add/allow-escape-hatches.test.ts +0 -144
- package/src/add/audit-meta.test.ts +0 -75
- package/src/add/classified-sink-check.test.ts +0 -347
- package/src/add/config-value-metadata.test.ts +0 -263
- package/src/add/dynamic-import-check.test.ts +0 -119
- package/src/add/expect-eventually-scenario-only.test.ts +0 -108
- package/src/add/function-body-span.test.ts +0 -96
- package/src/add/inline-wiring-function.test.ts +0 -104
- package/src/add/pii-check.test.ts +0 -258
- package/src/add/rpc-type-cast.test.ts +0 -123
- package/src/add/scenario-instrumentation.test.ts +0 -89
- package/src/add/scenario-instrumentation.ts +0 -42
- package/src/add/scenario-marker.test.ts +0 -89
- package/src/add/scenario-not-extractable.test.ts +0 -159
- package/src/add/scenario-services.test.ts +0 -103
- package/src/add/scenario-skip.test.ts +0 -83
- package/src/add/validate-workflow-graph-addons.test.ts +0 -102
- package/src/add/wire-name-literal.test.ts +0 -117
- package/src/error-codes-emitted.test.ts +0 -76
- package/src/error-codes.ts +0 -145
- package/src/index.ts +0 -66
- package/src/inspector.ts +0 -575
- package/src/schema-generator.ts +0 -1
- package/src/source-file-cache.ts +0 -73
- package/src/tsconfig-paths.test.ts +0 -90
- package/src/types-map.ts +0 -141
- package/src/types.ts +0 -775
- package/src/utils/annotate-http-route-auth.test.ts +0 -161
- package/src/utils/annotate-http-route-auth.ts +0 -53
- package/src/utils/check-classified-sinks.ts +0 -215
- package/src/utils/check-pii-output.ts +0 -120
- package/src/utils/compute-diagnostics.test.ts +0 -106
- package/src/utils/compute-required-schemas.ts +0 -48
- package/src/utils/contract-hashes.test.ts +0 -553
- package/src/utils/contract-hashes.ts +0 -387
- package/src/utils/custom-types-generator.test.ts +0 -124
- package/src/utils/custom-types-generator.ts +0 -150
- package/src/utils/detect-schema-vendor.test.ts +0 -123
- package/src/utils/detect-schema-vendor.ts +0 -120
- package/src/utils/does-type-extend-core-type.ts +0 -53
- package/src/utils/ensure-function-metadata.ts +0 -385
- package/src/utils/extract-function-name.ts +0 -612
- package/src/utils/extract-function-node.ts +0 -70
- package/src/utils/extract-node-value.test.ts +0 -117
- package/src/utils/extract-node-value.ts +0 -172
- package/src/utils/extract-secret-usage.test.ts +0 -81
- package/src/utils/extract-secret-usage.ts +0 -73
- package/src/utils/extract-services.ts +0 -70
- package/src/utils/extract-surface-usage.test.ts +0 -147
- package/src/utils/extract-surface-usage.ts +0 -121
- package/src/utils/filter-inspector-state.test.ts +0 -2207
- package/src/utils/filter-inspector-state.ts +0 -1302
- package/src/utils/filter-utils.test.ts +0 -843
- package/src/utils/filter-utils.ts +0 -161
- package/src/utils/find-root-dir.ts +0 -68
- package/src/utils/find-zod-transform.test.ts +0 -70
- package/src/utils/find-zod-transform.ts +0 -103
- package/src/utils/get-exported-variable-name.ts +0 -48
- package/src/utils/get-files-and-methods.ts +0 -157
- package/src/utils/get-property-value.test.ts +0 -141
- package/src/utils/get-property-value.ts +0 -331
- package/src/utils/hash.ts +0 -26
- package/src/utils/literal-properties.ts +0 -97
- package/src/utils/load-addon-functions-meta.test.ts +0 -157
- package/src/utils/load-addon-functions-meta.ts +0 -508
- package/src/utils/load-addon-scopes.test.ts +0 -138
- package/src/utils/middleware.test.ts +0 -204
- package/src/utils/middleware.ts +0 -303
- package/src/utils/nested-project-filter.test.ts +0 -53
- package/src/utils/nested-project-filter.ts +0 -28
- package/src/utils/permissions-shorthand.test.ts +0 -79
- package/src/utils/permissions.test.ts +0 -125
- package/src/utils/permissions.ts +0 -129
- package/src/utils/post-process.test.ts +0 -1094
- package/src/utils/post-process.ts +0 -1604
- package/src/utils/resolve-addon-package.ts +0 -106
- package/src/utils/resolve-core-type.test.ts +0 -142
- package/src/utils/resolve-core-type.ts +0 -35
- package/src/utils/resolve-deploy-target.test.ts +0 -135
- package/src/utils/resolve-deploy-target.ts +0 -65
- package/src/utils/resolve-function-meta.ts +0 -25
- package/src/utils/resolve-function-types.ts +0 -42
- package/src/utils/resolve-identifier.ts +0 -46
- package/src/utils/resolve-ref-contract.ts +0 -71
- package/src/utils/resolve-versions.test.ts +0 -499
- package/src/utils/resolve-versions.ts +0 -228
- package/src/utils/schema-cache.test.ts +0 -124
- package/src/utils/schema-generator.test.ts +0 -272
- package/src/utils/schema-generator.ts +0 -780
- package/src/utils/secret-alias-services.test.ts +0 -146
- package/src/utils/secret-alias-services.ts +0 -58
- package/src/utils/serialize-inspector-state.ts +0 -742
- package/src/utils/serialize-mcp-json.ts +0 -150
- package/src/utils/serialize-middleware-groups-meta.ts +0 -36
- package/src/utils/serialize-openapi-json.ts +0 -277
- package/src/utils/serialize-permissions-groups-meta.ts +0 -7
- package/src/utils/single-declaration.ts +0 -57
- package/src/utils/type-utils.ts +0 -145
- package/src/utils/validate-auth-sessionless.ts +0 -30
- package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
- package/src/utils/validate-exposed-functions-gated.ts +0 -103
- package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
- package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
- package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
- package/src/utils/workflow/derive-workflow-plan.ts +0 -117
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
- package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
- package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
- package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
- package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
- package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
- package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
- package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
- package/src/utils/workflow/dsl/index.ts +0 -11
- package/src/utils/workflow/dsl/patterns.ts +0 -440
- package/src/utils/workflow/dsl/validation.ts +0 -203
- package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
- package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
- package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
- package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
- package/src/utils/workflow/graph/index.ts +0 -28
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
- package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
- package/src/visit.ts +0 -181
- package/tsconfig.json +0 -19
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,2207 +0,0 @@
|
|
|
1
|
-
import { test, describe } from 'node:test'
|
|
2
|
-
import { strict as assert } from 'node:assert'
|
|
3
|
-
import { filterInspectorState } from './filter-inspector-state.js'
|
|
4
|
-
import type { InspectorState, InspectorFilters } from '../types.js'
|
|
5
|
-
import type { SerializableInspectorState } from './serialize-inspector-state.js'
|
|
6
|
-
import {
|
|
7
|
-
deserializeInspectorState,
|
|
8
|
-
serializeInspectorState,
|
|
9
|
-
} from './serialize-inspector-state.js'
|
|
10
|
-
import { getInitialInspectorState } from '../inspector.js'
|
|
11
|
-
import { readFileSync } from 'node:fs'
|
|
12
|
-
import { fileURLToPath } from 'node:url'
|
|
13
|
-
import { dirname, join } from 'node:path'
|
|
14
|
-
|
|
15
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
16
|
-
const __dirname = dirname(__filename)
|
|
17
|
-
|
|
18
|
-
// Load the real inspector state from templates/functions
|
|
19
|
-
let realState: Omit<InspectorState, 'typesLookup'>
|
|
20
|
-
try {
|
|
21
|
-
const stateJson = readFileSync(
|
|
22
|
-
join(__dirname, 'test-data/inspector-state.json'),
|
|
23
|
-
'utf-8'
|
|
24
|
-
)
|
|
25
|
-
const serialized = JSON.parse(stateJson) as SerializableInspectorState
|
|
26
|
-
realState = deserializeInspectorState(serialized)
|
|
27
|
-
} catch (e) {
|
|
28
|
-
console.error('Failed to load real inspector state:', e)
|
|
29
|
-
throw e
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Helper to create a minimal mock InspectorState for testing (kept for compatibility)
|
|
33
|
-
function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
|
|
34
|
-
return {
|
|
35
|
-
rootDir: '/test/project',
|
|
36
|
-
singletonServicesTypeImportMap: new Map(),
|
|
37
|
-
wireServicesTypeImportMap: new Map(),
|
|
38
|
-
userSessionTypeImportMap: new Map(),
|
|
39
|
-
configTypeImportMap: new Map(),
|
|
40
|
-
singletonServicesFactories: new Map(),
|
|
41
|
-
wireServicesFactories: new Map(),
|
|
42
|
-
wireServicesMeta: new Map(),
|
|
43
|
-
configFactories: new Map(),
|
|
44
|
-
filesAndMethods: {},
|
|
45
|
-
filesAndMethodsErrors: new Map(),
|
|
46
|
-
http: {
|
|
47
|
-
metaInputTypes: new Map(),
|
|
48
|
-
meta: {
|
|
49
|
-
get: {
|
|
50
|
-
'/api/users': {
|
|
51
|
-
pikkuFuncId: 'getUsers',
|
|
52
|
-
route: '/api/users',
|
|
53
|
-
method: 'GET',
|
|
54
|
-
tags: ['api', 'public'],
|
|
55
|
-
middleware: [],
|
|
56
|
-
permissions: [],
|
|
57
|
-
},
|
|
58
|
-
'/admin/settings': {
|
|
59
|
-
pikkuFuncId: 'getAdminSettings',
|
|
60
|
-
route: '/admin/settings',
|
|
61
|
-
method: 'GET',
|
|
62
|
-
tags: ['admin'],
|
|
63
|
-
middleware: [{ type: 'wire', name: 'authMiddleware' }],
|
|
64
|
-
permissions: [{ type: 'wire', name: 'adminPermission' }],
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
post: {
|
|
68
|
-
'/api/users': {
|
|
69
|
-
pikkuFuncId: 'createUser',
|
|
70
|
-
route: '/api/users',
|
|
71
|
-
method: 'POST',
|
|
72
|
-
tags: ['api'],
|
|
73
|
-
middleware: [],
|
|
74
|
-
permissions: [],
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
put: {},
|
|
78
|
-
patch: {},
|
|
79
|
-
delete: {},
|
|
80
|
-
head: {},
|
|
81
|
-
options: {},
|
|
82
|
-
},
|
|
83
|
-
files: new Set(['/test/project/src/api/users.ts']),
|
|
84
|
-
routeMiddleware: new Map(),
|
|
85
|
-
},
|
|
86
|
-
functions: {
|
|
87
|
-
typesMap: new Map(),
|
|
88
|
-
meta: {
|
|
89
|
-
getUsers: {
|
|
90
|
-
services: [],
|
|
91
|
-
optimized: false,
|
|
92
|
-
},
|
|
93
|
-
createUser: {
|
|
94
|
-
services: [],
|
|
95
|
-
optimized: false,
|
|
96
|
-
},
|
|
97
|
-
getAdminSettings: {
|
|
98
|
-
services: [],
|
|
99
|
-
optimized: false,
|
|
100
|
-
},
|
|
101
|
-
sendEmailWorker: {
|
|
102
|
-
services: [],
|
|
103
|
-
optimized: false,
|
|
104
|
-
},
|
|
105
|
-
dailyReport: {
|
|
106
|
-
services: [],
|
|
107
|
-
optimized: false,
|
|
108
|
-
},
|
|
109
|
-
mcpSearchTool: {
|
|
110
|
-
services: [],
|
|
111
|
-
optimized: false,
|
|
112
|
-
},
|
|
113
|
-
cliCommand: {
|
|
114
|
-
services: [],
|
|
115
|
-
optimized: false,
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
files: new Map([
|
|
119
|
-
[
|
|
120
|
-
'getUsers',
|
|
121
|
-
{ path: '/test/project/src/api/users.ts', exportedName: 'getUsers' },
|
|
122
|
-
],
|
|
123
|
-
[
|
|
124
|
-
'createUser',
|
|
125
|
-
{
|
|
126
|
-
path: '/test/project/src/api/users.ts',
|
|
127
|
-
exportedName: 'createUser',
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
[
|
|
131
|
-
'getAdminSettings',
|
|
132
|
-
{
|
|
133
|
-
path: '/test/project/src/admin/settings.ts',
|
|
134
|
-
exportedName: 'getAdminSettings',
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
[
|
|
138
|
-
'sendEmailWorker',
|
|
139
|
-
{
|
|
140
|
-
path: '/test/project/src/workers/email.ts',
|
|
141
|
-
exportedName: 'sendEmailWorker',
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
[
|
|
145
|
-
'dailyReport',
|
|
146
|
-
{
|
|
147
|
-
path: '/test/project/src/tasks/reports.ts',
|
|
148
|
-
exportedName: 'dailyReport',
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
]),
|
|
152
|
-
},
|
|
153
|
-
triggers: {
|
|
154
|
-
meta: {},
|
|
155
|
-
files: new Set(),
|
|
156
|
-
},
|
|
157
|
-
channels: {
|
|
158
|
-
meta: {
|
|
159
|
-
'chat-channel': {
|
|
160
|
-
pikkuFuncId: 'handleChatMessage',
|
|
161
|
-
tags: ['realtime', 'public'],
|
|
162
|
-
middleware: [],
|
|
163
|
-
permissions: [],
|
|
164
|
-
sourceFile: '/test/project/src/channels/chat.ts',
|
|
165
|
-
} as any,
|
|
166
|
-
'admin-channel': {
|
|
167
|
-
pikkuFuncId: 'handleAdminMessage',
|
|
168
|
-
tags: ['realtime', 'admin'],
|
|
169
|
-
middleware: [{ type: 'wire', name: 'authMiddleware' }],
|
|
170
|
-
permissions: [],
|
|
171
|
-
sourceFile: '/test/project/src/channels/admin.ts',
|
|
172
|
-
} as any,
|
|
173
|
-
},
|
|
174
|
-
files: new Set([
|
|
175
|
-
'/test/project/src/channels/chat.ts',
|
|
176
|
-
'/test/project/src/channels/admin.ts',
|
|
177
|
-
]),
|
|
178
|
-
},
|
|
179
|
-
scheduledTasks: {
|
|
180
|
-
meta: {
|
|
181
|
-
'daily-report': {
|
|
182
|
-
pikkuFuncId: 'dailyReport',
|
|
183
|
-
schedule: '0 0 * * *',
|
|
184
|
-
tags: ['cron', 'reports'],
|
|
185
|
-
middleware: [],
|
|
186
|
-
sourceFile: '/test/project/src/tasks/reports.ts',
|
|
187
|
-
} as any,
|
|
188
|
-
'hourly-cleanup': {
|
|
189
|
-
pikkuFuncId: 'hourlyCleanup',
|
|
190
|
-
schedule: '0 * * * *',
|
|
191
|
-
tags: ['cron', 'maintenance'],
|
|
192
|
-
middleware: [],
|
|
193
|
-
sourceFile: '/test/project/src/tasks/cleanup.ts',
|
|
194
|
-
} as any,
|
|
195
|
-
},
|
|
196
|
-
files: new Set([
|
|
197
|
-
'/test/project/src/tasks/reports.ts',
|
|
198
|
-
'/test/project/src/tasks/cleanup.ts',
|
|
199
|
-
]),
|
|
200
|
-
},
|
|
201
|
-
queueWorkers: {
|
|
202
|
-
meta: {
|
|
203
|
-
'email-worker': {
|
|
204
|
-
pikkuFuncId: 'sendEmailWorker',
|
|
205
|
-
name: 'email-queue',
|
|
206
|
-
tags: ['queue', 'email'],
|
|
207
|
-
middleware: [],
|
|
208
|
-
sourceFile: '/test/project/src/workers/email.ts',
|
|
209
|
-
} as any,
|
|
210
|
-
'notification-worker': {
|
|
211
|
-
pikkuFuncId: 'sendNotificationWorker',
|
|
212
|
-
name: 'notification-queue',
|
|
213
|
-
tags: ['queue', 'notifications'],
|
|
214
|
-
middleware: [],
|
|
215
|
-
sourceFile: '/test/project/src/workers/notification.ts',
|
|
216
|
-
} as any,
|
|
217
|
-
},
|
|
218
|
-
files: new Set([
|
|
219
|
-
'/test/project/src/workers/email.ts',
|
|
220
|
-
'/test/project/src/workers/notification.ts',
|
|
221
|
-
]),
|
|
222
|
-
},
|
|
223
|
-
workflows: {
|
|
224
|
-
meta: {},
|
|
225
|
-
files: new Map(),
|
|
226
|
-
graphMeta: {},
|
|
227
|
-
graphFiles: new Map(),
|
|
228
|
-
invokedWorkflows: new Set(),
|
|
229
|
-
},
|
|
230
|
-
rpc: {
|
|
231
|
-
internalMeta: {},
|
|
232
|
-
internalFiles: new Map(),
|
|
233
|
-
exposedMeta: {},
|
|
234
|
-
exposedFiles: new Map(),
|
|
235
|
-
invokedFunctions: new Set(),
|
|
236
|
-
invokedFunctionsByFile: new Map(),
|
|
237
|
-
},
|
|
238
|
-
mcpEndpoints: {
|
|
239
|
-
toolsMeta: {
|
|
240
|
-
'search-tool': {
|
|
241
|
-
name: 'search-tool',
|
|
242
|
-
description: 'Search tool',
|
|
243
|
-
pikkuFuncId: 'mcpSearchTool',
|
|
244
|
-
tags: ['mcp', 'search'],
|
|
245
|
-
middleware: [],
|
|
246
|
-
permissions: [],
|
|
247
|
-
sourceFile: '/test/project/src/mcp/search.ts',
|
|
248
|
-
} as any,
|
|
249
|
-
'analyze-tool': {
|
|
250
|
-
name: 'analyze-tool',
|
|
251
|
-
description: 'Analyze tool',
|
|
252
|
-
pikkuFuncId: 'mcpAnalyzeTool',
|
|
253
|
-
tags: ['mcp', 'analytics'],
|
|
254
|
-
middleware: [],
|
|
255
|
-
permissions: [],
|
|
256
|
-
sourceFile: '/test/project/src/mcp/analyze.ts',
|
|
257
|
-
} as any,
|
|
258
|
-
},
|
|
259
|
-
resourcesMeta: {
|
|
260
|
-
'docs-resource': {
|
|
261
|
-
title: 'Docs Resource',
|
|
262
|
-
description: 'Documentation resource',
|
|
263
|
-
uri: 'docs://resource',
|
|
264
|
-
pikkuFuncId: 'mcpDocsResource',
|
|
265
|
-
tags: ['mcp', 'docs'],
|
|
266
|
-
middleware: [],
|
|
267
|
-
permissions: [],
|
|
268
|
-
sourceFile: '/test/project/src/mcp/docs.ts',
|
|
269
|
-
} as any,
|
|
270
|
-
},
|
|
271
|
-
promptsMeta: {
|
|
272
|
-
'help-prompt': {
|
|
273
|
-
name: 'help-prompt',
|
|
274
|
-
description: 'Help prompt',
|
|
275
|
-
pikkuFuncId: 'mcpHelpPrompt',
|
|
276
|
-
tags: ['mcp', 'help'],
|
|
277
|
-
middleware: [],
|
|
278
|
-
permissions: [],
|
|
279
|
-
sourceFile: '/test/project/src/mcp/help.ts',
|
|
280
|
-
} as any,
|
|
281
|
-
},
|
|
282
|
-
files: new Set([
|
|
283
|
-
'/test/project/src/mcp/search.ts',
|
|
284
|
-
'/test/project/src/mcp/analyze.ts',
|
|
285
|
-
'/test/project/src/mcp/docs.ts',
|
|
286
|
-
'/test/project/src/mcp/help.ts',
|
|
287
|
-
]),
|
|
288
|
-
},
|
|
289
|
-
cli: {
|
|
290
|
-
meta: {
|
|
291
|
-
programs: {
|
|
292
|
-
'my-cli': {
|
|
293
|
-
sourceFile: '/test/project/src/cli/program.ts',
|
|
294
|
-
commands: {
|
|
295
|
-
build: {
|
|
296
|
-
pikkuFuncId: 'cliCommand',
|
|
297
|
-
tags: ['cli', 'build'],
|
|
298
|
-
middleware: [],
|
|
299
|
-
positionals: [],
|
|
300
|
-
options: {},
|
|
301
|
-
sourceFile: '/test/project/src/cli/build.ts',
|
|
302
|
-
} as any,
|
|
303
|
-
test: {
|
|
304
|
-
pikkuFuncId: 'cliTestCommand',
|
|
305
|
-
tags: ['cli', 'test'],
|
|
306
|
-
middleware: [],
|
|
307
|
-
positionals: [],
|
|
308
|
-
options: {},
|
|
309
|
-
sourceFile: '/test/project/src/cli/test.ts',
|
|
310
|
-
} as any,
|
|
311
|
-
},
|
|
312
|
-
} as any,
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
files: new Set([
|
|
316
|
-
'/test/project/src/cli/program.ts',
|
|
317
|
-
'/test/project/src/cli/build.ts',
|
|
318
|
-
'/test/project/src/cli/test.ts',
|
|
319
|
-
]),
|
|
320
|
-
},
|
|
321
|
-
middleware: {
|
|
322
|
-
definitions: {},
|
|
323
|
-
instances: {},
|
|
324
|
-
tagMiddleware: new Map(),
|
|
325
|
-
},
|
|
326
|
-
channelMiddleware: {
|
|
327
|
-
definitions: {},
|
|
328
|
-
instances: {},
|
|
329
|
-
tagMiddleware: new Map(),
|
|
330
|
-
},
|
|
331
|
-
permissions: {
|
|
332
|
-
definitions: {},
|
|
333
|
-
instances: {},
|
|
334
|
-
},
|
|
335
|
-
serviceAggregation: {
|
|
336
|
-
requiredServices: new Set(),
|
|
337
|
-
usedFunctions: new Set(),
|
|
338
|
-
usedMiddleware: new Set(),
|
|
339
|
-
usedPermissions: new Set(),
|
|
340
|
-
},
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Mock logger for testing
|
|
345
|
-
const mockLogger = {
|
|
346
|
-
info: () => {},
|
|
347
|
-
error: () => {},
|
|
348
|
-
warn: () => {},
|
|
349
|
-
debug: () => {},
|
|
350
|
-
diagnostic: () => {},
|
|
351
|
-
critical: () => {},
|
|
352
|
-
hasCriticalErrors: () => false,
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
describe('filterInspectorState', () => {
|
|
356
|
-
describe('No filters - returns original state', () => {
|
|
357
|
-
test('should return original state when no filters provided', () => {
|
|
358
|
-
const state = createMockInspectorState()
|
|
359
|
-
const filters: InspectorFilters = {}
|
|
360
|
-
|
|
361
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
362
|
-
|
|
363
|
-
assert.equal(result, state) // Should be the same reference
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
test('should return original state when all filter arrays are empty', () => {
|
|
367
|
-
const state = createMockInspectorState()
|
|
368
|
-
const filters: InspectorFilters = {
|
|
369
|
-
names: [],
|
|
370
|
-
tags: [],
|
|
371
|
-
wires: [],
|
|
372
|
-
directories: [],
|
|
373
|
-
httpRoutes: [],
|
|
374
|
-
httpMethods: [],
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
378
|
-
|
|
379
|
-
assert.equal(result, state)
|
|
380
|
-
})
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
describe('HTTP filtering', () => {
|
|
384
|
-
test('should filter HTTP routes by type', () => {
|
|
385
|
-
const state = createMockInspectorState()
|
|
386
|
-
const filters: InspectorFilters = {
|
|
387
|
-
wires: ['channel'], // Only channels, not HTTP
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
391
|
-
|
|
392
|
-
// HTTP routes should be empty
|
|
393
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
394
|
-
assert.equal(Object.keys(result.http.meta.post).length, 0)
|
|
395
|
-
|
|
396
|
-
// Channels should still exist
|
|
397
|
-
assert.equal(Object.keys(result.channels.meta).length, 2)
|
|
398
|
-
})
|
|
399
|
-
|
|
400
|
-
test('should filter HTTP routes by tags', () => {
|
|
401
|
-
const state = createMockInspectorState()
|
|
402
|
-
const filters: InspectorFilters = {
|
|
403
|
-
wires: ['http'],
|
|
404
|
-
tags: ['admin'],
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
408
|
-
|
|
409
|
-
// Only admin route should remain
|
|
410
|
-
assert.equal(Object.keys(result.http.meta.get).length, 1)
|
|
411
|
-
assert.ok(result.http.meta.get['/admin/settings'])
|
|
412
|
-
assert.equal(Object.keys(result.http.meta.post).length, 0)
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
test('should filter HTTP routes by httpMethod', () => {
|
|
416
|
-
const state = createMockInspectorState()
|
|
417
|
-
const filters: InspectorFilters = {
|
|
418
|
-
httpMethods: ['GET'],
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
422
|
-
|
|
423
|
-
// Only GET routes should remain
|
|
424
|
-
assert.equal(Object.keys(result.http.meta.get).length, 2)
|
|
425
|
-
assert.equal(Object.keys(result.http.meta.post).length, 0)
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
test('should filter HTTP routes by httpRoute pattern', () => {
|
|
429
|
-
const state = createMockInspectorState()
|
|
430
|
-
const filters: InspectorFilters = {
|
|
431
|
-
httpRoutes: ['/api/*'],
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
435
|
-
|
|
436
|
-
// Only /api/* routes should remain
|
|
437
|
-
assert.ok(result.http.meta.get['/api/users'])
|
|
438
|
-
assert.ok(result.http.meta.post['/api/users'])
|
|
439
|
-
assert.ok(!result.http.meta.get['/admin/settings'])
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
test('should filter HTTP routes by directory', () => {
|
|
443
|
-
const state = createMockInspectorState()
|
|
444
|
-
const filters: InspectorFilters = {
|
|
445
|
-
wires: ['http'],
|
|
446
|
-
directories: ['src/admin'],
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
450
|
-
|
|
451
|
-
// Only admin routes should remain
|
|
452
|
-
assert.equal(Object.keys(result.http.meta.get).length, 1)
|
|
453
|
-
assert.ok(result.http.meta.get['/admin/settings'])
|
|
454
|
-
})
|
|
455
|
-
|
|
456
|
-
test('should filter HTTP routes by function name', () => {
|
|
457
|
-
const state = createMockInspectorState()
|
|
458
|
-
const filters: InspectorFilters = {
|
|
459
|
-
wires: ['http'],
|
|
460
|
-
names: ['getUsers'],
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
464
|
-
|
|
465
|
-
assert.equal(Object.keys(result.http.meta.get).length, 1)
|
|
466
|
-
assert.ok(result.http.meta.get['/api/users'])
|
|
467
|
-
assert.equal(result.http.meta.get['/api/users'].pikkuFuncId, 'getUsers')
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
test('should filter HTTP routes by name wildcard', () => {
|
|
471
|
-
const state = createMockInspectorState()
|
|
472
|
-
const filters: InspectorFilters = {
|
|
473
|
-
wires: ['http'],
|
|
474
|
-
names: ['get*'],
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
478
|
-
|
|
479
|
-
// Should match getUsers and getAdminSettings
|
|
480
|
-
assert.equal(Object.keys(result.http.meta.get).length, 2)
|
|
481
|
-
assert.equal(Object.keys(result.http.meta.post).length, 0)
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
test('should track middleware and permissions for filtered HTTP routes', () => {
|
|
485
|
-
const state = createMockInspectorState()
|
|
486
|
-
const filters: InspectorFilters = {
|
|
487
|
-
wires: ['http'],
|
|
488
|
-
tags: ['admin'],
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
492
|
-
|
|
493
|
-
// Check that service aggregation includes the middleware and permissions
|
|
494
|
-
assert.ok(result.serviceAggregation.usedMiddleware.has('authMiddleware'))
|
|
495
|
-
assert.ok(
|
|
496
|
-
result.serviceAggregation.usedPermissions.has('adminPermission')
|
|
497
|
-
)
|
|
498
|
-
assert.ok(result.serviceAggregation.usedFunctions.has('getAdminSettings'))
|
|
499
|
-
})
|
|
500
|
-
})
|
|
501
|
-
|
|
502
|
-
describe('Channel filtering', () => {
|
|
503
|
-
test('should filter channels by type', () => {
|
|
504
|
-
const state = createMockInspectorState()
|
|
505
|
-
const filters: InspectorFilters = {
|
|
506
|
-
wires: ['channel'],
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
510
|
-
|
|
511
|
-
// Channels should exist
|
|
512
|
-
assert.equal(Object.keys(result.channels.meta).length, 2)
|
|
513
|
-
|
|
514
|
-
// HTTP routes should be empty
|
|
515
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
516
|
-
assert.equal(Object.keys(result.http.meta.post).length, 0)
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
test('should filter channels by tags', () => {
|
|
520
|
-
const state = createMockInspectorState()
|
|
521
|
-
const filters: InspectorFilters = {
|
|
522
|
-
wires: ['channel'],
|
|
523
|
-
tags: ['admin'],
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
527
|
-
|
|
528
|
-
assert.equal(Object.keys(result.channels.meta).length, 1)
|
|
529
|
-
assert.ok(result.channels.meta['admin-channel'])
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
test('should filter channels by name', () => {
|
|
533
|
-
const state = createMockInspectorState()
|
|
534
|
-
const filters: InspectorFilters = {
|
|
535
|
-
names: ['chat-channel'],
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
539
|
-
|
|
540
|
-
assert.equal(Object.keys(result.channels.meta).length, 1)
|
|
541
|
-
assert.ok(result.channels.meta['chat-channel'])
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
test('should filter channels by name wildcard', () => {
|
|
545
|
-
const state = createMockInspectorState()
|
|
546
|
-
const filters: InspectorFilters = {
|
|
547
|
-
names: ['*-channel'],
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
551
|
-
|
|
552
|
-
assert.equal(Object.keys(result.channels.meta).length, 2)
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
test('should repopulate channel files from surviving metadata', () => {
|
|
556
|
-
const state = createMockInspectorState()
|
|
557
|
-
const filters: InspectorFilters = {
|
|
558
|
-
wires: ['channel'],
|
|
559
|
-
tags: ['admin'],
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
563
|
-
|
|
564
|
-
assert.deepEqual(Array.from(result.channels.files).sort(), [
|
|
565
|
-
'/test/project/src/channels/admin.ts',
|
|
566
|
-
])
|
|
567
|
-
})
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
describe('Scheduled task filtering', () => {
|
|
571
|
-
test('should filter scheduled tasks by type', () => {
|
|
572
|
-
const state = createMockInspectorState()
|
|
573
|
-
const filters: InspectorFilters = {
|
|
574
|
-
wires: ['scheduler'],
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
578
|
-
|
|
579
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 2)
|
|
580
|
-
|
|
581
|
-
// Other types should be empty
|
|
582
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
583
|
-
assert.equal(Object.keys(result.channels.meta).length, 0)
|
|
584
|
-
})
|
|
585
|
-
|
|
586
|
-
test('should filter scheduled tasks by tags', () => {
|
|
587
|
-
const state = createMockInspectorState()
|
|
588
|
-
const filters: InspectorFilters = {
|
|
589
|
-
wires: ['scheduler'],
|
|
590
|
-
tags: ['reports'],
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
594
|
-
|
|
595
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 1)
|
|
596
|
-
assert.ok(result.scheduledTasks.meta['daily-report'])
|
|
597
|
-
})
|
|
598
|
-
|
|
599
|
-
test('should filter scheduled tasks by name', () => {
|
|
600
|
-
const state = createMockInspectorState()
|
|
601
|
-
const filters: InspectorFilters = {
|
|
602
|
-
names: ['daily-report'],
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
606
|
-
|
|
607
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 1)
|
|
608
|
-
assert.ok(result.scheduledTasks.meta['daily-report'])
|
|
609
|
-
})
|
|
610
|
-
|
|
611
|
-
test('should filter scheduled tasks by name wildcard', () => {
|
|
612
|
-
const state = createMockInspectorState()
|
|
613
|
-
const filters: InspectorFilters = {
|
|
614
|
-
names: ['*-cleanup'],
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
618
|
-
|
|
619
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 1)
|
|
620
|
-
assert.ok(result.scheduledTasks.meta['hourly-cleanup'])
|
|
621
|
-
})
|
|
622
|
-
|
|
623
|
-
test('should repopulate scheduled task files from surviving metadata', () => {
|
|
624
|
-
const state = createMockInspectorState()
|
|
625
|
-
const filters: InspectorFilters = {
|
|
626
|
-
wires: ['scheduler'],
|
|
627
|
-
tags: ['reports'],
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
631
|
-
|
|
632
|
-
assert.deepEqual(Array.from(result.scheduledTasks.files).sort(), [
|
|
633
|
-
'/test/project/src/tasks/reports.ts',
|
|
634
|
-
])
|
|
635
|
-
})
|
|
636
|
-
})
|
|
637
|
-
|
|
638
|
-
describe('Queue worker filtering', () => {
|
|
639
|
-
test('should filter queue workers by type', () => {
|
|
640
|
-
const state = createMockInspectorState()
|
|
641
|
-
const filters: InspectorFilters = {
|
|
642
|
-
wires: ['queue'],
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
646
|
-
|
|
647
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 2)
|
|
648
|
-
|
|
649
|
-
// Other types should be empty
|
|
650
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
651
|
-
assert.equal(Object.keys(result.channels.meta).length, 0)
|
|
652
|
-
})
|
|
653
|
-
|
|
654
|
-
test('should filter queue workers by tags', () => {
|
|
655
|
-
const state = createMockInspectorState()
|
|
656
|
-
const filters: InspectorFilters = {
|
|
657
|
-
wires: ['queue'],
|
|
658
|
-
tags: ['email'],
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
662
|
-
|
|
663
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 1)
|
|
664
|
-
assert.ok(result.queueWorkers.meta['email-worker'])
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
test('should filter queue workers by name', () => {
|
|
668
|
-
const state = createMockInspectorState()
|
|
669
|
-
const filters: InspectorFilters = {
|
|
670
|
-
names: ['email-worker'],
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
674
|
-
|
|
675
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 1)
|
|
676
|
-
assert.ok(result.queueWorkers.meta['email-worker'])
|
|
677
|
-
})
|
|
678
|
-
|
|
679
|
-
test('should filter queue workers by name wildcard', () => {
|
|
680
|
-
const state = createMockInspectorState()
|
|
681
|
-
const filters: InspectorFilters = {
|
|
682
|
-
names: ['*-worker'],
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
686
|
-
|
|
687
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 2)
|
|
688
|
-
})
|
|
689
|
-
|
|
690
|
-
test('should repopulate queue worker files from surviving metadata', () => {
|
|
691
|
-
const state = createMockInspectorState()
|
|
692
|
-
const filters: InspectorFilters = {
|
|
693
|
-
wires: ['queue'],
|
|
694
|
-
tags: ['email'],
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
698
|
-
|
|
699
|
-
assert.deepEqual(Array.from(result.queueWorkers.files).sort(), [
|
|
700
|
-
'/test/project/src/workers/email.ts',
|
|
701
|
-
])
|
|
702
|
-
})
|
|
703
|
-
})
|
|
704
|
-
|
|
705
|
-
describe('MCP endpoint filtering', () => {
|
|
706
|
-
test('should filter MCP tools by type', () => {
|
|
707
|
-
const state = createMockInspectorState()
|
|
708
|
-
const filters: InspectorFilters = {
|
|
709
|
-
wires: ['mcp'],
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
713
|
-
|
|
714
|
-
assert.equal(Object.keys(result.mcpEndpoints.toolsMeta).length, 2)
|
|
715
|
-
assert.equal(Object.keys(result.mcpEndpoints.resourcesMeta).length, 1)
|
|
716
|
-
assert.equal(Object.keys(result.mcpEndpoints.promptsMeta).length, 1)
|
|
717
|
-
|
|
718
|
-
// Other types should be empty
|
|
719
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
720
|
-
})
|
|
721
|
-
|
|
722
|
-
test('should filter MCP endpoints by tags', () => {
|
|
723
|
-
const state = createMockInspectorState()
|
|
724
|
-
const filters: InspectorFilters = {
|
|
725
|
-
wires: ['mcp'],
|
|
726
|
-
tags: ['search'],
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
730
|
-
|
|
731
|
-
assert.equal(Object.keys(result.mcpEndpoints.toolsMeta).length, 1)
|
|
732
|
-
assert.ok(result.mcpEndpoints.toolsMeta['search-tool'])
|
|
733
|
-
assert.equal(Object.keys(result.mcpEndpoints.resourcesMeta).length, 0)
|
|
734
|
-
})
|
|
735
|
-
|
|
736
|
-
test('should filter MCP endpoints by name', () => {
|
|
737
|
-
const state = createMockInspectorState()
|
|
738
|
-
const filters: InspectorFilters = {
|
|
739
|
-
names: ['search-tool'],
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
743
|
-
|
|
744
|
-
assert.equal(Object.keys(result.mcpEndpoints.toolsMeta).length, 1)
|
|
745
|
-
assert.ok(result.mcpEndpoints.toolsMeta['search-tool'])
|
|
746
|
-
})
|
|
747
|
-
|
|
748
|
-
test('should filter MCP endpoints by name wildcard', () => {
|
|
749
|
-
const state = createMockInspectorState()
|
|
750
|
-
const filters: InspectorFilters = {
|
|
751
|
-
names: ['*-tool'],
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
755
|
-
|
|
756
|
-
assert.equal(Object.keys(result.mcpEndpoints.toolsMeta).length, 2)
|
|
757
|
-
assert.equal(Object.keys(result.mcpEndpoints.resourcesMeta).length, 0)
|
|
758
|
-
assert.equal(Object.keys(result.mcpEndpoints.promptsMeta).length, 0)
|
|
759
|
-
})
|
|
760
|
-
|
|
761
|
-
test('should repopulate MCP files from surviving metadata', () => {
|
|
762
|
-
const state = createMockInspectorState()
|
|
763
|
-
const filters: InspectorFilters = {
|
|
764
|
-
wires: ['mcp'],
|
|
765
|
-
tags: ['search'],
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
769
|
-
|
|
770
|
-
assert.deepEqual(Array.from(result.mcpEndpoints.files).sort(), [
|
|
771
|
-
'/test/project/src/mcp/search.ts',
|
|
772
|
-
])
|
|
773
|
-
})
|
|
774
|
-
})
|
|
775
|
-
|
|
776
|
-
describe('CLI filtering', () => {
|
|
777
|
-
test('should filter CLI commands by type', () => {
|
|
778
|
-
const state = createMockInspectorState()
|
|
779
|
-
const filters: InspectorFilters = {
|
|
780
|
-
wires: ['cli'],
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
784
|
-
|
|
785
|
-
assert.equal(
|
|
786
|
-
Object.keys(result.cli.meta.programs['my-cli'].commands).length,
|
|
787
|
-
2
|
|
788
|
-
)
|
|
789
|
-
|
|
790
|
-
// Other types should be empty
|
|
791
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
792
|
-
})
|
|
793
|
-
|
|
794
|
-
test('should filter CLI commands by tags', () => {
|
|
795
|
-
const state = createMockInspectorState()
|
|
796
|
-
const filters: InspectorFilters = {
|
|
797
|
-
wires: ['cli'],
|
|
798
|
-
tags: ['build'],
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
802
|
-
|
|
803
|
-
assert.equal(
|
|
804
|
-
Object.keys(result.cli.meta.programs['my-cli'].commands).length,
|
|
805
|
-
1
|
|
806
|
-
)
|
|
807
|
-
assert.ok(result.cli.meta.programs['my-cli'].commands['build'])
|
|
808
|
-
})
|
|
809
|
-
|
|
810
|
-
test('should filter CLI commands by name', () => {
|
|
811
|
-
const state = createMockInspectorState()
|
|
812
|
-
const filters: InspectorFilters = {
|
|
813
|
-
names: ['build'],
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
817
|
-
|
|
818
|
-
assert.equal(
|
|
819
|
-
Object.keys(result.cli.meta.programs['my-cli'].commands).length,
|
|
820
|
-
1
|
|
821
|
-
)
|
|
822
|
-
assert.ok(result.cli.meta.programs['my-cli'].commands['build'])
|
|
823
|
-
})
|
|
824
|
-
|
|
825
|
-
test('should remove program if all commands are filtered out', () => {
|
|
826
|
-
const state = createMockInspectorState()
|
|
827
|
-
const filters: InspectorFilters = {
|
|
828
|
-
wires: ['cli'],
|
|
829
|
-
tags: ['non-existent'],
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
833
|
-
|
|
834
|
-
// Program should be removed
|
|
835
|
-
assert.equal(Object.keys(result.cli.meta.programs).length, 0)
|
|
836
|
-
})
|
|
837
|
-
|
|
838
|
-
test('should repopulate CLI files from surviving metadata', () => {
|
|
839
|
-
const state = createMockInspectorState()
|
|
840
|
-
const filters: InspectorFilters = {
|
|
841
|
-
wires: ['cli'],
|
|
842
|
-
tags: ['build'],
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
846
|
-
|
|
847
|
-
assert.deepEqual(Array.from(result.cli.files).sort(), [
|
|
848
|
-
'/test/project/src/cli/build.ts',
|
|
849
|
-
'/test/project/src/cli/program.ts',
|
|
850
|
-
])
|
|
851
|
-
})
|
|
852
|
-
})
|
|
853
|
-
|
|
854
|
-
describe('Wire exclusion', () => {
|
|
855
|
-
test('should exclude queue and scheduler wires while keeping http', () => {
|
|
856
|
-
const state = createMockInspectorState()
|
|
857
|
-
const filters: InspectorFilters = {
|
|
858
|
-
excludeWires: ['queue', 'scheduler'],
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
862
|
-
|
|
863
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 0)
|
|
864
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 0)
|
|
865
|
-
assert.ok(Object.keys(result.http.meta.get).length > 0)
|
|
866
|
-
})
|
|
867
|
-
})
|
|
868
|
-
|
|
869
|
-
describe('Combined filtering', () => {
|
|
870
|
-
test('should filter multiple types at once', () => {
|
|
871
|
-
const state = createMockInspectorState()
|
|
872
|
-
const filters: InspectorFilters = {
|
|
873
|
-
wires: ['http', 'channel'],
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
877
|
-
|
|
878
|
-
// HTTP and channels should exist
|
|
879
|
-
assert.equal(Object.keys(result.http.meta.get).length, 2)
|
|
880
|
-
assert.equal(Object.keys(result.channels.meta).length, 2)
|
|
881
|
-
|
|
882
|
-
// Other types should be empty
|
|
883
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 0)
|
|
884
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 0)
|
|
885
|
-
})
|
|
886
|
-
|
|
887
|
-
test('should filter by tags across all types', () => {
|
|
888
|
-
const state = createMockInspectorState()
|
|
889
|
-
const filters: InspectorFilters = {
|
|
890
|
-
tags: ['admin'],
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
894
|
-
|
|
895
|
-
// Only admin-tagged items should remain
|
|
896
|
-
assert.equal(Object.keys(result.http.meta.get).length, 1)
|
|
897
|
-
assert.ok(result.http.meta.get['/admin/settings'])
|
|
898
|
-
assert.equal(Object.keys(result.channels.meta).length, 1)
|
|
899
|
-
assert.ok(result.channels.meta['admin-channel'])
|
|
900
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 0)
|
|
901
|
-
})
|
|
902
|
-
|
|
903
|
-
test('should handle complex combined filters', () => {
|
|
904
|
-
const state = createMockInspectorState()
|
|
905
|
-
const filters: InspectorFilters = {
|
|
906
|
-
wires: ['http'],
|
|
907
|
-
tags: ['api'],
|
|
908
|
-
httpMethods: ['POST'],
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
912
|
-
|
|
913
|
-
// Only POST /api/users should remain
|
|
914
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
915
|
-
assert.equal(Object.keys(result.http.meta.post).length, 1)
|
|
916
|
-
assert.ok(result.http.meta.post['/api/users'])
|
|
917
|
-
})
|
|
918
|
-
})
|
|
919
|
-
|
|
920
|
-
describe('Service aggregation', () => {
|
|
921
|
-
test('should reset and recalculate service aggregation', () => {
|
|
922
|
-
const state = createMockInspectorState()
|
|
923
|
-
|
|
924
|
-
// Pre-populate with some old data
|
|
925
|
-
state.serviceAggregation.usedFunctions.add('oldFunction')
|
|
926
|
-
state.serviceAggregation.usedMiddleware.add('oldMiddleware')
|
|
927
|
-
|
|
928
|
-
const filters: InspectorFilters = {
|
|
929
|
-
wires: ['http'],
|
|
930
|
-
tags: ['admin'],
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
934
|
-
|
|
935
|
-
// Old data should be gone, new data should be present
|
|
936
|
-
assert.ok(!result.serviceAggregation.usedFunctions.has('oldFunction'))
|
|
937
|
-
assert.ok(!result.serviceAggregation.usedMiddleware.has('oldMiddleware'))
|
|
938
|
-
assert.ok(result.serviceAggregation.usedFunctions.has('getAdminSettings'))
|
|
939
|
-
assert.ok(result.serviceAggregation.usedMiddleware.has('authMiddleware'))
|
|
940
|
-
})
|
|
941
|
-
|
|
942
|
-
test('should track all used functions across different wiring types', () => {
|
|
943
|
-
const state = createMockInspectorState()
|
|
944
|
-
const filters: InspectorFilters = {
|
|
945
|
-
tags: ['admin'], // Filter for admin-tagged items across all types
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
949
|
-
|
|
950
|
-
// Should include functions from HTTP and channels
|
|
951
|
-
assert.ok(result.serviceAggregation.usedFunctions.has('getAdminSettings'))
|
|
952
|
-
assert.ok(
|
|
953
|
-
result.serviceAggregation.usedFunctions.has('handleAdminMessage')
|
|
954
|
-
)
|
|
955
|
-
})
|
|
956
|
-
})
|
|
957
|
-
|
|
958
|
-
describe('State immutability', () => {
|
|
959
|
-
test('should not mutate original state when filtering', () => {
|
|
960
|
-
const state = createMockInspectorState()
|
|
961
|
-
const originalHttpCount = Object.keys(state.http.meta.get).length
|
|
962
|
-
const originalChannelCount = Object.keys(state.channels.meta).length
|
|
963
|
-
|
|
964
|
-
const filters: InspectorFilters = {
|
|
965
|
-
wires: ['http'],
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
filterInspectorState(state, filters, mockLogger)
|
|
969
|
-
|
|
970
|
-
// Original state should not be modified
|
|
971
|
-
assert.equal(Object.keys(state.http.meta.get).length, originalHttpCount)
|
|
972
|
-
assert.equal(
|
|
973
|
-
Object.keys(state.channels.meta).length,
|
|
974
|
-
originalChannelCount
|
|
975
|
-
)
|
|
976
|
-
})
|
|
977
|
-
|
|
978
|
-
test('should create deep copies of metadata', () => {
|
|
979
|
-
const state = createMockInspectorState()
|
|
980
|
-
const filters: InspectorFilters = {
|
|
981
|
-
wires: ['http'],
|
|
982
|
-
tags: ['api'],
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
986
|
-
|
|
987
|
-
// Modify filtered state
|
|
988
|
-
delete result.http.meta.get['/api/users']
|
|
989
|
-
|
|
990
|
-
// Original should still have the route
|
|
991
|
-
assert.ok(state.http.meta.get['/api/users'])
|
|
992
|
-
})
|
|
993
|
-
})
|
|
994
|
-
|
|
995
|
-
describe('Edge cases', () => {
|
|
996
|
-
test('should handle empty state', () => {
|
|
997
|
-
const state = createMockInspectorState()
|
|
998
|
-
state.http.meta = {
|
|
999
|
-
get: {},
|
|
1000
|
-
post: {},
|
|
1001
|
-
put: {},
|
|
1002
|
-
patch: {},
|
|
1003
|
-
delete: {},
|
|
1004
|
-
head: {},
|
|
1005
|
-
options: {},
|
|
1006
|
-
}
|
|
1007
|
-
state.channels.meta = {}
|
|
1008
|
-
state.scheduledTasks.meta = {}
|
|
1009
|
-
state.queueWorkers.meta = {}
|
|
1010
|
-
|
|
1011
|
-
const filters: InspectorFilters = {
|
|
1012
|
-
wires: ['http'],
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1016
|
-
|
|
1017
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
1018
|
-
})
|
|
1019
|
-
|
|
1020
|
-
test('should handle filters that match nothing', () => {
|
|
1021
|
-
const state = createMockInspectorState()
|
|
1022
|
-
const filters: InspectorFilters = {
|
|
1023
|
-
tags: ['non-existent-tag'],
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1027
|
-
|
|
1028
|
-
// Everything should be filtered out
|
|
1029
|
-
assert.equal(Object.keys(result.http.meta.get).length, 0)
|
|
1030
|
-
assert.equal(Object.keys(result.channels.meta).length, 0)
|
|
1031
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 0)
|
|
1032
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 0)
|
|
1033
|
-
})
|
|
1034
|
-
|
|
1035
|
-
test('should handle missing optional metadata fields', () => {
|
|
1036
|
-
const state = createMockInspectorState()
|
|
1037
|
-
// Remove tags from one route
|
|
1038
|
-
delete state.http.meta.get['/api/users'].tags
|
|
1039
|
-
|
|
1040
|
-
const filters: InspectorFilters = {
|
|
1041
|
-
tags: ['api'],
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1045
|
-
|
|
1046
|
-
// Route without tags should not match
|
|
1047
|
-
assert.ok(!result.http.meta.get['/api/users'])
|
|
1048
|
-
// Route with tags should match
|
|
1049
|
-
assert.ok(result.http.meta.post['/api/users'])
|
|
1050
|
-
})
|
|
1051
|
-
})
|
|
1052
|
-
|
|
1053
|
-
describe('Real data tests (from templates/functions)', () => {
|
|
1054
|
-
test('should have loaded real state correctly', () => {
|
|
1055
|
-
assert.ok(realState)
|
|
1056
|
-
assert.ok(realState.http)
|
|
1057
|
-
assert.ok(realState.functions)
|
|
1058
|
-
assert.ok(realState.channels)
|
|
1059
|
-
assert.ok(realState.scheduledTasks)
|
|
1060
|
-
assert.ok(realState.queueWorkers)
|
|
1061
|
-
assert.ok(realState.mcpEndpoints)
|
|
1062
|
-
assert.ok(realState.cli)
|
|
1063
|
-
})
|
|
1064
|
-
|
|
1065
|
-
test('should filter HTTP routes by type in real data', () => {
|
|
1066
|
-
// BEFORE: Check what we have in the real state
|
|
1067
|
-
const beforeHttpCount = Object.values(
|
|
1068
|
-
realState.http.meta as Record<string, any>
|
|
1069
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1070
|
-
const beforeChannelCount = Object.keys(realState.channels.meta).length
|
|
1071
|
-
const beforeTaskCount = Object.keys(realState.scheduledTasks.meta).length
|
|
1072
|
-
const beforeQueueCount = Object.keys(realState.queueWorkers.meta).length
|
|
1073
|
-
|
|
1074
|
-
assert.ok(beforeHttpCount > 0, 'Real state should have HTTP routes')
|
|
1075
|
-
assert.ok(beforeChannelCount > 0, 'Real state should have channels')
|
|
1076
|
-
assert.ok(beforeTaskCount > 0, 'Real state should have scheduled tasks')
|
|
1077
|
-
assert.ok(beforeQueueCount > 0, 'Real state should have queue workers')
|
|
1078
|
-
|
|
1079
|
-
// FILTER: Apply HTTP type filter
|
|
1080
|
-
const filters: InspectorFilters = {
|
|
1081
|
-
wires: ['http'],
|
|
1082
|
-
}
|
|
1083
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1084
|
-
|
|
1085
|
-
// AFTER: Check filtered result
|
|
1086
|
-
const afterHttpCount = Object.values(
|
|
1087
|
-
result.http.meta as Record<string, any>
|
|
1088
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1089
|
-
const afterChannelCount = Object.keys(result.channels.meta).length
|
|
1090
|
-
const afterTaskCount = Object.keys(result.scheduledTasks.meta).length
|
|
1091
|
-
const afterQueueCount = Object.keys(result.queueWorkers.meta).length
|
|
1092
|
-
|
|
1093
|
-
// HTTP routes should remain
|
|
1094
|
-
assert.equal(
|
|
1095
|
-
afterHttpCount,
|
|
1096
|
-
beforeHttpCount,
|
|
1097
|
-
'HTTP routes should be preserved'
|
|
1098
|
-
)
|
|
1099
|
-
|
|
1100
|
-
// Other types should be filtered out
|
|
1101
|
-
assert.equal(afterChannelCount, 0, 'Channels should be filtered out')
|
|
1102
|
-
assert.equal(afterTaskCount, 0, 'Scheduled tasks should be filtered out')
|
|
1103
|
-
assert.equal(afterQueueCount, 0, 'Queue workers should be filtered out')
|
|
1104
|
-
})
|
|
1105
|
-
|
|
1106
|
-
test('should filter channels by type in real data', () => {
|
|
1107
|
-
const filters: InspectorFilters = {
|
|
1108
|
-
wires: ['channel'],
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1112
|
-
|
|
1113
|
-
// Should have channels
|
|
1114
|
-
assert.ok(
|
|
1115
|
-
Object.keys(result.channels.meta).length > 0,
|
|
1116
|
-
'Should have channels'
|
|
1117
|
-
)
|
|
1118
|
-
|
|
1119
|
-
// Other types should be filtered out
|
|
1120
|
-
const httpRouteCount = Object.values(result.http.meta).flatMap((m) =>
|
|
1121
|
-
Object.keys(m)
|
|
1122
|
-
).length
|
|
1123
|
-
assert.equal(httpRouteCount, 0)
|
|
1124
|
-
assert.equal(Object.keys(result.scheduledTasks.meta).length, 0)
|
|
1125
|
-
assert.equal(Object.keys(result.queueWorkers.meta).length, 0)
|
|
1126
|
-
})
|
|
1127
|
-
|
|
1128
|
-
test('should filter scheduler tasks by type in real data', () => {
|
|
1129
|
-
// BEFORE: Check what we have in the real state
|
|
1130
|
-
const beforeHttpCount = Object.values(
|
|
1131
|
-
realState.http.meta as Record<string, any>
|
|
1132
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1133
|
-
const beforeChannelCount = Object.keys(realState.channels.meta).length
|
|
1134
|
-
const beforeTaskCount = Object.keys(realState.scheduledTasks.meta).length
|
|
1135
|
-
const beforeQueueCount = Object.keys(realState.queueWorkers.meta).length
|
|
1136
|
-
|
|
1137
|
-
assert.ok(beforeHttpCount > 0, 'Real state should have HTTP routes')
|
|
1138
|
-
assert.ok(beforeChannelCount > 0, 'Real state should have channels')
|
|
1139
|
-
assert.ok(beforeTaskCount > 0, 'Real state should have scheduled tasks')
|
|
1140
|
-
assert.ok(beforeQueueCount > 0, 'Real state should have queue workers')
|
|
1141
|
-
|
|
1142
|
-
// FILTER: Apply scheduler type filter
|
|
1143
|
-
const filters: InspectorFilters = {
|
|
1144
|
-
wires: ['scheduler'],
|
|
1145
|
-
}
|
|
1146
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1147
|
-
|
|
1148
|
-
// AFTER: Check filtered result
|
|
1149
|
-
const afterHttpCount = Object.values(
|
|
1150
|
-
result.http.meta as Record<string, any>
|
|
1151
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1152
|
-
const afterChannelCount = Object.keys(result.channels.meta).length
|
|
1153
|
-
const afterTaskCount = Object.keys(result.scheduledTasks.meta).length
|
|
1154
|
-
const afterQueueCount = Object.keys(result.queueWorkers.meta).length
|
|
1155
|
-
|
|
1156
|
-
// Scheduled tasks should remain
|
|
1157
|
-
assert.equal(
|
|
1158
|
-
afterTaskCount,
|
|
1159
|
-
beforeTaskCount,
|
|
1160
|
-
'Scheduled tasks should be preserved'
|
|
1161
|
-
)
|
|
1162
|
-
|
|
1163
|
-
// Other types should be filtered out
|
|
1164
|
-
assert.equal(afterHttpCount, 0, 'HTTP routes should be filtered out')
|
|
1165
|
-
assert.equal(afterChannelCount, 0, 'Channels should be filtered out')
|
|
1166
|
-
assert.equal(afterQueueCount, 0, 'Queue workers should be filtered out')
|
|
1167
|
-
})
|
|
1168
|
-
|
|
1169
|
-
test('should filter queue workers by type in real data', () => {
|
|
1170
|
-
// BEFORE: Check what we have in the real state
|
|
1171
|
-
const beforeHttpCount = Object.values(
|
|
1172
|
-
realState.http.meta as Record<string, any>
|
|
1173
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1174
|
-
const beforeChannelCount = Object.keys(realState.channels.meta).length
|
|
1175
|
-
const beforeTaskCount = Object.keys(realState.scheduledTasks.meta).length
|
|
1176
|
-
const beforeQueueCount = Object.keys(realState.queueWorkers.meta).length
|
|
1177
|
-
|
|
1178
|
-
assert.ok(beforeHttpCount > 0, 'Real state should have HTTP routes')
|
|
1179
|
-
assert.ok(beforeChannelCount > 0, 'Real state should have channels')
|
|
1180
|
-
assert.ok(beforeTaskCount > 0, 'Real state should have scheduled tasks')
|
|
1181
|
-
assert.ok(beforeQueueCount > 0, 'Real state should have queue workers')
|
|
1182
|
-
|
|
1183
|
-
// FILTER: Apply queue type filter
|
|
1184
|
-
const filters: InspectorFilters = {
|
|
1185
|
-
wires: ['queue'],
|
|
1186
|
-
}
|
|
1187
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1188
|
-
|
|
1189
|
-
// AFTER: Check filtered result
|
|
1190
|
-
const afterHttpCount = Object.values(
|
|
1191
|
-
result.http.meta as Record<string, any>
|
|
1192
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1193
|
-
const afterChannelCount = Object.keys(result.channels.meta).length
|
|
1194
|
-
const afterTaskCount = Object.keys(result.scheduledTasks.meta).length
|
|
1195
|
-
const afterQueueCount = Object.keys(result.queueWorkers.meta).length
|
|
1196
|
-
|
|
1197
|
-
// Queue workers should remain
|
|
1198
|
-
assert.equal(
|
|
1199
|
-
afterQueueCount,
|
|
1200
|
-
beforeQueueCount,
|
|
1201
|
-
'Queue workers should be preserved'
|
|
1202
|
-
)
|
|
1203
|
-
|
|
1204
|
-
// Other types should be filtered out
|
|
1205
|
-
assert.equal(afterHttpCount, 0, 'HTTP routes should be filtered out')
|
|
1206
|
-
assert.equal(afterChannelCount, 0, 'Channels should be filtered out')
|
|
1207
|
-
assert.equal(afterTaskCount, 0, 'Scheduled tasks should be filtered out')
|
|
1208
|
-
})
|
|
1209
|
-
|
|
1210
|
-
test('should filter MCP endpoints by type in real data', () => {
|
|
1211
|
-
// BEFORE: Check what we have in the real state
|
|
1212
|
-
const beforeHttpCount = Object.values(
|
|
1213
|
-
realState.http.meta as Record<string, any>
|
|
1214
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1215
|
-
const beforeChannelCount = Object.keys(realState.channels.meta).length
|
|
1216
|
-
const beforeTaskCount = Object.keys(realState.scheduledTasks.meta).length
|
|
1217
|
-
const beforeQueueCount = Object.keys(realState.queueWorkers.meta).length
|
|
1218
|
-
const beforeMcpToolsCount = Object.keys(
|
|
1219
|
-
realState.mcpEndpoints.toolsMeta
|
|
1220
|
-
).length
|
|
1221
|
-
const beforeMcpResourcesCount = Object.keys(
|
|
1222
|
-
realState.mcpEndpoints.resourcesMeta
|
|
1223
|
-
).length
|
|
1224
|
-
const beforeMcpPromptsCount = Object.keys(
|
|
1225
|
-
realState.mcpEndpoints.promptsMeta
|
|
1226
|
-
).length
|
|
1227
|
-
|
|
1228
|
-
assert.ok(beforeHttpCount > 0, 'Real state should have HTTP routes')
|
|
1229
|
-
assert.ok(beforeChannelCount > 0, 'Real state should have channels')
|
|
1230
|
-
assert.ok(beforeTaskCount > 0, 'Real state should have scheduled tasks')
|
|
1231
|
-
assert.ok(beforeQueueCount > 0, 'Real state should have queue workers')
|
|
1232
|
-
assert.ok(
|
|
1233
|
-
beforeMcpToolsCount + beforeMcpResourcesCount + beforeMcpPromptsCount >
|
|
1234
|
-
0,
|
|
1235
|
-
'Real state should have MCP endpoints'
|
|
1236
|
-
)
|
|
1237
|
-
|
|
1238
|
-
// FILTER: Apply MCP type filter
|
|
1239
|
-
const filters: InspectorFilters = {
|
|
1240
|
-
wires: ['mcp'],
|
|
1241
|
-
}
|
|
1242
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1243
|
-
|
|
1244
|
-
// AFTER: Check filtered result
|
|
1245
|
-
const afterHttpCount = Object.values(
|
|
1246
|
-
result.http.meta as Record<string, any>
|
|
1247
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1248
|
-
const afterChannelCount = Object.keys(result.channels.meta).length
|
|
1249
|
-
const afterTaskCount = Object.keys(result.scheduledTasks.meta).length
|
|
1250
|
-
const afterQueueCount = Object.keys(result.queueWorkers.meta).length
|
|
1251
|
-
const afterMcpToolsCount = Object.keys(
|
|
1252
|
-
result.mcpEndpoints.toolsMeta
|
|
1253
|
-
).length
|
|
1254
|
-
const afterMcpResourcesCount = Object.keys(
|
|
1255
|
-
result.mcpEndpoints.resourcesMeta
|
|
1256
|
-
).length
|
|
1257
|
-
const afterMcpPromptsCount = Object.keys(
|
|
1258
|
-
result.mcpEndpoints.promptsMeta
|
|
1259
|
-
).length
|
|
1260
|
-
|
|
1261
|
-
// MCP endpoints should remain
|
|
1262
|
-
assert.equal(
|
|
1263
|
-
afterMcpToolsCount,
|
|
1264
|
-
beforeMcpToolsCount,
|
|
1265
|
-
'MCP tools should be preserved'
|
|
1266
|
-
)
|
|
1267
|
-
assert.equal(
|
|
1268
|
-
afterMcpResourcesCount,
|
|
1269
|
-
beforeMcpResourcesCount,
|
|
1270
|
-
'MCP resources should be preserved'
|
|
1271
|
-
)
|
|
1272
|
-
assert.equal(
|
|
1273
|
-
afterMcpPromptsCount,
|
|
1274
|
-
beforeMcpPromptsCount,
|
|
1275
|
-
'MCP prompts should be preserved'
|
|
1276
|
-
)
|
|
1277
|
-
|
|
1278
|
-
// Other types should be filtered out
|
|
1279
|
-
assert.equal(afterHttpCount, 0, 'HTTP routes should be filtered out')
|
|
1280
|
-
assert.equal(afterChannelCount, 0, 'Channels should be filtered out')
|
|
1281
|
-
assert.equal(afterTaskCount, 0, 'Scheduled tasks should be filtered out')
|
|
1282
|
-
assert.equal(afterQueueCount, 0, 'Queue workers should be filtered out')
|
|
1283
|
-
})
|
|
1284
|
-
|
|
1285
|
-
test('should filter CLI programs by type in real data', () => {
|
|
1286
|
-
// BEFORE: Check what we have in the real state
|
|
1287
|
-
const beforeHttpCount = Object.values(
|
|
1288
|
-
realState.http.meta as Record<string, any>
|
|
1289
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1290
|
-
const beforeChannelCount = Object.keys(realState.channels.meta).length
|
|
1291
|
-
const beforeTaskCount = Object.keys(realState.scheduledTasks.meta).length
|
|
1292
|
-
const beforeQueueCount = Object.keys(realState.queueWorkers.meta).length
|
|
1293
|
-
const beforeCliProgramCount = Object.keys(
|
|
1294
|
-
realState.cli.meta.programs
|
|
1295
|
-
).length
|
|
1296
|
-
|
|
1297
|
-
assert.ok(beforeHttpCount > 0, 'Real state should have HTTP routes')
|
|
1298
|
-
assert.ok(beforeChannelCount > 0, 'Real state should have channels')
|
|
1299
|
-
assert.ok(beforeTaskCount > 0, 'Real state should have scheduled tasks')
|
|
1300
|
-
assert.ok(beforeQueueCount > 0, 'Real state should have queue workers')
|
|
1301
|
-
assert.ok(
|
|
1302
|
-
beforeCliProgramCount > 0,
|
|
1303
|
-
'Real state should have CLI programs'
|
|
1304
|
-
)
|
|
1305
|
-
|
|
1306
|
-
// FILTER: Apply CLI type filter
|
|
1307
|
-
const filters: InspectorFilters = {
|
|
1308
|
-
wires: ['cli'],
|
|
1309
|
-
}
|
|
1310
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1311
|
-
|
|
1312
|
-
// AFTER: Check filtered result
|
|
1313
|
-
const afterHttpCount = Object.values(
|
|
1314
|
-
result.http.meta as Record<string, any>
|
|
1315
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1316
|
-
const afterChannelCount = Object.keys(result.channels.meta).length
|
|
1317
|
-
const afterTaskCount = Object.keys(result.scheduledTasks.meta).length
|
|
1318
|
-
const afterQueueCount = Object.keys(result.queueWorkers.meta).length
|
|
1319
|
-
const afterCliProgramCount = Object.keys(result.cli.meta.programs).length
|
|
1320
|
-
|
|
1321
|
-
// CLI programs should remain
|
|
1322
|
-
assert.equal(
|
|
1323
|
-
afterCliProgramCount,
|
|
1324
|
-
beforeCliProgramCount,
|
|
1325
|
-
'CLI programs should be preserved'
|
|
1326
|
-
)
|
|
1327
|
-
|
|
1328
|
-
// Other types should be filtered out
|
|
1329
|
-
assert.equal(afterHttpCount, 0, 'HTTP routes should be filtered out')
|
|
1330
|
-
assert.equal(afterChannelCount, 0, 'Channels should be filtered out')
|
|
1331
|
-
assert.equal(afterTaskCount, 0, 'Scheduled tasks should be filtered out')
|
|
1332
|
-
assert.equal(afterQueueCount, 0, 'Queue workers should be filtered out')
|
|
1333
|
-
})
|
|
1334
|
-
|
|
1335
|
-
test('should filter by HTTP method in real data', () => {
|
|
1336
|
-
// BEFORE: Check what HTTP methods exist
|
|
1337
|
-
const beforeGetCount = Object.keys(realState.http.meta.get || {}).length
|
|
1338
|
-
const beforePostCount = Object.keys(realState.http.meta.post || {}).length
|
|
1339
|
-
const beforePutCount = Object.keys(realState.http.meta.put || {}).length
|
|
1340
|
-
const beforeDeleteCount = Object.keys(
|
|
1341
|
-
realState.http.meta.delete || {}
|
|
1342
|
-
).length
|
|
1343
|
-
|
|
1344
|
-
assert.ok(
|
|
1345
|
-
beforeGetCount + beforePostCount + beforePutCount + beforeDeleteCount >
|
|
1346
|
-
0,
|
|
1347
|
-
'Real state should have HTTP routes'
|
|
1348
|
-
)
|
|
1349
|
-
|
|
1350
|
-
// FILTER: Apply GET method filter
|
|
1351
|
-
const filters: InspectorFilters = {
|
|
1352
|
-
wires: ['http'],
|
|
1353
|
-
httpMethods: ['GET'],
|
|
1354
|
-
}
|
|
1355
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1356
|
-
|
|
1357
|
-
// AFTER: Check filtered result
|
|
1358
|
-
const afterGetCount = Object.keys(result.http.meta.get || {}).length
|
|
1359
|
-
const afterPostCount = Object.keys(result.http.meta.post || {}).length
|
|
1360
|
-
const afterPutCount = Object.keys(result.http.meta.put || {}).length
|
|
1361
|
-
const afterDeleteCount = Object.keys(result.http.meta.delete || {}).length
|
|
1362
|
-
|
|
1363
|
-
// GET routes should remain
|
|
1364
|
-
assert.equal(
|
|
1365
|
-
afterGetCount,
|
|
1366
|
-
beforeGetCount,
|
|
1367
|
-
'GET routes should be preserved'
|
|
1368
|
-
)
|
|
1369
|
-
|
|
1370
|
-
// Other methods should be filtered out
|
|
1371
|
-
assert.equal(afterPostCount, 0, 'POST routes should be filtered out')
|
|
1372
|
-
assert.equal(afterPutCount, 0, 'PUT routes should be filtered out')
|
|
1373
|
-
assert.equal(afterDeleteCount, 0, 'DELETE routes should be filtered out')
|
|
1374
|
-
})
|
|
1375
|
-
|
|
1376
|
-
test('should filter by name pattern in real data', () => {
|
|
1377
|
-
// BEFORE: Count all endpoints
|
|
1378
|
-
const beforeHttpCount = Object.values(
|
|
1379
|
-
realState.http.meta as Record<string, any>
|
|
1380
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1381
|
-
const beforeChannelCount = Object.keys(realState.channels.meta).length
|
|
1382
|
-
const beforeTaskCount = Object.keys(realState.scheduledTasks.meta).length
|
|
1383
|
-
const beforeQueueCount = Object.keys(realState.queueWorkers.meta).length
|
|
1384
|
-
const beforeTotal =
|
|
1385
|
-
beforeHttpCount +
|
|
1386
|
-
beforeChannelCount +
|
|
1387
|
-
beforeTaskCount +
|
|
1388
|
-
beforeQueueCount
|
|
1389
|
-
|
|
1390
|
-
assert.ok(beforeTotal > 0, 'Real state should have endpoints')
|
|
1391
|
-
|
|
1392
|
-
// FILTER: Apply name pattern filter for functions with "http" in their name
|
|
1393
|
-
const filters: InspectorFilters = {
|
|
1394
|
-
names: ['*http*'],
|
|
1395
|
-
}
|
|
1396
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1397
|
-
|
|
1398
|
-
// AFTER: Count all filtered endpoints
|
|
1399
|
-
const afterHttpCount = Object.values(
|
|
1400
|
-
result.http.meta as Record<string, any>
|
|
1401
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1402
|
-
const afterChannelCount = Object.keys(result.channels.meta).length
|
|
1403
|
-
const afterTaskCount = Object.keys(result.scheduledTasks.meta).length
|
|
1404
|
-
const afterQueueCount = Object.keys(result.queueWorkers.meta).length
|
|
1405
|
-
const afterTotal =
|
|
1406
|
-
afterHttpCount + afterChannelCount + afterTaskCount + afterQueueCount
|
|
1407
|
-
|
|
1408
|
-
// After filtering by name pattern, we should have fewer or equal items
|
|
1409
|
-
assert.ok(
|
|
1410
|
-
afterTotal <= beforeTotal,
|
|
1411
|
-
'Filtered count should be less than or equal to original'
|
|
1412
|
-
)
|
|
1413
|
-
})
|
|
1414
|
-
|
|
1415
|
-
test('should not mutate real state when filtering', () => {
|
|
1416
|
-
const originalHttpCount = Object.values(
|
|
1417
|
-
realState.http.meta as Record<string, any>
|
|
1418
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1419
|
-
const originalChannelCount = Object.keys(realState.channels.meta).length
|
|
1420
|
-
|
|
1421
|
-
const filters: InspectorFilters = {
|
|
1422
|
-
wires: ['http'],
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
filterInspectorState(realState, filters, mockLogger)
|
|
1426
|
-
|
|
1427
|
-
// Original should not be modified
|
|
1428
|
-
const afterHttpCount = Object.values(
|
|
1429
|
-
realState.http.meta as Record<string, any>
|
|
1430
|
-
).flatMap((m) => Object.keys(m)).length
|
|
1431
|
-
const afterChannelCount = Object.keys(realState.channels.meta).length
|
|
1432
|
-
|
|
1433
|
-
assert.equal(afterHttpCount, originalHttpCount)
|
|
1434
|
-
assert.equal(afterChannelCount, originalChannelCount)
|
|
1435
|
-
})
|
|
1436
|
-
|
|
1437
|
-
test('should aggregate services correctly with real data', () => {
|
|
1438
|
-
const filters: InspectorFilters = {
|
|
1439
|
-
wires: ['http'],
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1443
|
-
|
|
1444
|
-
// Service aggregation should be recalculated
|
|
1445
|
-
assert.ok(result.serviceAggregation)
|
|
1446
|
-
assert.ok(result.serviceAggregation.usedFunctions instanceof Set)
|
|
1447
|
-
assert.ok(result.serviceAggregation.usedMiddleware instanceof Set)
|
|
1448
|
-
assert.ok(result.serviceAggregation.usedPermissions instanceof Set)
|
|
1449
|
-
assert.ok(result.serviceAggregation.requiredServices instanceof Set)
|
|
1450
|
-
})
|
|
1451
|
-
|
|
1452
|
-
test('should handle combined filters with real data', () => {
|
|
1453
|
-
// BEFORE: Check what we have
|
|
1454
|
-
const beforeGetCount = Object.keys(realState.http.meta.get || {}).length
|
|
1455
|
-
const beforePostCount = Object.keys(realState.http.meta.post || {}).length
|
|
1456
|
-
const beforePutCount = Object.keys(realState.http.meta.put || {}).length
|
|
1457
|
-
const beforeChannelCount = Object.keys(realState.channels.meta).length
|
|
1458
|
-
const beforeTaskCount = Object.keys(realState.scheduledTasks.meta).length
|
|
1459
|
-
const beforeQueueCount = Object.keys(realState.queueWorkers.meta).length
|
|
1460
|
-
|
|
1461
|
-
assert.ok(
|
|
1462
|
-
beforeGetCount + beforePostCount > 0,
|
|
1463
|
-
'Real state should have GET/POST routes'
|
|
1464
|
-
)
|
|
1465
|
-
assert.ok(beforeChannelCount > 0, 'Real state should have channels')
|
|
1466
|
-
|
|
1467
|
-
// FILTER: Apply combined filters (HTTP and channels, GET/POST methods only)
|
|
1468
|
-
const filters: InspectorFilters = {
|
|
1469
|
-
wires: ['http', 'channel'],
|
|
1470
|
-
httpMethods: ['GET', 'POST'],
|
|
1471
|
-
}
|
|
1472
|
-
const result = filterInspectorState(realState, filters, mockLogger)
|
|
1473
|
-
|
|
1474
|
-
// AFTER: Check filtered result
|
|
1475
|
-
const afterGetCount = Object.keys(result.http.meta.get || {}).length
|
|
1476
|
-
const afterPostCount = Object.keys(result.http.meta.post || {}).length
|
|
1477
|
-
const afterPutCount = Object.keys(result.http.meta.put || {}).length
|
|
1478
|
-
const afterChannelCount = Object.keys(result.channels.meta).length
|
|
1479
|
-
const afterTaskCount = Object.keys(result.scheduledTasks.meta).length
|
|
1480
|
-
const afterQueueCount = Object.keys(result.queueWorkers.meta).length
|
|
1481
|
-
|
|
1482
|
-
// GET and POST routes should be preserved
|
|
1483
|
-
assert.equal(
|
|
1484
|
-
afterGetCount,
|
|
1485
|
-
beforeGetCount,
|
|
1486
|
-
'GET routes should be preserved'
|
|
1487
|
-
)
|
|
1488
|
-
assert.equal(
|
|
1489
|
-
afterPostCount,
|
|
1490
|
-
beforePostCount,
|
|
1491
|
-
'POST routes should be preserved'
|
|
1492
|
-
)
|
|
1493
|
-
|
|
1494
|
-
// Channels should be preserved (no HTTP method filter applies)
|
|
1495
|
-
assert.equal(
|
|
1496
|
-
afterChannelCount,
|
|
1497
|
-
beforeChannelCount,
|
|
1498
|
-
'Channels should be preserved'
|
|
1499
|
-
)
|
|
1500
|
-
|
|
1501
|
-
// Other methods/types should be filtered out
|
|
1502
|
-
assert.equal(afterPutCount, 0, 'PUT routes should be filtered out')
|
|
1503
|
-
assert.equal(afterTaskCount, 0, 'Scheduled tasks should be filtered out')
|
|
1504
|
-
assert.equal(afterQueueCount, 0, 'Queue workers should be filtered out')
|
|
1505
|
-
})
|
|
1506
|
-
})
|
|
1507
|
-
})
|
|
1508
|
-
|
|
1509
|
-
describe('filterInspectorState exclude filters', () => {
|
|
1510
|
-
test('exclude tags removes matching entries after include', () => {
|
|
1511
|
-
const state = createMockInspectorState()
|
|
1512
|
-
const filters: InspectorFilters = {
|
|
1513
|
-
tags: ['api', 'admin'],
|
|
1514
|
-
excludeTags: ['admin'],
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1518
|
-
|
|
1519
|
-
assert.ok(result.http.meta.get['/api/users'])
|
|
1520
|
-
assert.ok(result.http.meta.post['/api/users'])
|
|
1521
|
-
assert.ok(!result.http.meta.get['/admin/settings'])
|
|
1522
|
-
})
|
|
1523
|
-
|
|
1524
|
-
test('exclude names wins over broad names include', () => {
|
|
1525
|
-
const state = createMockInspectorState()
|
|
1526
|
-
const filters: InspectorFilters = {
|
|
1527
|
-
names: ['*'],
|
|
1528
|
-
excludeNames: ['getAdminSettings'],
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1532
|
-
|
|
1533
|
-
assert.ok(result.http.meta.get['/api/users'])
|
|
1534
|
-
assert.ok(result.http.meta.post['/api/users'])
|
|
1535
|
-
assert.ok(!result.http.meta.get['/admin/settings'])
|
|
1536
|
-
})
|
|
1537
|
-
|
|
1538
|
-
test('exclude target prunes server target while keeping serverless', () => {
|
|
1539
|
-
const state = createMockInspectorState()
|
|
1540
|
-
;(state.functions.meta as any).getAdminSettings.deploy = 'server'
|
|
1541
|
-
;(state.functions.meta as any).createUser.deploy = 'serverless'
|
|
1542
|
-
|
|
1543
|
-
const filters: InspectorFilters = {
|
|
1544
|
-
target: ['server', 'serverless'],
|
|
1545
|
-
excludeTarget: ['server'],
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1549
|
-
|
|
1550
|
-
assert.ok(result.http.meta.post['/api/users'])
|
|
1551
|
-
assert.ok(!result.http.meta.get['/admin/settings'])
|
|
1552
|
-
})
|
|
1553
|
-
})
|
|
1554
|
-
|
|
1555
|
-
describe('addonServerlessIncompatible scoping', () => {
|
|
1556
|
-
// Service names inside addons are scoped to that addon's namespace.
|
|
1557
|
-
// 'FfmpegService' in addon A is unrelated to 'FfmpegService' in the app or
|
|
1558
|
-
// another addon. The state.addonServerlessIncompatible map must NOT be
|
|
1559
|
-
// merged into the global filter — it is intentionally kept separate.
|
|
1560
|
-
|
|
1561
|
-
test('addon serverlessIncompatible does not bleed into app-level function resolution', () => {
|
|
1562
|
-
const state = createMockInspectorState()
|
|
1563
|
-
|
|
1564
|
-
// App function uses a service named 'FfmpegService' (e.g. its own service,
|
|
1565
|
-
// unrelated to any addon's FfmpegService).
|
|
1566
|
-
;(state.functions.meta as any).getUsers.services = {
|
|
1567
|
-
services: ['FfmpegService'],
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
// An addon declares FfmpegService as serverless-incompatible for ITS namespace.
|
|
1571
|
-
;(state as any).addonServerlessIncompatible = new Map([
|
|
1572
|
-
['ffmpeg', ['FfmpegService']],
|
|
1573
|
-
])
|
|
1574
|
-
|
|
1575
|
-
// Filter: only keep serverless functions. The filters object does NOT
|
|
1576
|
-
// include serverlessIncompatible — that's app-level config only.
|
|
1577
|
-
const filters: InspectorFilters = {
|
|
1578
|
-
target: ['serverless'],
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1582
|
-
|
|
1583
|
-
// The app's getUsers function must survive — the addon's scoped
|
|
1584
|
-
// serverlessIncompatible should have no effect on app-level resolution.
|
|
1585
|
-
assert.ok(
|
|
1586
|
-
result.http.meta.get['/api/users'],
|
|
1587
|
-
'app function using same-named service as addon should not be pruned by addon scoping'
|
|
1588
|
-
)
|
|
1589
|
-
})
|
|
1590
|
-
|
|
1591
|
-
test('app-level serverlessIncompatible in filters still prunes matching app functions', () => {
|
|
1592
|
-
const state = createMockInspectorState()
|
|
1593
|
-
|
|
1594
|
-
;(state.functions.meta as any).getUsers.services = {
|
|
1595
|
-
services: ['HeavyService'],
|
|
1596
|
-
}
|
|
1597
|
-
;(state as any).addonServerlessIncompatible = new Map([
|
|
1598
|
-
['some-addon', ['HeavyService']],
|
|
1599
|
-
])
|
|
1600
|
-
|
|
1601
|
-
// The APP explicitly declares HeavyService incompatible via filters.
|
|
1602
|
-
const filters: InspectorFilters = {
|
|
1603
|
-
target: ['serverless'],
|
|
1604
|
-
serverlessIncompatible: ['HeavyService'],
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
const result = filterInspectorState(state, filters, mockLogger)
|
|
1608
|
-
|
|
1609
|
-
// App explicitly opted in via filters.serverlessIncompatible → pruned.
|
|
1610
|
-
assert.ok(
|
|
1611
|
-
!result.http.meta.get['/api/users'],
|
|
1612
|
-
'app function should be pruned when app explicitly lists service in filters.serverlessIncompatible'
|
|
1613
|
-
)
|
|
1614
|
-
})
|
|
1615
|
-
})
|
|
1616
|
-
|
|
1617
|
-
describe("an addon's own deploy conflict is the consumer's only when used", () => {
|
|
1618
|
-
// An addon publishes metadata for everything it exports. A function it marks
|
|
1619
|
-
// `deploy: 'serverless'` while also naming one of its services
|
|
1620
|
-
// serverless-incompatible is the addon author's contradiction — but the
|
|
1621
|
-
// deploy scan walks every published function, so without care it fails the
|
|
1622
|
-
// build of a consumer that never wires that function.
|
|
1623
|
-
const withConflictingAddonFunction = (route?: string) => {
|
|
1624
|
-
const state = createMockInspectorState()
|
|
1625
|
-
;(state as any).addonFunctions = {
|
|
1626
|
-
ffmpeg: {
|
|
1627
|
-
transcode: {
|
|
1628
|
-
deploy: 'serverless',
|
|
1629
|
-
services: { services: ['FfmpegService'] },
|
|
1630
|
-
},
|
|
1631
|
-
},
|
|
1632
|
-
}
|
|
1633
|
-
state.addonServerlessIncompatible = new Map([['ffmpeg', ['FfmpegService']]])
|
|
1634
|
-
if (route) {
|
|
1635
|
-
;(state.http.meta.get as any)[route] = {
|
|
1636
|
-
pikkuFuncId: 'ffmpeg:transcode',
|
|
1637
|
-
route,
|
|
1638
|
-
method: 'GET',
|
|
1639
|
-
tags: [],
|
|
1640
|
-
middleware: [],
|
|
1641
|
-
permissions: [],
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
return state
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
test('a conflicting addon function nothing wires does not fail the build', () => {
|
|
1648
|
-
const state = withConflictingAddonFunction()
|
|
1649
|
-
|
|
1650
|
-
assert.doesNotThrow(() =>
|
|
1651
|
-
filterInspectorState(state, { target: ['serverless'] }, mockLogger)
|
|
1652
|
-
)
|
|
1653
|
-
})
|
|
1654
|
-
|
|
1655
|
-
test('wiring that same function reports the conflict', () => {
|
|
1656
|
-
const state = withConflictingAddonFunction('/transcode')
|
|
1657
|
-
|
|
1658
|
-
assert.throws(
|
|
1659
|
-
() => filterInspectorState(state, { target: ['serverless'] }, mockLogger),
|
|
1660
|
-
/FfmpegService/
|
|
1661
|
-
)
|
|
1662
|
-
})
|
|
1663
|
-
})
|
|
1664
|
-
|
|
1665
|
-
describe('addonServerlessIncompatible serialization roundtrip', () => {
|
|
1666
|
-
// Uses getInitialInspectorState to guarantee all Map fields are initialized —
|
|
1667
|
-
// the lightweight mock in createMockInspectorState() omits fields like
|
|
1668
|
-
// schemaLookup that serializeInspectorState needs.
|
|
1669
|
-
|
|
1670
|
-
test('Map serializes to Array<[string, string[]]> and back', () => {
|
|
1671
|
-
const state = getInitialInspectorState('/test')
|
|
1672
|
-
state.addonServerlessIncompatible = new Map([
|
|
1673
|
-
['ffmpeg', ['FfmpegService', 'FfprobeService']],
|
|
1674
|
-
['humandesign', ['HumanDesignService']],
|
|
1675
|
-
])
|
|
1676
|
-
|
|
1677
|
-
const serialized = serializeInspectorState(state)
|
|
1678
|
-
assert.deepStrictEqual(serialized.addonServerlessIncompatible, [
|
|
1679
|
-
['ffmpeg', ['FfmpegService', 'FfprobeService']],
|
|
1680
|
-
['humandesign', ['HumanDesignService']],
|
|
1681
|
-
])
|
|
1682
|
-
|
|
1683
|
-
const restored = deserializeInspectorState(serialized)
|
|
1684
|
-
assert.ok(restored.addonServerlessIncompatible instanceof Map)
|
|
1685
|
-
assert.deepStrictEqual(restored.addonServerlessIncompatible.get('ffmpeg'), [
|
|
1686
|
-
'FfmpegService',
|
|
1687
|
-
'FfprobeService',
|
|
1688
|
-
])
|
|
1689
|
-
assert.deepStrictEqual(
|
|
1690
|
-
restored.addonServerlessIncompatible.get('humandesign'),
|
|
1691
|
-
['HumanDesignService']
|
|
1692
|
-
)
|
|
1693
|
-
})
|
|
1694
|
-
|
|
1695
|
-
test('empty Map serializes and restores correctly', () => {
|
|
1696
|
-
const state = getInitialInspectorState('/test')
|
|
1697
|
-
// addonServerlessIncompatible is already an empty Map from getInitialInspectorState
|
|
1698
|
-
|
|
1699
|
-
const serialized = serializeInspectorState(state)
|
|
1700
|
-
assert.deepStrictEqual(serialized.addonServerlessIncompatible, [])
|
|
1701
|
-
|
|
1702
|
-
const restored = deserializeInspectorState(serialized)
|
|
1703
|
-
assert.ok(restored.addonServerlessIncompatible instanceof Map)
|
|
1704
|
-
assert.strictEqual(restored.addonServerlessIncompatible.size, 0)
|
|
1705
|
-
})
|
|
1706
|
-
})
|
|
1707
|
-
|
|
1708
|
-
describe('addon bootstrap tree-shake', () => {
|
|
1709
|
-
const withAddon = (
|
|
1710
|
-
mutate?: (state: Omit<InspectorState, 'typesLookup'>) => void
|
|
1711
|
-
) => {
|
|
1712
|
-
const state = createMockInspectorState()
|
|
1713
|
-
state.rpc.wireAddonDeclarations = new Map([
|
|
1714
|
-
['console', { package: '@pikku/addon-console' }],
|
|
1715
|
-
])
|
|
1716
|
-
state.rpc.usedAddons = new Set(['console'])
|
|
1717
|
-
state.rpc.invokedFunctions = new Set(['console:streamWorkflowRun'])
|
|
1718
|
-
mutate?.(state)
|
|
1719
|
-
return state
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
test('drops an addon nothing kept references', () => {
|
|
1723
|
-
const state = withAddon()
|
|
1724
|
-
const filtered = filterInspectorState(
|
|
1725
|
-
state,
|
|
1726
|
-
{ names: ['getUsers'] },
|
|
1727
|
-
mockLogger
|
|
1728
|
-
)
|
|
1729
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 0)
|
|
1730
|
-
assert.strictEqual(filtered.rpc.usedAddons.size, 0)
|
|
1731
|
-
})
|
|
1732
|
-
|
|
1733
|
-
test('keeps an addon when a kept wiring targets one of its functions', () => {
|
|
1734
|
-
const state = withAddon((s) => {
|
|
1735
|
-
s.http.meta.get['/console/stream'] = {
|
|
1736
|
-
pikkuFuncId: 'console:streamWorkflowRun',
|
|
1737
|
-
route: '/console/stream',
|
|
1738
|
-
method: 'GET',
|
|
1739
|
-
tags: [],
|
|
1740
|
-
middleware: [],
|
|
1741
|
-
permissions: [],
|
|
1742
|
-
} as any
|
|
1743
|
-
})
|
|
1744
|
-
const filtered = filterInspectorState(
|
|
1745
|
-
state,
|
|
1746
|
-
{ names: ['console:streamWorkflowRun'] },
|
|
1747
|
-
mockLogger
|
|
1748
|
-
)
|
|
1749
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1750
|
-
assert.ok(filtered.rpc.wireAddonDeclarations.has('console'))
|
|
1751
|
-
})
|
|
1752
|
-
|
|
1753
|
-
test('keeps an addon when a kept route ref()-targets one of its functions', () => {
|
|
1754
|
-
const state = withAddon((s) => {
|
|
1755
|
-
s.http.meta.get['/workflow-run/stream'] = {
|
|
1756
|
-
pikkuFuncId: 'console:streamWorkflowRun',
|
|
1757
|
-
packageName: '@pikku/console',
|
|
1758
|
-
route: '/workflow-run/stream',
|
|
1759
|
-
method: 'GET',
|
|
1760
|
-
tags: ['stream'],
|
|
1761
|
-
middleware: [],
|
|
1762
|
-
permissions: [],
|
|
1763
|
-
} as any
|
|
1764
|
-
})
|
|
1765
|
-
const filtered = filterInspectorState(
|
|
1766
|
-
state,
|
|
1767
|
-
{ tags: ['stream'] },
|
|
1768
|
-
mockLogger
|
|
1769
|
-
)
|
|
1770
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1771
|
-
assert.ok(filtered.rpc.wireAddonDeclarations.has('console'))
|
|
1772
|
-
assert.ok(
|
|
1773
|
-
filtered.serviceAggregation.usedFunctions.has('console:streamWorkflowRun')
|
|
1774
|
-
)
|
|
1775
|
-
})
|
|
1776
|
-
|
|
1777
|
-
test('keeps a ref()-wired route selected by its wiring id', () => {
|
|
1778
|
-
const state = withAddon((s) => {
|
|
1779
|
-
s.http.meta.get['/workflow-run/stream'] = {
|
|
1780
|
-
pikkuFuncId: 'console:streamWorkflowRun',
|
|
1781
|
-
packageName: '@pikku/console',
|
|
1782
|
-
route: '/workflow-run/stream',
|
|
1783
|
-
method: 'GET',
|
|
1784
|
-
tags: [],
|
|
1785
|
-
middleware: [],
|
|
1786
|
-
permissions: [],
|
|
1787
|
-
} as any
|
|
1788
|
-
})
|
|
1789
|
-
const filtered = filterInspectorState(
|
|
1790
|
-
state,
|
|
1791
|
-
{ names: ['http:get:/workflow-run/stream'] },
|
|
1792
|
-
mockLogger
|
|
1793
|
-
)
|
|
1794
|
-
assert.ok(filtered.http.meta.get['/workflow-run/stream'])
|
|
1795
|
-
assert.ok(
|
|
1796
|
-
filtered.serviceAggregation.usedFunctions.has('console:streamWorkflowRun')
|
|
1797
|
-
)
|
|
1798
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1799
|
-
assert.ok(filtered.rpc.wireAddonDeclarations.has('console'))
|
|
1800
|
-
})
|
|
1801
|
-
|
|
1802
|
-
test('drops the addon when the ref()-wired route is filtered out', () => {
|
|
1803
|
-
const state = withAddon((s) => {
|
|
1804
|
-
s.http.meta.get['/workflow-run/stream'] = {
|
|
1805
|
-
pikkuFuncId: 'console:streamWorkflowRun',
|
|
1806
|
-
packageName: '@pikku/console',
|
|
1807
|
-
route: '/workflow-run/stream',
|
|
1808
|
-
method: 'GET',
|
|
1809
|
-
tags: [],
|
|
1810
|
-
middleware: [],
|
|
1811
|
-
permissions: [],
|
|
1812
|
-
} as any
|
|
1813
|
-
})
|
|
1814
|
-
const filtered = filterInspectorState(
|
|
1815
|
-
state,
|
|
1816
|
-
{ names: ['getUsers'] },
|
|
1817
|
-
mockLogger
|
|
1818
|
-
)
|
|
1819
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 0)
|
|
1820
|
-
assert.ok(
|
|
1821
|
-
!filtered.serviceAggregation.usedFunctions.has(
|
|
1822
|
-
'console:streamWorkflowRun'
|
|
1823
|
-
)
|
|
1824
|
-
)
|
|
1825
|
-
})
|
|
1826
|
-
|
|
1827
|
-
test('keeps an addon when a kept MCP tool targets one of its functions', () => {
|
|
1828
|
-
const state = withAddon((s) => {
|
|
1829
|
-
s.mcpEndpoints.toolsMeta['console:getSchema'] = {
|
|
1830
|
-
name: 'console:getSchema',
|
|
1831
|
-
description: 'addon tool',
|
|
1832
|
-
pikkuFuncId: 'console:getSchema',
|
|
1833
|
-
tags: [],
|
|
1834
|
-
middleware: [],
|
|
1835
|
-
permissions: [],
|
|
1836
|
-
} as any
|
|
1837
|
-
})
|
|
1838
|
-
const filtered = filterInspectorState(
|
|
1839
|
-
state,
|
|
1840
|
-
{ names: ['console:getSchema'] },
|
|
1841
|
-
mockLogger
|
|
1842
|
-
)
|
|
1843
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1844
|
-
})
|
|
1845
|
-
|
|
1846
|
-
test('keeps an addon when a kept agent lists one of its functions as a tool', () => {
|
|
1847
|
-
const state = withAddon((s) => {
|
|
1848
|
-
s.agents = s.agents ?? ({ agentsMeta: {} } as any)
|
|
1849
|
-
s.agents.agentsMeta['support-agent'] = {
|
|
1850
|
-
name: 'support-agent',
|
|
1851
|
-
pikkuFuncId: 'supportAgent',
|
|
1852
|
-
tools: ['console:getSchema'],
|
|
1853
|
-
tags: [],
|
|
1854
|
-
} as any
|
|
1855
|
-
})
|
|
1856
|
-
const filtered = filterInspectorState(
|
|
1857
|
-
state,
|
|
1858
|
-
{ names: ['support-agent'] },
|
|
1859
|
-
mockLogger
|
|
1860
|
-
)
|
|
1861
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1862
|
-
})
|
|
1863
|
-
|
|
1864
|
-
test('keeps an addon when a kept function body-invokes it', () => {
|
|
1865
|
-
const state = withAddon((s) => {
|
|
1866
|
-
s.rpc.invokedFunctionsByFile = new Map([
|
|
1867
|
-
['/test/project/src/api/users.ts', new Set(['console:getSchema'])],
|
|
1868
|
-
])
|
|
1869
|
-
})
|
|
1870
|
-
const filtered = filterInspectorState(
|
|
1871
|
-
state,
|
|
1872
|
-
{ names: ['getUsers'] },
|
|
1873
|
-
mockLogger
|
|
1874
|
-
)
|
|
1875
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1876
|
-
})
|
|
1877
|
-
|
|
1878
|
-
test('a kept-file body invoke joins the filtered usedFunctions', () => {
|
|
1879
|
-
const state = withAddon((s) => {
|
|
1880
|
-
s.rpc.invokedFunctionsByFile = new Map([
|
|
1881
|
-
['/test/project/src/api/users.ts', new Set(['console:getSchema'])],
|
|
1882
|
-
])
|
|
1883
|
-
})
|
|
1884
|
-
const filtered = filterInspectorState(
|
|
1885
|
-
state,
|
|
1886
|
-
{ names: ['getUsers'] },
|
|
1887
|
-
mockLogger
|
|
1888
|
-
)
|
|
1889
|
-
assert.ok(
|
|
1890
|
-
filtered.serviceAggregation.usedFunctions.has('console:getSchema')
|
|
1891
|
-
)
|
|
1892
|
-
})
|
|
1893
|
-
|
|
1894
|
-
test('drops an addon body-invoked only from filtered-out files', () => {
|
|
1895
|
-
const state = withAddon((s) => {
|
|
1896
|
-
s.rpc.invokedFunctionsByFile = new Map([
|
|
1897
|
-
['/test/project/src/admin/settings.ts', new Set(['console:getSchema'])],
|
|
1898
|
-
])
|
|
1899
|
-
})
|
|
1900
|
-
const filtered = filterInspectorState(
|
|
1901
|
-
state,
|
|
1902
|
-
{ names: ['getUsers'] },
|
|
1903
|
-
mockLogger
|
|
1904
|
-
)
|
|
1905
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 0)
|
|
1906
|
-
})
|
|
1907
|
-
|
|
1908
|
-
test('unfiltered state keeps all addons', () => {
|
|
1909
|
-
const state = withAddon()
|
|
1910
|
-
const filtered = filterInspectorState(state, {}, mockLogger)
|
|
1911
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1912
|
-
})
|
|
1913
|
-
|
|
1914
|
-
const withRefWiredAddonRoute = (
|
|
1915
|
-
addonFuncMeta: Record<string, unknown> = { services: [], optimized: false }
|
|
1916
|
-
) =>
|
|
1917
|
-
withAddon((s) => {
|
|
1918
|
-
s.http.meta.get['/workflow-run/stream'] = {
|
|
1919
|
-
pikkuFuncId: 'console:streamWorkflowRun',
|
|
1920
|
-
packageName: '@pikku/addon-console',
|
|
1921
|
-
route: '/workflow-run/stream',
|
|
1922
|
-
method: 'GET',
|
|
1923
|
-
tags: [],
|
|
1924
|
-
middleware: [],
|
|
1925
|
-
permissions: [],
|
|
1926
|
-
} as any
|
|
1927
|
-
s.addonFunctions = {
|
|
1928
|
-
console: { streamWorkflowRun: addonFuncMeta as any },
|
|
1929
|
-
}
|
|
1930
|
-
})
|
|
1931
|
-
|
|
1932
|
-
test('the deploy filter keeps a ref()-wired route onto a serverless addon function', () => {
|
|
1933
|
-
const filtered = filterInspectorState(
|
|
1934
|
-
withRefWiredAddonRoute(),
|
|
1935
|
-
{ target: ['serverless'] },
|
|
1936
|
-
mockLogger
|
|
1937
|
-
)
|
|
1938
|
-
assert.ok(filtered.http.meta.get['/workflow-run/stream'])
|
|
1939
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 1)
|
|
1940
|
-
assert.ok(filtered.rpc.wireAddonDeclarations.has('console'))
|
|
1941
|
-
})
|
|
1942
|
-
|
|
1943
|
-
test('the deploy filter drops a ref()-wired route onto a server-only addon function', () => {
|
|
1944
|
-
const filtered = filterInspectorState(
|
|
1945
|
-
withRefWiredAddonRoute({
|
|
1946
|
-
services: [],
|
|
1947
|
-
optimized: false,
|
|
1948
|
-
deploy: 'server',
|
|
1949
|
-
}),
|
|
1950
|
-
{ target: ['serverless'] },
|
|
1951
|
-
mockLogger
|
|
1952
|
-
)
|
|
1953
|
-
assert.ok(!filtered.http.meta.get['/workflow-run/stream'])
|
|
1954
|
-
assert.strictEqual(filtered.rpc.wireAddonDeclarations.size, 0)
|
|
1955
|
-
})
|
|
1956
|
-
|
|
1957
|
-
test("the deploy filter honours the addon's own serverlessIncompatible services", () => {
|
|
1958
|
-
const state = withRefWiredAddonRoute({
|
|
1959
|
-
services: { services: ['FfmpegService'] },
|
|
1960
|
-
optimized: false,
|
|
1961
|
-
})
|
|
1962
|
-
state.addonServerlessIncompatible = new Map([
|
|
1963
|
-
['console', ['FfmpegService']],
|
|
1964
|
-
])
|
|
1965
|
-
const filtered = filterInspectorState(
|
|
1966
|
-
state,
|
|
1967
|
-
{ target: ['serverless'] },
|
|
1968
|
-
mockLogger
|
|
1969
|
-
)
|
|
1970
|
-
assert.ok(!filtered.http.meta.get['/workflow-run/stream'])
|
|
1971
|
-
})
|
|
1972
|
-
})
|
|
1973
|
-
|
|
1974
|
-
describe('invokedFunctionsByFile serialization', () => {
|
|
1975
|
-
test('round-trips through serialize/deserialize', () => {
|
|
1976
|
-
const state = getInitialInspectorState('/test/project')
|
|
1977
|
-
state.rpc.invokedFunctionsByFile = new Map([
|
|
1978
|
-
['/test/project/src/api/users.ts', new Set(['console:getSchema'])],
|
|
1979
|
-
[
|
|
1980
|
-
'/test/project/src/api/tasks.ts',
|
|
1981
|
-
new Set(['listTasks', 'console:runAgent']),
|
|
1982
|
-
],
|
|
1983
|
-
])
|
|
1984
|
-
const restored = deserializeInspectorState(
|
|
1985
|
-
JSON.parse(JSON.stringify(serializeInspectorState(state as any)))
|
|
1986
|
-
)
|
|
1987
|
-
assert.ok(restored.rpc.invokedFunctionsByFile instanceof Map)
|
|
1988
|
-
assert.strictEqual(restored.rpc.invokedFunctionsByFile.size, 2)
|
|
1989
|
-
assert.deepStrictEqual(
|
|
1990
|
-
[
|
|
1991
|
-
...restored.rpc.invokedFunctionsByFile.get(
|
|
1992
|
-
'/test/project/src/api/tasks.ts'
|
|
1993
|
-
)!,
|
|
1994
|
-
].sort(),
|
|
1995
|
-
['console:runAgent', 'listTasks']
|
|
1996
|
-
)
|
|
1997
|
-
})
|
|
1998
|
-
|
|
1999
|
-
test('deserializing legacy state without the field yields an empty Map', () => {
|
|
2000
|
-
const serialized = JSON.parse(
|
|
2001
|
-
JSON.stringify(
|
|
2002
|
-
serializeInspectorState(
|
|
2003
|
-
getInitialInspectorState('/test/project') as any
|
|
2004
|
-
)
|
|
2005
|
-
)
|
|
2006
|
-
)
|
|
2007
|
-
delete serialized.rpc.invokedFunctionsByFile
|
|
2008
|
-
const restored = deserializeInspectorState(serialized)
|
|
2009
|
-
assert.ok(restored.rpc.invokedFunctionsByFile instanceof Map)
|
|
2010
|
-
assert.strictEqual(restored.rpc.invokedFunctionsByFile.size, 0)
|
|
2011
|
-
})
|
|
2012
|
-
})
|
|
2013
|
-
|
|
2014
|
-
describe('Workflow file pruning', () => {
|
|
2015
|
-
const workflowState = () => {
|
|
2016
|
-
const state = createMockInspectorState()
|
|
2017
|
-
state.workflows.meta = {
|
|
2018
|
-
moveOut: { pikkuFuncId: 'getUsers' } as any,
|
|
2019
|
-
theMoveOutScenario: { pikkuFuncId: 'createUser' } as any,
|
|
2020
|
-
}
|
|
2021
|
-
state.workflows.graphMeta = {
|
|
2022
|
-
moveOut: { pikkuFuncId: 'getUsers' } as any,
|
|
2023
|
-
theMoveOutScenario: {
|
|
2024
|
-
pikkuFuncId: 'createUser',
|
|
2025
|
-
source: 'scenario',
|
|
2026
|
-
} as any,
|
|
2027
|
-
}
|
|
2028
|
-
state.workflows.files = new Map([
|
|
2029
|
-
[
|
|
2030
|
-
'moveOut',
|
|
2031
|
-
{
|
|
2032
|
-
path: '/test/project/src/wirings/move-out.workflow.ts',
|
|
2033
|
-
exportedName: 'moveOut',
|
|
2034
|
-
},
|
|
2035
|
-
],
|
|
2036
|
-
[
|
|
2037
|
-
'theMoveOutScenario',
|
|
2038
|
-
{
|
|
2039
|
-
path: '/test/project/test/scenarios/the-move-out.scenario.ts',
|
|
2040
|
-
exportedName: 'theMoveOutScenario',
|
|
2041
|
-
},
|
|
2042
|
-
],
|
|
2043
|
-
])
|
|
2044
|
-
return state
|
|
2045
|
-
}
|
|
2046
|
-
|
|
2047
|
-
test('drops the file entry of a pruned workflow', () => {
|
|
2048
|
-
const result = filterInspectorState(
|
|
2049
|
-
workflowState(),
|
|
2050
|
-
{ names: ['getUsers'] },
|
|
2051
|
-
mockLogger
|
|
2052
|
-
)
|
|
2053
|
-
|
|
2054
|
-
assert.deepEqual(Object.keys(result.workflows.graphMeta), ['moveOut'])
|
|
2055
|
-
assert.deepEqual([...result.workflows.files.keys()], ['moveOut'])
|
|
2056
|
-
})
|
|
2057
|
-
|
|
2058
|
-
test('leaves the original state untouched', () => {
|
|
2059
|
-
const state = workflowState()
|
|
2060
|
-
filterInspectorState(state, { names: ['getUsers'] }, mockLogger)
|
|
2061
|
-
|
|
2062
|
-
assert.equal(state.workflows.files.size, 2)
|
|
2063
|
-
assert.equal(Object.keys(state.workflows.graphMeta).length, 2)
|
|
2064
|
-
})
|
|
2065
|
-
})
|
|
2066
|
-
|
|
2067
|
-
describe('invokedAgentsByFile', () => {
|
|
2068
|
-
test('round-trips through serialize/deserialize', () => {
|
|
2069
|
-
const state = getInitialInspectorState('/test/project')
|
|
2070
|
-
state.agents.invokedAgentsByFile = new Map([
|
|
2071
|
-
['/test/project/src/api/users.ts', new Set(['houseAssistant'])],
|
|
2072
|
-
[
|
|
2073
|
-
'/test/project/src/api/tasks.ts',
|
|
2074
|
-
new Set(['houseAssistant', 'gardenAssistant']),
|
|
2075
|
-
],
|
|
2076
|
-
])
|
|
2077
|
-
const restored = deserializeInspectorState(
|
|
2078
|
-
JSON.parse(JSON.stringify(serializeInspectorState(state as any)))
|
|
2079
|
-
)
|
|
2080
|
-
assert.ok(restored.agents.invokedAgentsByFile instanceof Map)
|
|
2081
|
-
assert.strictEqual(restored.agents.invokedAgentsByFile.size, 2)
|
|
2082
|
-
assert.deepStrictEqual(
|
|
2083
|
-
[
|
|
2084
|
-
...restored.agents.invokedAgentsByFile.get(
|
|
2085
|
-
'/test/project/src/api/tasks.ts'
|
|
2086
|
-
)!,
|
|
2087
|
-
].sort(),
|
|
2088
|
-
['gardenAssistant', 'houseAssistant']
|
|
2089
|
-
)
|
|
2090
|
-
})
|
|
2091
|
-
|
|
2092
|
-
test('deserializing legacy state without the field yields an empty Map', () => {
|
|
2093
|
-
const serialized = JSON.parse(
|
|
2094
|
-
JSON.stringify(
|
|
2095
|
-
serializeInspectorState(
|
|
2096
|
-
getInitialInspectorState('/test/project') as any
|
|
2097
|
-
)
|
|
2098
|
-
)
|
|
2099
|
-
)
|
|
2100
|
-
delete serialized.agents.invokedAgentsByFile
|
|
2101
|
-
const restored = deserializeInspectorState(serialized)
|
|
2102
|
-
assert.ok(restored.agents.invokedAgentsByFile instanceof Map)
|
|
2103
|
-
assert.strictEqual(restored.agents.invokedAgentsByFile.size, 0)
|
|
2104
|
-
})
|
|
2105
|
-
|
|
2106
|
-
test('the filtered map is a clone, not the original', () => {
|
|
2107
|
-
const state = createMockInspectorState()
|
|
2108
|
-
state.agents = {
|
|
2109
|
-
agentsMeta: {},
|
|
2110
|
-
files: new Map(),
|
|
2111
|
-
invokedAgentsByFile: new Map([
|
|
2112
|
-
['/test/project/src/api/users.ts', new Set(['houseAssistant'])],
|
|
2113
|
-
]),
|
|
2114
|
-
}
|
|
2115
|
-
const filtered = filterInspectorState(
|
|
2116
|
-
state,
|
|
2117
|
-
{ names: ['getUsers'] },
|
|
2118
|
-
mockLogger
|
|
2119
|
-
)
|
|
2120
|
-
filtered.agents.invokedAgentsByFile.clear()
|
|
2121
|
-
assert.strictEqual(state.agents.invokedAgentsByFile.size, 1)
|
|
2122
|
-
})
|
|
2123
|
-
|
|
2124
|
-
test('keeps the entry of a kept function file and prunes the rest', () => {
|
|
2125
|
-
const state = createMockInspectorState()
|
|
2126
|
-
state.agents = {
|
|
2127
|
-
agentsMeta: {},
|
|
2128
|
-
files: new Map(),
|
|
2129
|
-
invokedAgentsByFile: new Map([
|
|
2130
|
-
['/test/project/src/api/users.ts', new Set(['houseAssistant'])],
|
|
2131
|
-
['/test/project/src/admin/settings.ts', new Set(['adminAssistant'])],
|
|
2132
|
-
]),
|
|
2133
|
-
}
|
|
2134
|
-
const filtered = filterInspectorState(
|
|
2135
|
-
state,
|
|
2136
|
-
{ names: ['getUsers'] },
|
|
2137
|
-
mockLogger
|
|
2138
|
-
)
|
|
2139
|
-
assert.deepStrictEqual(
|
|
2140
|
-
[...filtered.agents.invokedAgentsByFile.keys()],
|
|
2141
|
-
['/test/project/src/api/users.ts']
|
|
2142
|
-
)
|
|
2143
|
-
})
|
|
2144
|
-
|
|
2145
|
-
describe('Wired addon retention', () => {
|
|
2146
|
-
// A unit that owns the generic `/rpc/:rpcName` catch-all (the rpcCaller
|
|
2147
|
-
// unit, and agent units) dispatches arbitrary RPCs at runtime via
|
|
2148
|
-
// `rpc.exposed(name)`, including namespaced addon RPCs like
|
|
2149
|
-
// `admin:createUser`. No app-local function statically references those
|
|
2150
|
-
// ids, so the catch-all must be enough to keep every wired addon's
|
|
2151
|
-
// declaration — otherwise the per-unit bootstrap omits the addon package
|
|
2152
|
-
// bootstrap and `rpc.exposed('admin:createUser')` throws RPCNotFoundError
|
|
2153
|
-
// on a deployed stage.
|
|
2154
|
-
const withAddons = () => {
|
|
2155
|
-
const state = createMockInspectorState()
|
|
2156
|
-
state.rpc.wireAddonDeclarations = new Map([
|
|
2157
|
-
['admin', { package: '@pikku/addon-admin' } as any],
|
|
2158
|
-
['console', { package: '@pikku/addon-console' } as any],
|
|
2159
|
-
])
|
|
2160
|
-
state.rpc.usedAddons = new Set(['admin', 'console'])
|
|
2161
|
-
return state
|
|
2162
|
-
}
|
|
2163
|
-
|
|
2164
|
-
test('keeps all wired addons for a unit with the /rpc/:rpcName catch-all', () => {
|
|
2165
|
-
const result = filterInspectorState(
|
|
2166
|
-
withAddons(),
|
|
2167
|
-
{ names: ['/rpc/:rpcName', 'http:post:/rpc/:rpcName', 'rpcCaller'] },
|
|
2168
|
-
mockLogger
|
|
2169
|
-
)
|
|
2170
|
-
assert.deepStrictEqual(
|
|
2171
|
-
[...result.rpc.wireAddonDeclarations.keys()].sort(),
|
|
2172
|
-
['admin', 'console']
|
|
2173
|
-
)
|
|
2174
|
-
assert.deepStrictEqual([...result.rpc.usedAddons].sort(), [
|
|
2175
|
-
'admin',
|
|
2176
|
-
'console',
|
|
2177
|
-
])
|
|
2178
|
-
})
|
|
2179
|
-
|
|
2180
|
-
test('drops wired addons for a unit without the catch-all that references no namespace', () => {
|
|
2181
|
-
const result = filterInspectorState(
|
|
2182
|
-
withAddons(),
|
|
2183
|
-
{ names: ['getUsers'] },
|
|
2184
|
-
mockLogger
|
|
2185
|
-
)
|
|
2186
|
-
assert.deepStrictEqual([...result.rpc.wireAddonDeclarations.keys()], [])
|
|
2187
|
-
assert.deepStrictEqual([...result.rpc.usedAddons], [])
|
|
2188
|
-
})
|
|
2189
|
-
|
|
2190
|
-
test('still keeps an addon a surviving function statically references, without the catch-all', () => {
|
|
2191
|
-
const state = withAddons()
|
|
2192
|
-
state.serviceAggregation.usedFunctions.add('getUsers')
|
|
2193
|
-
state.rpc.invokedFunctionsByFile = new Map([
|
|
2194
|
-
['/test/project/src/api/users.ts', new Set(['admin:createUser'])],
|
|
2195
|
-
])
|
|
2196
|
-
const result = filterInspectorState(
|
|
2197
|
-
state,
|
|
2198
|
-
{ names: ['getUsers'] },
|
|
2199
|
-
mockLogger
|
|
2200
|
-
)
|
|
2201
|
-
assert.deepStrictEqual(
|
|
2202
|
-
[...result.rpc.wireAddonDeclarations.keys()],
|
|
2203
|
-
['admin']
|
|
2204
|
-
)
|
|
2205
|
-
})
|
|
2206
|
-
})
|
|
2207
|
-
})
|