@pikku/inspector 0.12.75 → 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.
Files changed (219) hide show
  1. package/CHANGELOG.md +330 -0
  2. package/dist/add/add-analytics.d.ts +9 -0
  3. package/dist/add/add-analytics.js +178 -0
  4. package/dist/add/add-auth.js +0 -54
  5. package/dist/add/add-feature-flag.d.ts +2 -0
  6. package/dist/add/add-feature-flag.js +162 -0
  7. package/dist/add/add-wire-addon.js +1 -0
  8. package/dist/add/add-wire-remote-addon.js +1 -0
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.js +3 -1
  11. package/dist/inspector.js +4 -0
  12. package/dist/surface/collect-surface.d.ts +58 -0
  13. package/dist/surface/collect-surface.js +613 -0
  14. package/dist/types.d.ts +20 -11
  15. package/dist/utils/filter-inspector-state.js +14 -0
  16. package/dist/utils/post-process.js +15 -0
  17. package/dist/utils/read-module-specifiers.d.ts +8 -0
  18. package/dist/utils/read-module-specifiers.js +23 -0
  19. package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
  20. package/dist/utils/read-tsconfig-out-dir.js +12 -0
  21. package/dist/utils/resolve-deploy-target.d.ts +7 -0
  22. package/dist/utils/resolve-deploy-target.js +15 -7
  23. package/dist/utils/serialize-inspector-state.d.ts +5 -0
  24. package/dist/utils/serialize-inspector-state.js +8 -0
  25. package/dist/visit.js +4 -0
  26. package/package.json +16 -6
  27. package/run-tests.sh +21 -6
  28. package/src/add/add-addon-bans.ts +0 -85
  29. package/src/add/add-agent-scorer.test.ts +0 -197
  30. package/src/add/add-agent-scorer.ts +0 -83
  31. package/src/add/add-agent.test.ts +0 -87
  32. package/src/add/add-agent.ts +0 -638
  33. package/src/add/add-approval-description.ts +0 -76
  34. package/src/add/add-auth.test.ts +0 -791
  35. package/src/add/add-auth.ts +0 -424
  36. package/src/add/add-channel.ts +0 -778
  37. package/src/add/add-cli-renderers.test.ts +0 -74
  38. package/src/add/add-cli.ts +0 -1006
  39. package/src/add/add-credential.ts +0 -186
  40. package/src/add/add-feature.test.ts +0 -306
  41. package/src/add/add-feature.ts +0 -253
  42. package/src/add/add-file-extends-core-type.ts +0 -66
  43. package/src/add/add-file-with-config.ts +0 -45
  44. package/src/add/add-file-with-factory.test.ts +0 -134
  45. package/src/add/add-file-with-factory.ts +0 -288
  46. package/src/add/add-functions.test.ts +0 -743
  47. package/src/add/add-functions.ts +0 -1548
  48. package/src/add/add-gateway.ts +0 -143
  49. package/src/add/add-http-route-ref.test.ts +0 -149
  50. package/src/add/add-http-route.ts +0 -530
  51. package/src/add/add-http-routes.ts +0 -471
  52. package/src/add/add-keyed-wiring.ts +0 -180
  53. package/src/add/add-mcp-prompt.ts +0 -157
  54. package/src/add/add-mcp-resource.ts +0 -175
  55. package/src/add/add-middleware.test.ts +0 -196
  56. package/src/add/add-middleware.ts +0 -879
  57. package/src/add/add-permission-auth.test.ts +0 -59
  58. package/src/add/add-permission.ts +0 -311
  59. package/src/add/add-personas.test.ts +0 -406
  60. package/src/add/add-personas.ts +0 -287
  61. package/src/add/add-queue-worker.ts +0 -117
  62. package/src/add/add-rpc-invocations.test.ts +0 -287
  63. package/src/add/add-rpc-invocations.ts +0 -221
  64. package/src/add/add-scenario-step.test.ts +0 -509
  65. package/src/add/add-schedule.ts +0 -120
  66. package/src/add/add-scope.test.ts +0 -419
  67. package/src/add/add-scope.ts +0 -221
  68. package/src/add/add-secret.ts +0 -10
  69. package/src/add/add-subject-scenario-steps.test.ts +0 -161
  70. package/src/add/add-system-role.test.ts +0 -297
  71. package/src/add/add-system-role.ts +0 -162
  72. package/src/add/add-trigger.ts +0 -165
  73. package/src/add/add-variable.ts +0 -10
  74. package/src/add/add-wire-addon.test.ts +0 -185
  75. package/src/add/add-wire-addon.ts +0 -146
  76. package/src/add/add-wire-remote-addon.ts +0 -80
  77. package/src/add/add-workflow-edge-cases.test.ts +0 -201
  78. package/src/add/add-workflow-fanout.test.ts +0 -166
  79. package/src/add/add-workflow-graph-foreach.test.ts +0 -174
  80. package/src/add/add-workflow-graph-input.test.ts +0 -94
  81. package/src/add/add-workflow-graph-notes.test.ts +0 -74
  82. package/src/add/add-workflow-graph.ts +0 -644
  83. package/src/add/add-workflow-on-error.test.ts +0 -118
  84. package/src/add/add-workflow-return-switch.test.ts +0 -121
  85. package/src/add/add-workflow-silent-drops.test.ts +0 -261
  86. package/src/add/add-workflow-sleep-expression.test.ts +0 -96
  87. package/src/add/add-workflow.test.ts +0 -155
  88. package/src/add/add-workflow.ts +0 -482
  89. package/src/add/addon-bans.test.ts +0 -121
  90. package/src/add/addon-contracts.test.ts +0 -221
  91. package/src/add/allow-escape-hatches.test.ts +0 -144
  92. package/src/add/audit-meta.test.ts +0 -75
  93. package/src/add/classified-sink-check.test.ts +0 -347
  94. package/src/add/config-value-metadata.test.ts +0 -263
  95. package/src/add/dynamic-import-check.test.ts +0 -119
  96. package/src/add/expect-eventually-scenario-only.test.ts +0 -108
  97. package/src/add/function-body-span.test.ts +0 -96
  98. package/src/add/inline-wiring-function.test.ts +0 -104
  99. package/src/add/pii-check.test.ts +0 -258
  100. package/src/add/rpc-type-cast.test.ts +0 -123
  101. package/src/add/scenario-instrumentation.test.ts +0 -89
  102. package/src/add/scenario-instrumentation.ts +0 -42
  103. package/src/add/scenario-marker.test.ts +0 -89
  104. package/src/add/scenario-not-extractable.test.ts +0 -159
  105. package/src/add/scenario-services.test.ts +0 -103
  106. package/src/add/scenario-skip.test.ts +0 -83
  107. package/src/add/validate-workflow-graph-addons.test.ts +0 -102
  108. package/src/add/wire-name-literal.test.ts +0 -117
  109. package/src/error-codes-emitted.test.ts +0 -76
  110. package/src/error-codes.ts +0 -145
  111. package/src/index.ts +0 -66
  112. package/src/inspector.ts +0 -575
  113. package/src/schema-generator.ts +0 -1
  114. package/src/source-file-cache.ts +0 -73
  115. package/src/tsconfig-paths.test.ts +0 -90
  116. package/src/types-map.ts +0 -141
  117. package/src/types.ts +0 -775
  118. package/src/utils/annotate-http-route-auth.test.ts +0 -161
  119. package/src/utils/annotate-http-route-auth.ts +0 -53
  120. package/src/utils/check-classified-sinks.ts +0 -215
  121. package/src/utils/check-pii-output.ts +0 -120
  122. package/src/utils/compute-diagnostics.test.ts +0 -106
  123. package/src/utils/compute-required-schemas.ts +0 -48
  124. package/src/utils/contract-hashes.test.ts +0 -553
  125. package/src/utils/contract-hashes.ts +0 -387
  126. package/src/utils/custom-types-generator.test.ts +0 -124
  127. package/src/utils/custom-types-generator.ts +0 -150
  128. package/src/utils/detect-schema-vendor.test.ts +0 -123
  129. package/src/utils/detect-schema-vendor.ts +0 -120
  130. package/src/utils/does-type-extend-core-type.ts +0 -53
  131. package/src/utils/ensure-function-metadata.ts +0 -385
  132. package/src/utils/extract-function-name.ts +0 -612
  133. package/src/utils/extract-function-node.ts +0 -70
  134. package/src/utils/extract-node-value.test.ts +0 -117
  135. package/src/utils/extract-node-value.ts +0 -172
  136. package/src/utils/extract-secret-usage.test.ts +0 -81
  137. package/src/utils/extract-secret-usage.ts +0 -73
  138. package/src/utils/extract-services.ts +0 -70
  139. package/src/utils/extract-surface-usage.test.ts +0 -147
  140. package/src/utils/extract-surface-usage.ts +0 -121
  141. package/src/utils/filter-inspector-state.test.ts +0 -2232
  142. package/src/utils/filter-inspector-state.ts +0 -1298
  143. package/src/utils/filter-utils.test.ts +0 -843
  144. package/src/utils/filter-utils.ts +0 -161
  145. package/src/utils/find-root-dir.ts +0 -68
  146. package/src/utils/find-zod-transform.test.ts +0 -70
  147. package/src/utils/find-zod-transform.ts +0 -103
  148. package/src/utils/get-exported-variable-name.ts +0 -48
  149. package/src/utils/get-files-and-methods.ts +0 -157
  150. package/src/utils/get-property-value.test.ts +0 -141
  151. package/src/utils/get-property-value.ts +0 -331
  152. package/src/utils/hash.ts +0 -26
  153. package/src/utils/literal-properties.ts +0 -97
  154. package/src/utils/load-addon-functions-meta.test.ts +0 -157
  155. package/src/utils/load-addon-functions-meta.ts +0 -508
  156. package/src/utils/load-addon-scopes.test.ts +0 -138
  157. package/src/utils/middleware.test.ts +0 -204
  158. package/src/utils/middleware.ts +0 -303
  159. package/src/utils/nested-project-filter.test.ts +0 -53
  160. package/src/utils/nested-project-filter.ts +0 -28
  161. package/src/utils/permissions-shorthand.test.ts +0 -79
  162. package/src/utils/permissions.test.ts +0 -125
  163. package/src/utils/permissions.ts +0 -129
  164. package/src/utils/post-process.test.ts +0 -1094
  165. package/src/utils/post-process.ts +0 -1604
  166. package/src/utils/resolve-addon-package.ts +0 -106
  167. package/src/utils/resolve-core-type.test.ts +0 -142
  168. package/src/utils/resolve-core-type.ts +0 -35
  169. package/src/utils/resolve-deploy-target.test.ts +0 -135
  170. package/src/utils/resolve-deploy-target.ts +0 -65
  171. package/src/utils/resolve-function-meta.ts +0 -25
  172. package/src/utils/resolve-function-types.ts +0 -42
  173. package/src/utils/resolve-identifier.ts +0 -46
  174. package/src/utils/resolve-ref-contract.ts +0 -71
  175. package/src/utils/resolve-versions.test.ts +0 -499
  176. package/src/utils/resolve-versions.ts +0 -228
  177. package/src/utils/schema-cache.test.ts +0 -124
  178. package/src/utils/schema-generator.test.ts +0 -272
  179. package/src/utils/schema-generator.ts +0 -780
  180. package/src/utils/secret-alias-services.test.ts +0 -146
  181. package/src/utils/secret-alias-services.ts +0 -58
  182. package/src/utils/serialize-inspector-state.ts +0 -742
  183. package/src/utils/serialize-mcp-json.ts +0 -150
  184. package/src/utils/serialize-middleware-groups-meta.ts +0 -36
  185. package/src/utils/serialize-openapi-json.ts +0 -277
  186. package/src/utils/serialize-permissions-groups-meta.ts +0 -7
  187. package/src/utils/single-declaration.ts +0 -57
  188. package/src/utils/type-utils.ts +0 -145
  189. package/src/utils/validate-auth-sessionless.ts +0 -30
  190. package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
  191. package/src/utils/validate-exposed-functions-gated.ts +0 -103
  192. package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
  193. package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
  194. package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
  195. package/src/utils/workflow/derive-workflow-plan.ts +0 -117
  196. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
  197. package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
  198. package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
  199. package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
  200. package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
  201. package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
  202. package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
  203. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
  204. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
  205. package/src/utils/workflow/dsl/index.ts +0 -11
  206. package/src/utils/workflow/dsl/patterns.ts +0 -440
  207. package/src/utils/workflow/dsl/validation.ts +0 -203
  208. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
  209. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
  210. package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
  211. package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
  212. package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
  213. package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
  214. package/src/utils/workflow/graph/index.ts +0 -28
  215. package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
  216. package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
  217. package/src/visit.ts +0 -181
  218. package/tsconfig.json +0 -19
  219. package/tsconfig.tsbuildinfo +0 -1
@@ -1,2232 +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
- const withAddons = () => {
2147
- const state = createMockInspectorState()
2148
- state.rpc.wireAddonDeclarations = new Map([
2149
- ['admin', { package: '@pikku/addon-admin' } as any],
2150
- ['console', { package: '@pikku/addon-console' } as any],
2151
- ])
2152
- state.rpc.usedAddons = new Set(['admin', 'console'])
2153
- return state
2154
- }
2155
-
2156
- test('keeps only the addon whose function the unit is built for', () => {
2157
- const result = filterInspectorState(
2158
- withAddons(),
2159
- {
2160
- names: [
2161
- 'console:runSecurityAudit',
2162
- '/rpc/:rpcName',
2163
- 'http:post:/rpc/:rpcName',
2164
- 'rpcCaller',
2165
- ],
2166
- },
2167
- mockLogger
2168
- )
2169
- assert.deepStrictEqual(
2170
- [...result.rpc.wireAddonDeclarations.keys()],
2171
- ['console']
2172
- )
2173
- assert.deepStrictEqual([...result.rpc.usedAddons], ['console'])
2174
- })
2175
-
2176
- test('drops every wired addon for a unit serving the catch-all', () => {
2177
- const result = filterInspectorState(
2178
- withAddons(),
2179
- {
2180
- names: ['/rpc/:rpcName', 'http:post:/rpc/:rpcName', 'rpcCaller'],
2181
- },
2182
- mockLogger
2183
- )
2184
- assert.deepStrictEqual([...result.rpc.wireAddonDeclarations.keys()], [])
2185
- assert.deepStrictEqual([...result.rpc.usedAddons], [])
2186
- })
2187
-
2188
- test('drops wired addons for a unit given the scaffold names', () => {
2189
- const result = filterInspectorState(
2190
- withAddons(),
2191
- {
2192
- names: [
2193
- 'addRetreatPerson',
2194
- '/rpc/:rpcName',
2195
- 'http:post:/rpc/:rpcName',
2196
- 'rpcCaller',
2197
- ],
2198
- },
2199
- mockLogger
2200
- )
2201
- assert.deepStrictEqual([...result.rpc.wireAddonDeclarations.keys()], [])
2202
- assert.deepStrictEqual([...result.rpc.usedAddons], [])
2203
- })
2204
-
2205
- test('drops wired addons for a unit without the catch-all that references no namespace', () => {
2206
- const result = filterInspectorState(
2207
- withAddons(),
2208
- { names: ['getUsers'] },
2209
- mockLogger
2210
- )
2211
- assert.deepStrictEqual([...result.rpc.wireAddonDeclarations.keys()], [])
2212
- assert.deepStrictEqual([...result.rpc.usedAddons], [])
2213
- })
2214
-
2215
- test('still keeps an addon a surviving function statically references, without the catch-all', () => {
2216
- const state = withAddons()
2217
- state.serviceAggregation.usedFunctions.add('getUsers')
2218
- state.rpc.invokedFunctionsByFile = new Map([
2219
- ['/test/project/src/api/users.ts', new Set(['admin:createUser'])],
2220
- ])
2221
- const result = filterInspectorState(
2222
- state,
2223
- { names: ['getUsers'] },
2224
- mockLogger
2225
- )
2226
- assert.deepStrictEqual(
2227
- [...result.rpc.wireAddonDeclarations.keys()],
2228
- ['admin']
2229
- )
2230
- })
2231
- })
2232
- })