@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,1302 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
InspectorState,
|
|
3
|
-
InspectorFilters,
|
|
4
|
-
InspectorLogger,
|
|
5
|
-
} from '../types.js'
|
|
6
|
-
import type { PikkuWiringTypes } from '@pikku/core/types'
|
|
7
|
-
import { parseVersionedId } from '@pikku/core/utils'
|
|
8
|
-
import { makeContextBasedId } from './extract-function-name.js'
|
|
9
|
-
import { aggregateRequiredServices } from './post-process.js'
|
|
10
|
-
import {
|
|
11
|
-
IncompatibleDeployTargetError,
|
|
12
|
-
resolveDeployTarget,
|
|
13
|
-
} from './resolve-deploy-target.js'
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* The deploy filter, asked about one function at a time rather than handed over
|
|
17
|
-
* as a set, so that a function's own deploy conflict is raised only if this app
|
|
18
|
-
* turns out to wire it.
|
|
19
|
-
*/
|
|
20
|
-
type DeployFilter = {
|
|
21
|
-
has(pikkuFuncId: string): boolean
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Drop a workflow from every map that describes it. `files` and `graphFiles`
|
|
26
|
-
* are what the wiring generator iterates, and `graphMeta` is what tells a
|
|
27
|
-
* scenario apart from an app workflow — leaving a pruned workflow's file entry
|
|
28
|
-
* behind registers it anyway, and a scenario whose graphMeta has gone reads as
|
|
29
|
-
* an app workflow, which is how scenario steps reached deployed bundles.
|
|
30
|
-
*/
|
|
31
|
-
const pruneWorkflow = (
|
|
32
|
-
workflows: InspectorState['workflows'],
|
|
33
|
-
name: string,
|
|
34
|
-
pikkuFuncId: string | undefined
|
|
35
|
-
) => {
|
|
36
|
-
delete workflows.graphMeta[name]
|
|
37
|
-
delete workflows.meta[name]
|
|
38
|
-
workflows.files.delete(name)
|
|
39
|
-
workflows.graphFiles.delete(name)
|
|
40
|
-
if (pikkuFuncId) {
|
|
41
|
-
workflows.files.delete(pikkuFuncId)
|
|
42
|
-
workflows.graphFiles.delete(pikkuFuncId)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Module-level Set to track warned groups across multiple filter calls
|
|
47
|
-
const globalWarnedGroups = new Set<string>()
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Match a value against a pattern with wildcard support
|
|
51
|
-
* Supports "*" at the beginning, end, or both (e.g., "send*", "*Payment", "*process*")
|
|
52
|
-
*/
|
|
53
|
-
function matchesWildcard(value: string, pattern: string): boolean {
|
|
54
|
-
if (pattern === '*') return true
|
|
55
|
-
|
|
56
|
-
const startsWithWildcard = pattern.startsWith('*')
|
|
57
|
-
const endsWithWildcard = pattern.endsWith('*')
|
|
58
|
-
|
|
59
|
-
if (startsWithWildcard && endsWithWildcard) {
|
|
60
|
-
const middle = pattern.slice(1, -1)
|
|
61
|
-
if (middle === '') return true
|
|
62
|
-
return value.includes(middle)
|
|
63
|
-
} else if (startsWithWildcard) {
|
|
64
|
-
const suffix = pattern.slice(1)
|
|
65
|
-
return value.endsWith(suffix) && value.length > suffix.length
|
|
66
|
-
} else if (endsWithWildcard) {
|
|
67
|
-
const prefix = pattern.slice(0, -1)
|
|
68
|
-
return value.startsWith(prefix) && value.length > prefix.length
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return value === pattern
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function collectSourceFiles(
|
|
75
|
-
value: unknown,
|
|
76
|
-
sourceFiles = new Set<string>(),
|
|
77
|
-
seen = new Set<object>()
|
|
78
|
-
): Set<string> {
|
|
79
|
-
if (typeof value === 'string') {
|
|
80
|
-
return sourceFiles
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (Array.isArray(value)) {
|
|
84
|
-
for (const entry of value) {
|
|
85
|
-
collectSourceFiles(entry, sourceFiles, seen)
|
|
86
|
-
}
|
|
87
|
-
return sourceFiles
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!value || typeof value !== 'object') {
|
|
91
|
-
return sourceFiles
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (seen.has(value)) {
|
|
95
|
-
return sourceFiles
|
|
96
|
-
}
|
|
97
|
-
seen.add(value)
|
|
98
|
-
|
|
99
|
-
if ('sourceFile' in value && typeof value.sourceFile === 'string') {
|
|
100
|
-
sourceFiles.add(value.sourceFile)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
for (const nestedValue of Object.values(value)) {
|
|
104
|
-
collectSourceFiles(nestedValue, sourceFiles, seen)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return sourceFiles
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function repopulateFileSetFromMeta(
|
|
111
|
-
meta: unknown,
|
|
112
|
-
fallbackFiles: Set<string>,
|
|
113
|
-
hasEntries: boolean
|
|
114
|
-
): Set<string> {
|
|
115
|
-
const sourceFiles = collectSourceFiles(meta)
|
|
116
|
-
if (sourceFiles.size > 0) {
|
|
117
|
-
return sourceFiles
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (hasEntries) {
|
|
121
|
-
return new Set(fallbackFiles)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return new Set<string>()
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Check if metadata matches the given filters
|
|
129
|
-
*/
|
|
130
|
-
function matchesFilters(
|
|
131
|
-
filters: InspectorFilters,
|
|
132
|
-
meta: {
|
|
133
|
-
type: PikkuWiringTypes
|
|
134
|
-
name: string
|
|
135
|
-
/**
|
|
136
|
-
* Further ids the `--names` filter may address this wiring by. An HTTP
|
|
137
|
-
* route wired with `ref('ns:fn')` carries the addon's own function id as
|
|
138
|
-
* its name, so its `http:<method>:<route>` wiring id is the only id that
|
|
139
|
-
* names this route rather than every wiring onto that addon function.
|
|
140
|
-
*/
|
|
141
|
-
altNames?: string[]
|
|
142
|
-
tags?: string[]
|
|
143
|
-
filePath?: string
|
|
144
|
-
httpRoute?: string
|
|
145
|
-
httpMethod?: string
|
|
146
|
-
groupBasePath?: string
|
|
147
|
-
},
|
|
148
|
-
logger: InspectorLogger,
|
|
149
|
-
warnedGroups?: Set<string>,
|
|
150
|
-
// Kept pikkuFuncIds under the deploy filter; null when the filter is
|
|
151
|
-
// inactive. Asked about one id at a time, which is what lets a conflict in an
|
|
152
|
-
// addon's own metadata surface only for a function this app actually wires.
|
|
153
|
-
keptByDeploy?: DeployFilter | null
|
|
154
|
-
): boolean {
|
|
155
|
-
// If no filters, allow everything
|
|
156
|
-
if (Object.keys(filters).length === 0) return true
|
|
157
|
-
|
|
158
|
-
// If all filter arrays are empty, allow everything
|
|
159
|
-
if (
|
|
160
|
-
(!filters.names || filters.names.length === 0) &&
|
|
161
|
-
(!filters.tags || filters.tags.length === 0) &&
|
|
162
|
-
(!filters.wires || filters.wires.length === 0) &&
|
|
163
|
-
(!filters.excludeWires || filters.excludeWires.length === 0) &&
|
|
164
|
-
(!filters.directories || filters.directories.length === 0) &&
|
|
165
|
-
(!filters.httpRoutes || filters.httpRoutes.length === 0) &&
|
|
166
|
-
(!filters.httpMethods || filters.httpMethods.length === 0) &&
|
|
167
|
-
(!filters.target || filters.target.length === 0) &&
|
|
168
|
-
(!filters.excludeNames || filters.excludeNames.length === 0) &&
|
|
169
|
-
(!filters.excludeTags || filters.excludeTags.length === 0) &&
|
|
170
|
-
(!filters.excludeWires || filters.excludeWires.length === 0) &&
|
|
171
|
-
(!filters.excludeDirectories || filters.excludeDirectories.length === 0) &&
|
|
172
|
-
(!filters.excludeHttpRoutes || filters.excludeHttpRoutes.length === 0) &&
|
|
173
|
-
(!filters.excludeHttpMethods || filters.excludeHttpMethods.length === 0) &&
|
|
174
|
-
(!filters.excludeTarget || filters.excludeTarget.length === 0)
|
|
175
|
-
) {
|
|
176
|
-
return true
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Deploy-target include filter (computed once per filterInspectorState call).
|
|
180
|
-
if (keptByDeploy && !keptByDeploy.has(meta.name)) {
|
|
181
|
-
logger.debug(`⒡ Filtered by deploy include: ${meta.type}:${meta.name}`)
|
|
182
|
-
return false
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Check wire include filter
|
|
186
|
-
if (filters.wires && filters.wires.length > 0) {
|
|
187
|
-
if (!filters.wires.includes(meta.type)) {
|
|
188
|
-
logger.debug(`⒡ Filtered by wire include: ${meta.type}:${meta.name}`)
|
|
189
|
-
return false
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Check wire exclude filter
|
|
194
|
-
if (filters.excludeWires && filters.excludeWires.length > 0) {
|
|
195
|
-
if (filters.excludeWires.includes(meta.type)) {
|
|
196
|
-
logger.debug(`⒡ Filtered by wire exclude: ${meta.type}:${meta.name}`)
|
|
197
|
-
return false
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Check directory filter
|
|
202
|
-
if (filters.directories && filters.directories.length > 0 && meta.filePath) {
|
|
203
|
-
const matchesDirectory = filters.directories.some((dir) => {
|
|
204
|
-
const normalizedFilePath = meta.filePath!.replace(/\\/g, '/')
|
|
205
|
-
const normalizedDir = dir.replace(/\\/g, '/')
|
|
206
|
-
return normalizedFilePath.includes(normalizedDir)
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
if (!matchesDirectory) {
|
|
210
|
-
logger.debug(`⒡ Filtered by directory: ${meta.type}:${meta.name}`)
|
|
211
|
-
return false
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Check tag include filter
|
|
216
|
-
if (filters.tags && filters.tags.length > 0) {
|
|
217
|
-
if (!meta.tags || !filters.tags.some((tag) => meta.tags!.includes(tag))) {
|
|
218
|
-
logger.debug(`⒡ Filtered by tags include: ${meta.type}:${meta.name}`)
|
|
219
|
-
return false
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Check tag exclude filter
|
|
224
|
-
if (filters.excludeTags && filters.excludeTags.length > 0) {
|
|
225
|
-
if (
|
|
226
|
-
meta.tags &&
|
|
227
|
-
filters.excludeTags.some((tag) => meta.tags!.includes(tag))
|
|
228
|
-
) {
|
|
229
|
-
logger.debug(`⒡ Filtered by tags exclude: ${meta.type}:${meta.name}`)
|
|
230
|
-
return false
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const candidateNames = [meta.name, ...(meta.altNames ?? [])]
|
|
235
|
-
const matchesNamePattern = (pattern: string) =>
|
|
236
|
-
candidateNames.some((candidate) => {
|
|
237
|
-
const { baseName } = parseVersionedId(candidate)
|
|
238
|
-
return (
|
|
239
|
-
matchesWildcard(candidate, pattern) ||
|
|
240
|
-
(baseName !== candidate && matchesWildcard(baseName, pattern))
|
|
241
|
-
)
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
// Check name include filter (match against both full ID and base name for versioned functions)
|
|
245
|
-
if (filters.names && filters.names.length > 0) {
|
|
246
|
-
const nameMatches = filters.names.some(matchesNamePattern)
|
|
247
|
-
if (!nameMatches) {
|
|
248
|
-
logger.debug(`⒡ Filtered by name include: ${meta.type}:${meta.name}`)
|
|
249
|
-
return false
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Check name exclude filter
|
|
254
|
-
if (filters.excludeNames && filters.excludeNames.length > 0) {
|
|
255
|
-
if (filters.excludeNames.some(matchesNamePattern)) {
|
|
256
|
-
logger.debug(`⒡ Filtered by name exclude: ${meta.type}:${meta.name}`)
|
|
257
|
-
return false
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const matchesRoutePattern = (pattern: string) =>
|
|
262
|
-
!!meta.httpRoute && matchesWildcard(meta.httpRoute, pattern)
|
|
263
|
-
|
|
264
|
-
// Check HTTP route include filter
|
|
265
|
-
if (filters.httpRoutes && filters.httpRoutes.length > 0 && meta.httpRoute) {
|
|
266
|
-
const routeMatches = filters.httpRoutes.some(matchesRoutePattern)
|
|
267
|
-
if (!routeMatches) {
|
|
268
|
-
logger.debug(`⒡ Filtered by HTTP route include: ${meta.httpRoute}`)
|
|
269
|
-
return false
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// If route is part of a wireHTTPRoutes group, check if filter is at group level
|
|
273
|
-
if (meta.groupBasePath && warnedGroups) {
|
|
274
|
-
const groupMatches = filters.httpRoutes.some(
|
|
275
|
-
(pattern) =>
|
|
276
|
-
matchesWildcard(meta.groupBasePath!, pattern) ||
|
|
277
|
-
matchesWildcard(meta.groupBasePath! + '/*', pattern)
|
|
278
|
-
)
|
|
279
|
-
if (!groupMatches && !warnedGroups.has(meta.groupBasePath)) {
|
|
280
|
-
warnedGroups.add(meta.groupBasePath)
|
|
281
|
-
logger.warn(
|
|
282
|
-
`Filtering within wireHTTPRoutes group is not yet supported. ` +
|
|
283
|
-
`Route '${meta.httpRoute}' is part of group '${meta.groupBasePath}'. ` +
|
|
284
|
-
`Use '--httpRoutes=${meta.groupBasePath}/*' to filter the entire group.`
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Check HTTP route exclude filter
|
|
291
|
-
if (
|
|
292
|
-
filters.excludeHttpRoutes &&
|
|
293
|
-
filters.excludeHttpRoutes.length > 0 &&
|
|
294
|
-
meta.httpRoute
|
|
295
|
-
) {
|
|
296
|
-
if (filters.excludeHttpRoutes.some(matchesRoutePattern)) {
|
|
297
|
-
logger.debug(`⒡ Filtered by HTTP route exclude: ${meta.httpRoute}`)
|
|
298
|
-
return false
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const normalizedMethod = meta.httpMethod?.toUpperCase()
|
|
303
|
-
|
|
304
|
-
// Check HTTP method include filter
|
|
305
|
-
if (
|
|
306
|
-
filters.httpMethods &&
|
|
307
|
-
filters.httpMethods.length > 0 &&
|
|
308
|
-
normalizedMethod
|
|
309
|
-
) {
|
|
310
|
-
if (!filters.httpMethods.includes(normalizedMethod)) {
|
|
311
|
-
logger.debug(`⒡ Filtered by HTTP method include: ${meta.httpMethod}`)
|
|
312
|
-
return false
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Check HTTP method exclude filter
|
|
317
|
-
if (
|
|
318
|
-
filters.excludeHttpMethods &&
|
|
319
|
-
filters.excludeHttpMethods.length > 0 &&
|
|
320
|
-
normalizedMethod
|
|
321
|
-
) {
|
|
322
|
-
if (filters.excludeHttpMethods.includes(normalizedMethod)) {
|
|
323
|
-
logger.debug(`⒡ Filtered by HTTP method exclude: ${meta.httpMethod}`)
|
|
324
|
-
return false
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return true
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Extract wire names from middleware/permissions metadata
|
|
333
|
-
*/
|
|
334
|
-
function extractWireNames(obj: Record<string, any> | undefined): string[] {
|
|
335
|
-
if (!obj) return []
|
|
336
|
-
const names: string[] = []
|
|
337
|
-
for (const key of Object.keys(obj)) {
|
|
338
|
-
if (obj[key] && typeof obj[key] === 'object' && 'name' in obj[key]) {
|
|
339
|
-
names.push(obj[key].name)
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return names
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Filters inspector state based on provided filters
|
|
347
|
-
* This is applied post-inspection to support the inspect-once, filter-many pattern
|
|
348
|
-
*/
|
|
349
|
-
export function filterInspectorState(
|
|
350
|
-
state: InspectorState | Omit<InspectorState, 'typesLookup'>,
|
|
351
|
-
filters: InspectorFilters,
|
|
352
|
-
logger: InspectorLogger
|
|
353
|
-
): typeof state {
|
|
354
|
-
// If no filters, return original state
|
|
355
|
-
if (
|
|
356
|
-
Object.keys(filters).length === 0 ||
|
|
357
|
-
((!filters.names || filters.names.length === 0) &&
|
|
358
|
-
(!filters.tags || filters.tags.length === 0) &&
|
|
359
|
-
(!filters.wires || filters.wires.length === 0) &&
|
|
360
|
-
(!filters.excludeWires || filters.excludeWires.length === 0) &&
|
|
361
|
-
(!filters.directories || filters.directories.length === 0) &&
|
|
362
|
-
(!filters.httpRoutes || filters.httpRoutes.length === 0) &&
|
|
363
|
-
(!filters.httpMethods || filters.httpMethods.length === 0) &&
|
|
364
|
-
(!filters.target || filters.target.length === 0) &&
|
|
365
|
-
(!filters.excludeNames || filters.excludeNames.length === 0) &&
|
|
366
|
-
(!filters.excludeTags || filters.excludeTags.length === 0) &&
|
|
367
|
-
(!filters.excludeWires || filters.excludeWires.length === 0) &&
|
|
368
|
-
(!filters.excludeDirectories ||
|
|
369
|
-
filters.excludeDirectories.length === 0) &&
|
|
370
|
-
(!filters.excludeHttpRoutes || filters.excludeHttpRoutes.length === 0) &&
|
|
371
|
-
(!filters.excludeHttpMethods ||
|
|
372
|
-
filters.excludeHttpMethods.length === 0) &&
|
|
373
|
-
(!filters.excludeTarget || filters.excludeTarget.length === 0))
|
|
374
|
-
) {
|
|
375
|
-
return state
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Precompute kept-function set for the deploy filter (if active).
|
|
379
|
-
// resolveDeployTarget throws IncompatibleDeployTargetError when an
|
|
380
|
-
// explicit deploy: 'serverless' clashes with serverlessIncompatible.
|
|
381
|
-
let keptByDeploy: DeployFilter | null = null
|
|
382
|
-
if (
|
|
383
|
-
(filters.target && filters.target.length > 0) ||
|
|
384
|
-
(filters.excludeTarget && filters.excludeTarget.length > 0)
|
|
385
|
-
) {
|
|
386
|
-
const allowed = filters.target ? new Set(filters.target) : null
|
|
387
|
-
const excluded = filters.excludeTarget
|
|
388
|
-
? new Set(filters.excludeTarget)
|
|
389
|
-
: null
|
|
390
|
-
const incompatible = new Set(filters.serverlessIncompatible ?? [])
|
|
391
|
-
const defaultTarget = filters.defaultTarget ?? 'serverless'
|
|
392
|
-
const kept = new Set<string>()
|
|
393
|
-
// An addon publishes metadata for everything it exports, so a conflict in a
|
|
394
|
-
// function this app never wires is not this app's build error. Holding it
|
|
395
|
-
// here and raising it from `has` reports it at the one place the app is
|
|
396
|
-
// known to depend on that function.
|
|
397
|
-
const conflicts = new Map<string, IncompatibleDeployTargetError>()
|
|
398
|
-
keptByDeploy = {
|
|
399
|
-
has: (funcId: string) => {
|
|
400
|
-
const conflict = conflicts.get(funcId)
|
|
401
|
-
if (conflict) throw conflict
|
|
402
|
-
return kept.has(funcId)
|
|
403
|
-
},
|
|
404
|
-
}
|
|
405
|
-
const keepByTarget = (
|
|
406
|
-
funcId: string,
|
|
407
|
-
funcMeta: unknown,
|
|
408
|
-
incompatibleServices: Set<string>
|
|
409
|
-
) => {
|
|
410
|
-
const target = resolveDeployTarget(
|
|
411
|
-
funcMeta as any,
|
|
412
|
-
incompatibleServices,
|
|
413
|
-
funcId,
|
|
414
|
-
defaultTarget
|
|
415
|
-
)
|
|
416
|
-
if (allowed && !allowed.has(target)) return
|
|
417
|
-
if (excluded && excluded.has(target)) return
|
|
418
|
-
kept.add(funcId)
|
|
419
|
-
}
|
|
420
|
-
for (const [funcId, funcMeta] of Object.entries(state.functions.meta)) {
|
|
421
|
-
keepByTarget(funcId, funcMeta, incompatible)
|
|
422
|
-
}
|
|
423
|
-
// A wiring onto an addon function records the addon's own id, so the
|
|
424
|
-
// addon's published metadata is the only place its deploy target can be
|
|
425
|
-
// read from. Its serverlessIncompatible services are namespace-scoped —
|
|
426
|
-
// a service name means whatever the addon that declared it means.
|
|
427
|
-
for (const [namespace, addonMeta] of Object.entries(
|
|
428
|
-
state.addonFunctions ?? {}
|
|
429
|
-
)) {
|
|
430
|
-
const addonIncompatible = new Set(
|
|
431
|
-
state.addonServerlessIncompatible?.get(namespace) ?? []
|
|
432
|
-
)
|
|
433
|
-
for (const [funcName, funcMeta] of Object.entries(addonMeta)) {
|
|
434
|
-
const funcId = `${namespace}:${funcName}`
|
|
435
|
-
try {
|
|
436
|
-
keepByTarget(funcId, funcMeta, addonIncompatible)
|
|
437
|
-
} catch (e) {
|
|
438
|
-
if (!(e instanceof IncompatibleDeployTargetError)) throw e
|
|
439
|
-
conflicts.set(funcId, e)
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Snapshot the original workflow graph meta before filtering prunes it
|
|
446
|
-
const originalGraphMeta = {
|
|
447
|
-
...((state as InspectorState).workflows?.graphMeta ?? {}),
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Create a shallow copy with new Maps/Sets to avoid mutating the original
|
|
451
|
-
const filteredState = {
|
|
452
|
-
...state,
|
|
453
|
-
serviceAggregation: {
|
|
454
|
-
...state.serviceAggregation,
|
|
455
|
-
requiredServices: new Set<string>(), // Reset requiredServices - will be recalculated
|
|
456
|
-
usedFunctions: new Set<string>(),
|
|
457
|
-
usedMiddleware: new Set<string>(),
|
|
458
|
-
usedPermissions: new Set<string>(),
|
|
459
|
-
},
|
|
460
|
-
functions: {
|
|
461
|
-
...state.functions,
|
|
462
|
-
meta: JSON.parse(JSON.stringify(state.functions.meta)), // Deep clone to avoid mutating original
|
|
463
|
-
files: new Map(state.functions.files),
|
|
464
|
-
},
|
|
465
|
-
http: {
|
|
466
|
-
...state.http,
|
|
467
|
-
meta: JSON.parse(JSON.stringify(state.http.meta)), // Deep clone metadata
|
|
468
|
-
files: new Set<string>(), // Will be repopulated with filtered files
|
|
469
|
-
},
|
|
470
|
-
workflows: {
|
|
471
|
-
...state.workflows,
|
|
472
|
-
graphMeta: JSON.parse(JSON.stringify(state.workflows?.graphMeta ?? {})),
|
|
473
|
-
meta: JSON.parse(JSON.stringify(state.workflows?.meta ?? {})),
|
|
474
|
-
files: new Map(state.workflows?.files ?? []),
|
|
475
|
-
graphFiles: new Map(state.workflows?.graphFiles ?? []),
|
|
476
|
-
},
|
|
477
|
-
channels: {
|
|
478
|
-
...state.channels,
|
|
479
|
-
meta: JSON.parse(JSON.stringify(state.channels.meta)),
|
|
480
|
-
files: new Set<string>(), // Will be repopulated with filtered files
|
|
481
|
-
},
|
|
482
|
-
triggers: {
|
|
483
|
-
...state.triggers,
|
|
484
|
-
meta: JSON.parse(JSON.stringify(state.triggers?.meta ?? {})),
|
|
485
|
-
files: new Set<string>(),
|
|
486
|
-
},
|
|
487
|
-
scheduledTasks: {
|
|
488
|
-
...state.scheduledTasks,
|
|
489
|
-
meta: JSON.parse(JSON.stringify(state.scheduledTasks.meta)),
|
|
490
|
-
files: new Set<string>(), // Will be repopulated with filtered files
|
|
491
|
-
},
|
|
492
|
-
queueWorkers: {
|
|
493
|
-
...state.queueWorkers,
|
|
494
|
-
meta: JSON.parse(JSON.stringify(state.queueWorkers.meta)),
|
|
495
|
-
files: new Set<string>(), // Will be repopulated with filtered files
|
|
496
|
-
},
|
|
497
|
-
mcpEndpoints: {
|
|
498
|
-
...state.mcpEndpoints,
|
|
499
|
-
toolsMeta: JSON.parse(JSON.stringify(state.mcpEndpoints.toolsMeta)),
|
|
500
|
-
resourcesMeta: JSON.parse(
|
|
501
|
-
JSON.stringify(state.mcpEndpoints.resourcesMeta)
|
|
502
|
-
),
|
|
503
|
-
promptsMeta: JSON.parse(JSON.stringify(state.mcpEndpoints.promptsMeta)),
|
|
504
|
-
files: new Set<string>(),
|
|
505
|
-
},
|
|
506
|
-
agents: {
|
|
507
|
-
...state.agents,
|
|
508
|
-
agentsMeta: JSON.parse(JSON.stringify(state.agents?.agentsMeta ?? {})),
|
|
509
|
-
files: new Map(),
|
|
510
|
-
invokedAgentsByFile: new Map(
|
|
511
|
-
[
|
|
512
|
-
...(state.agents?.invokedAgentsByFile ??
|
|
513
|
-
new Map<string, Set<string>>()),
|
|
514
|
-
].map(([file, agents]): [string, Set<string>] => [
|
|
515
|
-
file,
|
|
516
|
-
new Set(agents),
|
|
517
|
-
])
|
|
518
|
-
),
|
|
519
|
-
},
|
|
520
|
-
// Scorers carry through every filter. They are named by the agents that
|
|
521
|
-
// survive it, and a deployment that quietly stopped grading would look
|
|
522
|
-
// exactly like one whose scorers all returned nothing.
|
|
523
|
-
scorers: {
|
|
524
|
-
...state.scorers,
|
|
525
|
-
scorersMeta: JSON.parse(JSON.stringify(state.scorers?.scorersMeta ?? {})),
|
|
526
|
-
files: new Map(state.scorers?.files ?? []),
|
|
527
|
-
},
|
|
528
|
-
rpc: {
|
|
529
|
-
...state.rpc,
|
|
530
|
-
internalMeta: { ...state.rpc.internalMeta }, // Clone to avoid mutating original
|
|
531
|
-
internalFiles: new Map(state.rpc.internalFiles),
|
|
532
|
-
exposedMeta: { ...state.rpc.exposedMeta },
|
|
533
|
-
exposedFiles: new Map(state.rpc.exposedFiles),
|
|
534
|
-
remoteMeta: { ...state.rpc.remoteMeta },
|
|
535
|
-
remoteFiles: new Map(state.rpc.remoteFiles),
|
|
536
|
-
invokedFunctions: new Set(state.rpc.invokedFunctions),
|
|
537
|
-
},
|
|
538
|
-
cli: {
|
|
539
|
-
...state.cli,
|
|
540
|
-
meta: JSON.parse(JSON.stringify(state.cli.meta)),
|
|
541
|
-
files: new Set<string>(), // Will be repopulated with filtered files
|
|
542
|
-
},
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Filter HTTP routes
|
|
546
|
-
for (const method of Object.keys(filteredState.http.meta)) {
|
|
547
|
-
const routes = filteredState.http.meta[method]
|
|
548
|
-
for (const route of Object.keys(routes)) {
|
|
549
|
-
const routeMeta = routes[route]
|
|
550
|
-
|
|
551
|
-
// Get function file path for directory filtering
|
|
552
|
-
const funcFile = filteredState.functions.files.get(routeMeta.pikkuFuncId)
|
|
553
|
-
const filePath = funcFile?.path
|
|
554
|
-
|
|
555
|
-
const matches = matchesFilters(
|
|
556
|
-
filters,
|
|
557
|
-
{
|
|
558
|
-
type: 'http' as PikkuWiringTypes,
|
|
559
|
-
name: routeMeta.pikkuFuncId, // Use function name, not route
|
|
560
|
-
altNames: [makeContextBasedId('http', method, routeMeta.route)],
|
|
561
|
-
tags: routeMeta.tags,
|
|
562
|
-
filePath,
|
|
563
|
-
httpRoute: routeMeta.route,
|
|
564
|
-
httpMethod: routeMeta.method,
|
|
565
|
-
groupBasePath: routeMeta.groupBasePath,
|
|
566
|
-
},
|
|
567
|
-
logger,
|
|
568
|
-
globalWarnedGroups,
|
|
569
|
-
keptByDeploy
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
if (!matches) {
|
|
573
|
-
delete routes[route]
|
|
574
|
-
} else {
|
|
575
|
-
// Track used functions/middleware/permissions
|
|
576
|
-
if (routeMeta.pikkuFuncId) {
|
|
577
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
578
|
-
routeMeta.pikkuFuncId
|
|
579
|
-
)
|
|
580
|
-
// For workflow/agent routes, also add the base name
|
|
581
|
-
// so the workflow/agent definition survives pruning
|
|
582
|
-
const colonIdx = routeMeta.pikkuFuncId.indexOf(':')
|
|
583
|
-
if (colonIdx !== -1) {
|
|
584
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
585
|
-
routeMeta.pikkuFuncId.slice(colonIdx + 1)
|
|
586
|
-
)
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
extractWireNames(routeMeta.middleware).forEach((name: string) =>
|
|
590
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
591
|
-
)
|
|
592
|
-
extractWireNames(routeMeta.permissions).forEach((name: string) =>
|
|
593
|
-
filteredState.serviceAggregation.usedPermissions.add(name)
|
|
594
|
-
)
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// Repopulate http.files with only files that have surviving routes
|
|
600
|
-
for (const method of Object.keys(filteredState.http.meta)) {
|
|
601
|
-
const routes = (
|
|
602
|
-
filteredState.http.meta as Record<
|
|
603
|
-
string,
|
|
604
|
-
Record<string, { sourceFile?: string }>
|
|
605
|
-
>
|
|
606
|
-
)[method]
|
|
607
|
-
for (const routeMeta of Object.values(routes)) {
|
|
608
|
-
if (routeMeta.sourceFile) {
|
|
609
|
-
filteredState.http.files.add(routeMeta.sourceFile)
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
// Fallback: if no sourceFile info available but routes exist, include all files
|
|
614
|
-
if (filteredState.http.files.size === 0) {
|
|
615
|
-
const hasHttpRoutes = Object.values(
|
|
616
|
-
filteredState.http.meta as Record<string, Record<string, unknown>>
|
|
617
|
-
).some((routes) => Object.keys(routes).length > 0)
|
|
618
|
-
if (hasHttpRoutes) {
|
|
619
|
-
filteredState.http.files = new Set(state.http.files)
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// Filter channels
|
|
624
|
-
for (const name of Object.keys(filteredState.channels.meta)) {
|
|
625
|
-
const channelMeta = filteredState.channels.meta[name]
|
|
626
|
-
const matches = matchesFilters(
|
|
627
|
-
filters,
|
|
628
|
-
{
|
|
629
|
-
type: 'channel' as PikkuWiringTypes,
|
|
630
|
-
name,
|
|
631
|
-
tags: channelMeta.tags,
|
|
632
|
-
},
|
|
633
|
-
logger,
|
|
634
|
-
|
|
635
|
-
undefined,
|
|
636
|
-
|
|
637
|
-
keptByDeploy
|
|
638
|
-
)
|
|
639
|
-
|
|
640
|
-
if (!matches) {
|
|
641
|
-
delete filteredState.channels.meta[name]
|
|
642
|
-
} else {
|
|
643
|
-
// Add all functions referenced by this channel
|
|
644
|
-
if ('pikkuFuncId' in channelMeta && channelMeta.pikkuFuncId) {
|
|
645
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
646
|
-
channelMeta.pikkuFuncId as string
|
|
647
|
-
)
|
|
648
|
-
}
|
|
649
|
-
if (channelMeta.connect?.pikkuFuncId) {
|
|
650
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
651
|
-
channelMeta.connect.pikkuFuncId
|
|
652
|
-
)
|
|
653
|
-
}
|
|
654
|
-
if (channelMeta.disconnect?.pikkuFuncId) {
|
|
655
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
656
|
-
channelMeta.disconnect.pikkuFuncId
|
|
657
|
-
)
|
|
658
|
-
}
|
|
659
|
-
if (channelMeta.message?.pikkuFuncId) {
|
|
660
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
661
|
-
channelMeta.message.pikkuFuncId
|
|
662
|
-
)
|
|
663
|
-
}
|
|
664
|
-
if (channelMeta.messageWirings) {
|
|
665
|
-
for (const groupKey of Object.keys(channelMeta.messageWirings)) {
|
|
666
|
-
const commands = channelMeta.messageWirings[groupKey]
|
|
667
|
-
for (const cmdKey of Object.keys(commands)) {
|
|
668
|
-
const wiring = commands[cmdKey]
|
|
669
|
-
if (wiring.pikkuFuncId) {
|
|
670
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
671
|
-
wiring.pikkuFuncId
|
|
672
|
-
)
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
extractWireNames(channelMeta.middleware).forEach((name: string) =>
|
|
678
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
679
|
-
)
|
|
680
|
-
extractWireNames(channelMeta.permissions).forEach((name: string) =>
|
|
681
|
-
filteredState.serviceAggregation.usedPermissions.add(name)
|
|
682
|
-
)
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
filteredState.channels.files = repopulateFileSetFromMeta(
|
|
687
|
-
filteredState.channels.meta,
|
|
688
|
-
state.channels.files,
|
|
689
|
-
Object.keys(filteredState.channels.meta).length > 0
|
|
690
|
-
)
|
|
691
|
-
|
|
692
|
-
// Filter triggers
|
|
693
|
-
for (const name of Object.keys(filteredState.triggers.meta)) {
|
|
694
|
-
const triggerMeta = filteredState.triggers.meta[name]
|
|
695
|
-
const matches = matchesFilters(
|
|
696
|
-
filters,
|
|
697
|
-
{
|
|
698
|
-
type: 'trigger' as PikkuWiringTypes,
|
|
699
|
-
name,
|
|
700
|
-
tags: triggerMeta.tags,
|
|
701
|
-
},
|
|
702
|
-
logger,
|
|
703
|
-
|
|
704
|
-
undefined,
|
|
705
|
-
|
|
706
|
-
keptByDeploy
|
|
707
|
-
)
|
|
708
|
-
|
|
709
|
-
if (!matches) {
|
|
710
|
-
delete filteredState.triggers.meta[name]
|
|
711
|
-
} else {
|
|
712
|
-
if (triggerMeta.pikkuFuncId) {
|
|
713
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
714
|
-
triggerMeta.pikkuFuncId
|
|
715
|
-
)
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
filteredState.triggers.files = repopulateFileSetFromMeta(
|
|
721
|
-
filteredState.triggers.meta,
|
|
722
|
-
state.triggers.files,
|
|
723
|
-
Object.keys(filteredState.triggers.meta).length > 0
|
|
724
|
-
)
|
|
725
|
-
|
|
726
|
-
// Filter scheduled tasks
|
|
727
|
-
for (const name of Object.keys(filteredState.scheduledTasks.meta)) {
|
|
728
|
-
const taskMeta = filteredState.scheduledTasks.meta[name]
|
|
729
|
-
const matches = matchesFilters(
|
|
730
|
-
filters,
|
|
731
|
-
{
|
|
732
|
-
type: 'scheduler' as PikkuWiringTypes,
|
|
733
|
-
name,
|
|
734
|
-
tags: taskMeta.tags,
|
|
735
|
-
},
|
|
736
|
-
logger,
|
|
737
|
-
|
|
738
|
-
undefined,
|
|
739
|
-
|
|
740
|
-
keptByDeploy
|
|
741
|
-
)
|
|
742
|
-
|
|
743
|
-
if (!matches) {
|
|
744
|
-
delete filteredState.scheduledTasks.meta[name]
|
|
745
|
-
} else {
|
|
746
|
-
if (taskMeta.pikkuFuncId) {
|
|
747
|
-
filteredState.serviceAggregation.usedFunctions.add(taskMeta.pikkuFuncId)
|
|
748
|
-
}
|
|
749
|
-
extractWireNames(taskMeta.middleware).forEach((name: string) =>
|
|
750
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
751
|
-
)
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
filteredState.scheduledTasks.files = repopulateFileSetFromMeta(
|
|
756
|
-
filteredState.scheduledTasks.meta,
|
|
757
|
-
state.scheduledTasks.files,
|
|
758
|
-
Object.keys(filteredState.scheduledTasks.meta).length > 0
|
|
759
|
-
)
|
|
760
|
-
|
|
761
|
-
// Filter queue workers
|
|
762
|
-
for (const name of Object.keys(filteredState.queueWorkers.meta)) {
|
|
763
|
-
const workerMeta = filteredState.queueWorkers.meta[name]
|
|
764
|
-
const matches = matchesFilters(
|
|
765
|
-
filters,
|
|
766
|
-
{
|
|
767
|
-
type: 'queue' as PikkuWiringTypes,
|
|
768
|
-
name,
|
|
769
|
-
tags: workerMeta.tags,
|
|
770
|
-
},
|
|
771
|
-
logger,
|
|
772
|
-
|
|
773
|
-
undefined,
|
|
774
|
-
|
|
775
|
-
keptByDeploy
|
|
776
|
-
)
|
|
777
|
-
|
|
778
|
-
if (!matches) {
|
|
779
|
-
delete filteredState.queueWorkers.meta[name]
|
|
780
|
-
} else {
|
|
781
|
-
if (workerMeta.pikkuFuncId) {
|
|
782
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
783
|
-
workerMeta.pikkuFuncId
|
|
784
|
-
)
|
|
785
|
-
const colonIdx = workerMeta.pikkuFuncId.indexOf(':')
|
|
786
|
-
if (colonIdx !== -1) {
|
|
787
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
788
|
-
workerMeta.pikkuFuncId.slice(colonIdx + 1)
|
|
789
|
-
)
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
extractWireNames(workerMeta.middleware).forEach((name: string) =>
|
|
793
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
794
|
-
)
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
filteredState.queueWorkers.files = repopulateFileSetFromMeta(
|
|
799
|
-
filteredState.queueWorkers.meta,
|
|
800
|
-
state.queueWorkers.files,
|
|
801
|
-
Object.keys(filteredState.queueWorkers.meta).length > 0
|
|
802
|
-
)
|
|
803
|
-
|
|
804
|
-
// Filter MCP tools
|
|
805
|
-
for (const name of Object.keys(filteredState.mcpEndpoints.toolsMeta)) {
|
|
806
|
-
const toolMeta = filteredState.mcpEndpoints.toolsMeta[name]
|
|
807
|
-
const matches = matchesFilters(
|
|
808
|
-
filters,
|
|
809
|
-
{
|
|
810
|
-
type: 'mcp' as PikkuWiringTypes,
|
|
811
|
-
name,
|
|
812
|
-
tags: toolMeta.tags,
|
|
813
|
-
},
|
|
814
|
-
logger,
|
|
815
|
-
|
|
816
|
-
undefined,
|
|
817
|
-
|
|
818
|
-
keptByDeploy
|
|
819
|
-
)
|
|
820
|
-
|
|
821
|
-
if (!matches) {
|
|
822
|
-
delete filteredState.mcpEndpoints.toolsMeta[name]
|
|
823
|
-
} else {
|
|
824
|
-
if (toolMeta.pikkuFuncId) {
|
|
825
|
-
filteredState.serviceAggregation.usedFunctions.add(toolMeta.pikkuFuncId)
|
|
826
|
-
}
|
|
827
|
-
extractWireNames(toolMeta.middleware).forEach((name: string) =>
|
|
828
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
829
|
-
)
|
|
830
|
-
extractWireNames(toolMeta.permissions).forEach((name: string) =>
|
|
831
|
-
filteredState.serviceAggregation.usedPermissions.add(name)
|
|
832
|
-
)
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
// Filter MCP resources
|
|
837
|
-
for (const name of Object.keys(filteredState.mcpEndpoints.resourcesMeta)) {
|
|
838
|
-
const resourceMeta = filteredState.mcpEndpoints.resourcesMeta[name]
|
|
839
|
-
const matches = matchesFilters(
|
|
840
|
-
filters,
|
|
841
|
-
{
|
|
842
|
-
type: 'mcp' as PikkuWiringTypes,
|
|
843
|
-
name,
|
|
844
|
-
tags: resourceMeta.tags,
|
|
845
|
-
},
|
|
846
|
-
logger,
|
|
847
|
-
|
|
848
|
-
undefined,
|
|
849
|
-
|
|
850
|
-
keptByDeploy
|
|
851
|
-
)
|
|
852
|
-
|
|
853
|
-
if (!matches) {
|
|
854
|
-
delete filteredState.mcpEndpoints.resourcesMeta[name]
|
|
855
|
-
} else {
|
|
856
|
-
if (resourceMeta.pikkuFuncId) {
|
|
857
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
858
|
-
resourceMeta.pikkuFuncId
|
|
859
|
-
)
|
|
860
|
-
}
|
|
861
|
-
extractWireNames(resourceMeta.middleware).forEach((name: string) =>
|
|
862
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
863
|
-
)
|
|
864
|
-
extractWireNames(resourceMeta.permissions).forEach((name: string) =>
|
|
865
|
-
filteredState.serviceAggregation.usedPermissions.add(name)
|
|
866
|
-
)
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
// Filter MCP prompts
|
|
871
|
-
for (const name of Object.keys(filteredState.mcpEndpoints.promptsMeta)) {
|
|
872
|
-
const promptMeta = filteredState.mcpEndpoints.promptsMeta[name]
|
|
873
|
-
const matches = matchesFilters(
|
|
874
|
-
filters,
|
|
875
|
-
{
|
|
876
|
-
type: 'mcp' as PikkuWiringTypes,
|
|
877
|
-
name,
|
|
878
|
-
tags: promptMeta.tags,
|
|
879
|
-
},
|
|
880
|
-
logger,
|
|
881
|
-
|
|
882
|
-
undefined,
|
|
883
|
-
|
|
884
|
-
keptByDeploy
|
|
885
|
-
)
|
|
886
|
-
|
|
887
|
-
if (!matches) {
|
|
888
|
-
delete filteredState.mcpEndpoints.promptsMeta[name]
|
|
889
|
-
} else {
|
|
890
|
-
if (promptMeta.pikkuFuncId) {
|
|
891
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
892
|
-
promptMeta.pikkuFuncId
|
|
893
|
-
)
|
|
894
|
-
}
|
|
895
|
-
extractWireNames(promptMeta.middleware).forEach((name: string) =>
|
|
896
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
897
|
-
)
|
|
898
|
-
extractWireNames(promptMeta.permissions).forEach((name: string) =>
|
|
899
|
-
filteredState.serviceAggregation.usedPermissions.add(name)
|
|
900
|
-
)
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
// Repopulate mcpEndpoints.files from surviving endpoint metadata
|
|
905
|
-
const hasMcpEndpoints =
|
|
906
|
-
Object.keys(filteredState.mcpEndpoints.toolsMeta).length > 0 ||
|
|
907
|
-
Object.keys(filteredState.mcpEndpoints.resourcesMeta).length > 0 ||
|
|
908
|
-
Object.keys(filteredState.mcpEndpoints.promptsMeta).length > 0
|
|
909
|
-
filteredState.mcpEndpoints.files = repopulateFileSetFromMeta(
|
|
910
|
-
{
|
|
911
|
-
toolsMeta: filteredState.mcpEndpoints.toolsMeta,
|
|
912
|
-
resourcesMeta: filteredState.mcpEndpoints.resourcesMeta,
|
|
913
|
-
promptsMeta: filteredState.mcpEndpoints.promptsMeta,
|
|
914
|
-
},
|
|
915
|
-
state.mcpEndpoints.files,
|
|
916
|
-
hasMcpEndpoints
|
|
917
|
-
)
|
|
918
|
-
|
|
919
|
-
// Filter AI agents
|
|
920
|
-
for (const name of Object.keys(filteredState.agents.agentsMeta)) {
|
|
921
|
-
const agentMeta = filteredState.agents.agentsMeta[name]
|
|
922
|
-
const matches = matchesFilters(
|
|
923
|
-
filters,
|
|
924
|
-
{
|
|
925
|
-
type: 'agent' as PikkuWiringTypes,
|
|
926
|
-
name,
|
|
927
|
-
tags: agentMeta.tags,
|
|
928
|
-
},
|
|
929
|
-
logger,
|
|
930
|
-
|
|
931
|
-
undefined,
|
|
932
|
-
|
|
933
|
-
keptByDeploy
|
|
934
|
-
)
|
|
935
|
-
|
|
936
|
-
if (!matches) {
|
|
937
|
-
delete filteredState.agents.agentsMeta[name]
|
|
938
|
-
} else {
|
|
939
|
-
if (agentMeta.pikkuFuncId) {
|
|
940
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
941
|
-
agentMeta.pikkuFuncId
|
|
942
|
-
)
|
|
943
|
-
}
|
|
944
|
-
extractWireNames(agentMeta.middleware).forEach((name: string) =>
|
|
945
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
946
|
-
)
|
|
947
|
-
extractWireNames(agentMeta.permissions).forEach((name: string) =>
|
|
948
|
-
filteredState.serviceAggregation.usedPermissions.add(name)
|
|
949
|
-
)
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
if (Object.keys(filteredState.agents.agentsMeta).length > 0) {
|
|
954
|
-
filteredState.agents.files = new Map(state.agents.files)
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
// Filter CLI programs (note: CLI filtering might be more complex with nested commands)
|
|
958
|
-
const referencedRenderers = new Set<string>()
|
|
959
|
-
|
|
960
|
-
for (const programName of Object.keys(filteredState.cli.meta.programs)) {
|
|
961
|
-
const programMeta = filteredState.cli.meta.programs[programName]
|
|
962
|
-
|
|
963
|
-
// Filter commands in the program
|
|
964
|
-
for (const commandName of Object.keys(programMeta.commands)) {
|
|
965
|
-
const commandMeta = programMeta.commands[commandName]
|
|
966
|
-
const matches = matchesFilters(
|
|
967
|
-
filters,
|
|
968
|
-
{
|
|
969
|
-
type: 'cli' as PikkuWiringTypes,
|
|
970
|
-
name: commandName,
|
|
971
|
-
tags: commandMeta.tags,
|
|
972
|
-
},
|
|
973
|
-
logger,
|
|
974
|
-
|
|
975
|
-
undefined,
|
|
976
|
-
|
|
977
|
-
keptByDeploy
|
|
978
|
-
)
|
|
979
|
-
|
|
980
|
-
if (!matches) {
|
|
981
|
-
delete programMeta.commands[commandName]
|
|
982
|
-
} else {
|
|
983
|
-
if (commandMeta.pikkuFuncId) {
|
|
984
|
-
filteredState.serviceAggregation.usedFunctions.add(
|
|
985
|
-
commandMeta.pikkuFuncId
|
|
986
|
-
)
|
|
987
|
-
}
|
|
988
|
-
extractWireNames(commandMeta.middleware).forEach((name: string) =>
|
|
989
|
-
filteredState.serviceAggregation.usedMiddleware.add(name)
|
|
990
|
-
)
|
|
991
|
-
// Track referenced renderers
|
|
992
|
-
if (commandMeta.defaultRenderName) {
|
|
993
|
-
referencedRenderers.add(commandMeta.defaultRenderName)
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
// Remove program if it has no commands left
|
|
999
|
-
if (Object.keys(programMeta.commands).length === 0) {
|
|
1000
|
-
delete filteredState.cli.meta.programs[programName]
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
// Filter out renderers that aren't referenced by any remaining commands
|
|
1005
|
-
for (const rendererName of Object.keys(
|
|
1006
|
-
filteredState.cli.meta.renderers || {}
|
|
1007
|
-
)) {
|
|
1008
|
-
if (!referencedRenderers.has(rendererName)) {
|
|
1009
|
-
delete filteredState.cli.meta.renderers![rendererName]
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// Repopulate cli.files from surviving program/renderer metadata
|
|
1014
|
-
const hasCliPrograms = Object.keys(filteredState.cli.meta.programs).length > 0
|
|
1015
|
-
const hasCliRenderers =
|
|
1016
|
-
Object.keys(filteredState.cli.meta.renderers || {}).length > 0
|
|
1017
|
-
filteredState.cli.files = repopulateFileSetFromMeta(
|
|
1018
|
-
filteredState.cli.meta,
|
|
1019
|
-
state.cli.files,
|
|
1020
|
-
hasCliPrograms || hasCliRenderers
|
|
1021
|
-
)
|
|
1022
|
-
|
|
1023
|
-
// Direct function filtering: functions that match the names/tags/directories
|
|
1024
|
-
// filters should be included even if no wiring (HTTP, scheduler, etc.) references them.
|
|
1025
|
-
// This ensures standalone RPC-callable functions survive filtering.
|
|
1026
|
-
// Only run when function-level filters are active — httpRoutes/httpMethods work
|
|
1027
|
-
// through the HTTP wiring pass which already adds the right functions.
|
|
1028
|
-
const hasFunctionLevelFilters =
|
|
1029
|
-
(filters.names && filters.names.length > 0) ||
|
|
1030
|
-
(filters.tags && filters.tags.length > 0) ||
|
|
1031
|
-
(filters.directories && filters.directories.length > 0)
|
|
1032
|
-
|
|
1033
|
-
for (const funcId of Object.keys(filteredState.functions.meta)) {
|
|
1034
|
-
if (!hasFunctionLevelFilters) break
|
|
1035
|
-
const funcMeta = filteredState.functions.meta[funcId]
|
|
1036
|
-
const funcFile = filteredState.functions.files.get(funcId)
|
|
1037
|
-
const filePath = funcFile?.path
|
|
1038
|
-
|
|
1039
|
-
const matches = matchesFilters(
|
|
1040
|
-
filters,
|
|
1041
|
-
{
|
|
1042
|
-
type: 'rpc' as PikkuWiringTypes,
|
|
1043
|
-
name: funcId,
|
|
1044
|
-
tags: funcMeta.tags,
|
|
1045
|
-
filePath,
|
|
1046
|
-
},
|
|
1047
|
-
logger,
|
|
1048
|
-
|
|
1049
|
-
undefined,
|
|
1050
|
-
|
|
1051
|
-
keptByDeploy
|
|
1052
|
-
)
|
|
1053
|
-
|
|
1054
|
-
if (matches) {
|
|
1055
|
-
filteredState.serviceAggregation.usedFunctions.add(funcId)
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// Post-filter version expansion: when an unversioned base name is matched,
|
|
1060
|
-
// include all its versions. Specific version matches (e.g. analyzeData@v1)
|
|
1061
|
-
// do NOT expand to include other versions.
|
|
1062
|
-
const includedBaseNames = new Set<string>()
|
|
1063
|
-
for (const funcId of filteredState.serviceAggregation.usedFunctions) {
|
|
1064
|
-
const { baseName, version } = parseVersionedId(funcId)
|
|
1065
|
-
if (version === null) {
|
|
1066
|
-
includedBaseNames.add(baseName)
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
if (includedBaseNames.size > 0) {
|
|
1070
|
-
for (const funcId of Object.keys(state.functions.meta)) {
|
|
1071
|
-
const { baseName } = parseVersionedId(funcId)
|
|
1072
|
-
if (includedBaseNames.has(baseName)) {
|
|
1073
|
-
filteredState.serviceAggregation.usedFunctions.add(funcId)
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
// Prune functions.meta and functions.files to only include used functions
|
|
1079
|
-
if (filteredState.serviceAggregation.usedFunctions.size > 0) {
|
|
1080
|
-
for (const funcId of Object.keys(filteredState.functions.meta)) {
|
|
1081
|
-
if (!filteredState.serviceAggregation.usedFunctions.has(funcId)) {
|
|
1082
|
-
delete filteredState.functions.meta[funcId]
|
|
1083
|
-
filteredState.functions.files.delete(funcId)
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
// Prune channels whose functions were filtered out
|
|
1088
|
-
for (const name of Object.keys(filteredState.channels.meta)) {
|
|
1089
|
-
const channelMeta = filteredState.channels.meta[name]
|
|
1090
|
-
// Check if any of the channel's functions are in the used set
|
|
1091
|
-
const channelFuncIds: string[] = []
|
|
1092
|
-
if (channelMeta.connect?.pikkuFuncId)
|
|
1093
|
-
channelFuncIds.push(channelMeta.connect.pikkuFuncId)
|
|
1094
|
-
if (channelMeta.disconnect?.pikkuFuncId)
|
|
1095
|
-
channelFuncIds.push(channelMeta.disconnect.pikkuFuncId)
|
|
1096
|
-
if (channelMeta.message?.pikkuFuncId)
|
|
1097
|
-
channelFuncIds.push(channelMeta.message.pikkuFuncId)
|
|
1098
|
-
if (channelMeta.messageWirings) {
|
|
1099
|
-
for (const groupKey of Object.keys(channelMeta.messageWirings)) {
|
|
1100
|
-
const commands = channelMeta.messageWirings[groupKey]
|
|
1101
|
-
for (const cmdKey of Object.keys(commands)) {
|
|
1102
|
-
const wiring = commands[cmdKey]
|
|
1103
|
-
if (wiring.pikkuFuncId) channelFuncIds.push(wiring.pikkuFuncId)
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
const hasUsedFunc = channelFuncIds.some((id) =>
|
|
1108
|
-
filteredState.serviceAggregation.usedFunctions.has(id)
|
|
1109
|
-
)
|
|
1110
|
-
if (channelFuncIds.length > 0 && !hasUsedFunc) {
|
|
1111
|
-
delete filteredState.channels.meta[name]
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
// Prune workflow graphs whose function was filtered out — UNLESS the
|
|
1116
|
-
// workflow's own name is in the filter (covers pikkuWorkflowGraph,
|
|
1117
|
-
// which doesn't register a function meta, so its pikkuFuncId never
|
|
1118
|
-
// ends up in usedFunctions even when callers explicitly want it).
|
|
1119
|
-
const filterNamesSet = new Set(filters.names ?? [])
|
|
1120
|
-
const workflowKeys = new Set([
|
|
1121
|
-
...Object.keys(filteredState.workflows.graphMeta),
|
|
1122
|
-
...Object.keys(filteredState.workflows.meta),
|
|
1123
|
-
])
|
|
1124
|
-
for (const name of workflowKeys) {
|
|
1125
|
-
if (filterNamesSet.has(name)) continue
|
|
1126
|
-
const graphMeta = filteredState.workflows.graphMeta[name]
|
|
1127
|
-
const workflowMeta = filteredState.workflows.meta[name]
|
|
1128
|
-
// Check both graphMeta.pikkuFuncId and meta.pikkuFuncId
|
|
1129
|
-
const pikkuFuncId = graphMeta?.pikkuFuncId ?? workflowMeta?.pikkuFuncId
|
|
1130
|
-
if (
|
|
1131
|
-
pikkuFuncId &&
|
|
1132
|
-
!filteredState.serviceAggregation.usedFunctions.has(pikkuFuncId)
|
|
1133
|
-
) {
|
|
1134
|
-
pruneWorkflow(filteredState.workflows, name, pikkuFuncId)
|
|
1135
|
-
} else if (!pikkuFuncId) {
|
|
1136
|
-
// No function ID found — prune it
|
|
1137
|
-
pruneWorkflow(filteredState.workflows, name, pikkuFuncId)
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
// Prune RPC meta to only include entries whose target function survived
|
|
1142
|
-
const survivingFuncIds = new Set(Object.keys(filteredState.functions.meta))
|
|
1143
|
-
for (const key of Object.keys(filteredState.rpc.internalMeta)) {
|
|
1144
|
-
const targetFuncId = filteredState.rpc.internalMeta[key]
|
|
1145
|
-
if (!survivingFuncIds.has(targetFuncId) && !survivingFuncIds.has(key)) {
|
|
1146
|
-
delete filteredState.rpc.internalMeta[key]
|
|
1147
|
-
filteredState.rpc.internalFiles.delete(key)
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
for (const key of Object.keys(filteredState.rpc.exposedMeta)) {
|
|
1151
|
-
const targetFuncId = filteredState.rpc.exposedMeta[key]
|
|
1152
|
-
if (!survivingFuncIds.has(targetFuncId) && !survivingFuncIds.has(key)) {
|
|
1153
|
-
delete filteredState.rpc.exposedMeta[key]
|
|
1154
|
-
filteredState.rpc.exposedFiles.delete(key)
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
for (const key of Object.keys(filteredState.rpc.remoteMeta)) {
|
|
1158
|
-
const targetFuncId = filteredState.rpc.remoteMeta[key]
|
|
1159
|
-
if (!survivingFuncIds.has(targetFuncId) && !survivingFuncIds.has(key)) {
|
|
1160
|
-
delete filteredState.rpc.remoteMeta[key]
|
|
1161
|
-
filteredState.rpc.remoteFiles.delete(key)
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
// Prune invokedFunctions to match surviving functions
|
|
1165
|
-
for (const funcId of filteredState.rpc.invokedFunctions) {
|
|
1166
|
-
if (!survivingFuncIds.has(funcId)) {
|
|
1167
|
-
filteredState.rpc.invokedFunctions.delete(funcId)
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
// Recompute requiredSchemas based on pruned functions.meta
|
|
1173
|
-
if (filteredState.serviceAggregation.usedFunctions.size > 0) {
|
|
1174
|
-
const prunedSchemas = new Set<string>()
|
|
1175
|
-
for (const funcMeta of Object.values(
|
|
1176
|
-
filteredState.functions.meta
|
|
1177
|
-
) as Array<{ inputs?: string[]; outputs?: string[] }>) {
|
|
1178
|
-
if (funcMeta.inputs?.[0]) prunedSchemas.add(funcMeta.inputs[0])
|
|
1179
|
-
if (funcMeta.outputs?.[0]) prunedSchemas.add(funcMeta.outputs[0])
|
|
1180
|
-
}
|
|
1181
|
-
filteredState.requiredSchemas = prunedSchemas
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
// Step dispatch is decided purely per-function: a workflow step runs via the
|
|
1185
|
-
// queue only when its function is marked `workflowQueued: true`. Such a unit
|
|
1186
|
-
// needs workflowService + queueService injected even though the function
|
|
1187
|
-
// itself doesn't reference them. Check the ORIGINAL graph meta (before
|
|
1188
|
-
// filtering pruned it).
|
|
1189
|
-
const survivingFuncIds = new Set(Object.keys(filteredState.functions.meta))
|
|
1190
|
-
const resolveFuncId = (rpcName: string): string =>
|
|
1191
|
-
filteredState.rpc.internalMeta[rpcName] ??
|
|
1192
|
-
filteredState.rpc.exposedMeta[rpcName] ??
|
|
1193
|
-
rpcName
|
|
1194
|
-
// Use the snapshot taken before filtering
|
|
1195
|
-
for (const graph of Object.values(originalGraphMeta)) {
|
|
1196
|
-
if (!graph.nodes) continue
|
|
1197
|
-
for (const node of Object.values(graph.nodes)) {
|
|
1198
|
-
if (!('rpcName' in node) || !node.rpcName) continue
|
|
1199
|
-
const rpcName = node.rpcName as string
|
|
1200
|
-
const funcId = resolveFuncId(rpcName)
|
|
1201
|
-
if (!survivingFuncIds.has(funcId) && !survivingFuncIds.has(rpcName))
|
|
1202
|
-
continue
|
|
1203
|
-
const funcMeta = (filteredState.functions.meta[funcId] ??
|
|
1204
|
-
filteredState.functions.meta[rpcName]) as { workflowQueued?: boolean }
|
|
1205
|
-
if (funcMeta?.workflowQueued === true) {
|
|
1206
|
-
filteredState.serviceAggregation.requiredServices.add('workflowService')
|
|
1207
|
-
filteredState.serviceAggregation.requiredServices.add('queueService')
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
// workflowService internally calls queueService (via queueStepWorker /
|
|
1213
|
-
// queueOrchestrator). Any unit that needs workflowService also needs
|
|
1214
|
-
// queueService for the transitive enqueue calls — covers
|
|
1215
|
-
// workflow-starter, graph-starter, workflow-runner, etc.
|
|
1216
|
-
if (
|
|
1217
|
-
filteredState.serviceAggregation.requiredServices.has('workflowService')
|
|
1218
|
-
) {
|
|
1219
|
-
filteredState.serviceAggregation.requiredServices.add('queueService')
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
const keptFunctionFiles = new Set<string>()
|
|
1223
|
-
for (const file of filteredState.functions.files.values()) {
|
|
1224
|
-
if (file?.path) keptFunctionFiles.add(file.path)
|
|
1225
|
-
}
|
|
1226
|
-
for (const file of filteredState.agents.invokedAgentsByFile.keys()) {
|
|
1227
|
-
if (!keptFunctionFiles.has(file)) {
|
|
1228
|
-
filteredState.agents.invokedAgentsByFile.delete(file)
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
if ((filteredState.rpc.wireAddonDeclarations?.size ?? 0) > 0) {
|
|
1233
|
-
const referencedIds = new Set<string>(
|
|
1234
|
-
filteredState.serviceAggregation.usedFunctions
|
|
1235
|
-
)
|
|
1236
|
-
const keptFiles = new Set<string>()
|
|
1237
|
-
for (const funcId of filteredState.serviceAggregation.usedFunctions) {
|
|
1238
|
-
const file = filteredState.functions.files.get(funcId)
|
|
1239
|
-
if (file?.path) keptFiles.add(file.path)
|
|
1240
|
-
}
|
|
1241
|
-
for (const [file, invoked] of state.rpc.invokedFunctionsByFile ??
|
|
1242
|
-
new Map<string, Set<string>>()) {
|
|
1243
|
-
if (!keptFiles.has(file)) continue
|
|
1244
|
-
for (const id of invoked) {
|
|
1245
|
-
referencedIds.add(id)
|
|
1246
|
-
if (id.includes(':')) {
|
|
1247
|
-
filteredState.serviceAggregation.usedFunctions.add(id)
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
for (const toolMeta of Object.values(
|
|
1252
|
-
filteredState.mcpEndpoints?.toolsMeta ?? {}
|
|
1253
|
-
) as Array<{ pikkuFuncId?: string }>) {
|
|
1254
|
-
if (toolMeta.pikkuFuncId) referencedIds.add(toolMeta.pikkuFuncId)
|
|
1255
|
-
}
|
|
1256
|
-
for (const agentMeta of Object.values(
|
|
1257
|
-
filteredState.agents?.agentsMeta ?? {}
|
|
1258
|
-
) as Array<{ tools?: string[] }>) {
|
|
1259
|
-
for (const tool of agentMeta.tools ?? []) referencedIds.add(tool)
|
|
1260
|
-
}
|
|
1261
|
-
// A unit carrying the generic `/rpc/:rpcName` catch-all (the rpcCaller
|
|
1262
|
-
// unit, and agent units) dispatches arbitrary RPCs at runtime via
|
|
1263
|
-
// `rpc.exposed(name)`, including namespaced addon RPCs (e.g.
|
|
1264
|
-
// `admin:createUser`). Those never appear in referencedIds because no
|
|
1265
|
-
// app-local function statically references them, so keep every wired
|
|
1266
|
-
// addon's declaration for such units — otherwise the per-unit bootstrap
|
|
1267
|
-
// omits the addon package bootstrap and `rpc.exposed('admin:createUser')`
|
|
1268
|
-
// throws RPCNotFoundError on the deployed stage.
|
|
1269
|
-
const hasRpcCatchAll = (filters.names ?? []).includes('/rpc/:rpcName')
|
|
1270
|
-
const keptNamespaces = new Set<string>()
|
|
1271
|
-
for (const namespace of filteredState.rpc.wireAddonDeclarations.keys()) {
|
|
1272
|
-
if (hasRpcCatchAll) {
|
|
1273
|
-
keptNamespaces.add(namespace)
|
|
1274
|
-
continue
|
|
1275
|
-
}
|
|
1276
|
-
const prefix = `${namespace}:`
|
|
1277
|
-
for (const id of referencedIds) {
|
|
1278
|
-
if (id.startsWith(prefix)) {
|
|
1279
|
-
keptNamespaces.add(namespace)
|
|
1280
|
-
break
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
filteredState.rpc.wireAddonDeclarations = new Map(
|
|
1285
|
-
[...filteredState.rpc.wireAddonDeclarations].filter(([namespace]) =>
|
|
1286
|
-
keptNamespaces.has(namespace)
|
|
1287
|
-
)
|
|
1288
|
-
)
|
|
1289
|
-
filteredState.rpc.usedAddons = new Set(
|
|
1290
|
-
[...filteredState.rpc.usedAddons].filter((namespace) =>
|
|
1291
|
-
keptNamespaces.has(namespace)
|
|
1292
|
-
)
|
|
1293
|
-
)
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
// Recalculate requiredServices based on filtered functions/middleware/permissions
|
|
1297
|
-
// Need to cast to InspectorState temporarily for aggregateRequiredServices
|
|
1298
|
-
const stateForAggregation = filteredState as InspectorState
|
|
1299
|
-
aggregateRequiredServices(stateForAggregation)
|
|
1300
|
-
|
|
1301
|
-
return filteredState
|
|
1302
|
-
}
|