@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.
- package/CHANGELOG.md +330 -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 +14 -0
- 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 -2232
- package/src/utils/filter-inspector-state.ts +0 -1298
- 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,228 +0,0 @@
|
|
|
1
|
-
import { parseVersionedId, formatVersionedId } from '@pikku/core/utils'
|
|
2
|
-
import type { CLICommandMeta } from '@pikku/core/cli'
|
|
3
|
-
import type { InspectorState, InspectorLogger } from '../types.js'
|
|
4
|
-
import { ErrorCode } from '../error-codes.js'
|
|
5
|
-
|
|
6
|
-
export function resolveLatestVersions(
|
|
7
|
-
state: InspectorState,
|
|
8
|
-
logger: InspectorLogger
|
|
9
|
-
): void {
|
|
10
|
-
const functionsMeta = state.functions.meta
|
|
11
|
-
|
|
12
|
-
const groups = new Map<
|
|
13
|
-
string,
|
|
14
|
-
{
|
|
15
|
-
explicit: { id: string; version: number }[]
|
|
16
|
-
unversioned: string | null
|
|
17
|
-
}
|
|
18
|
-
>()
|
|
19
|
-
|
|
20
|
-
for (const [id, meta] of Object.entries(functionsMeta)) {
|
|
21
|
-
const parsed = parseVersionedId(id)
|
|
22
|
-
const baseName = parsed.version !== null ? parsed.baseName : id
|
|
23
|
-
let group = groups.get(baseName)
|
|
24
|
-
if (!group) {
|
|
25
|
-
group = { explicit: [], unversioned: null }
|
|
26
|
-
groups.set(baseName, group)
|
|
27
|
-
}
|
|
28
|
-
if (parsed.version !== null) {
|
|
29
|
-
group.explicit.push({ id, version: parsed.version })
|
|
30
|
-
} else if (meta.version !== undefined) {
|
|
31
|
-
group.explicit.push({ id, version: meta.version })
|
|
32
|
-
} else {
|
|
33
|
-
group.unversioned = id
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (const [baseName, group] of groups) {
|
|
38
|
-
if (group.explicit.length === 0) {
|
|
39
|
-
continue
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const seen = new Map<number, string>()
|
|
43
|
-
for (const entry of group.explicit) {
|
|
44
|
-
const existing = seen.get(entry.version)
|
|
45
|
-
if (existing) {
|
|
46
|
-
logger.critical(
|
|
47
|
-
ErrorCode.DUPLICATE_FUNCTION_VERSION,
|
|
48
|
-
`Duplicate version ${entry.version} for function '${baseName}': '${existing}' and '${entry.id}'.`
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
seen.set(entry.version, entry.id)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const maxVersion = Math.max(...group.explicit.map((e) => e.version))
|
|
55
|
-
|
|
56
|
-
if (group.unversioned) {
|
|
57
|
-
const implicitVersion = maxVersion + 1
|
|
58
|
-
const oldId = group.unversioned
|
|
59
|
-
const newId = formatVersionedId(baseName, implicitVersion)
|
|
60
|
-
|
|
61
|
-
const meta = functionsMeta[oldId]!
|
|
62
|
-
meta.pikkuFuncId = newId
|
|
63
|
-
meta.version = implicitVersion
|
|
64
|
-
delete functionsMeta[oldId]
|
|
65
|
-
functionsMeta[newId] = meta
|
|
66
|
-
|
|
67
|
-
const fileEntry = state.functions.files.get(oldId)
|
|
68
|
-
if (fileEntry) {
|
|
69
|
-
state.functions.files.delete(oldId)
|
|
70
|
-
state.functions.files.set(newId, fileEntry)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const rpcFileEntry = state.rpc.internalFiles.get(oldId)
|
|
74
|
-
if (rpcFileEntry) {
|
|
75
|
-
state.rpc.internalFiles.delete(oldId)
|
|
76
|
-
state.rpc.internalFiles.set(newId, rpcFileEntry)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (state.rpc.invokedFunctions.has(oldId)) {
|
|
80
|
-
state.rpc.invokedFunctions.delete(oldId)
|
|
81
|
-
state.rpc.invokedFunctions.add(newId)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (state.serviceAggregation.usedFunctions.has(oldId)) {
|
|
85
|
-
state.serviceAggregation.usedFunctions.delete(oldId)
|
|
86
|
-
state.serviceAggregation.usedFunctions.add(newId)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
state.rpc.internalMeta[baseName] = newId
|
|
90
|
-
state.rpc.internalMeta[newId] = newId
|
|
91
|
-
|
|
92
|
-
if (state.rpc.exposedMeta[baseName] === oldId) {
|
|
93
|
-
state.rpc.exposedMeta[baseName] = newId
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
updateWiringReferences(state, oldId, newId)
|
|
97
|
-
} else {
|
|
98
|
-
const latest = group.explicit.reduce((a, b) =>
|
|
99
|
-
a.version > b.version ? a : b
|
|
100
|
-
)
|
|
101
|
-
state.rpc.internalMeta[baseName] = latest.id
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (state.rpc.exposedMeta[baseName]) {
|
|
105
|
-
const latestId = state.rpc.internalMeta[baseName]!
|
|
106
|
-
state.rpc.exposedMeta[baseName] = latestId
|
|
107
|
-
for (const entry of group.explicit) {
|
|
108
|
-
state.rpc.exposedMeta[entry.id] = entry.id
|
|
109
|
-
const fileEntry = state.rpc.internalFiles.get(entry.id)
|
|
110
|
-
if (fileEntry) {
|
|
111
|
-
state.rpc.exposedFiles.set(entry.id, fileEntry)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (group.unversioned) {
|
|
115
|
-
state.rpc.exposedMeta[latestId] = latestId
|
|
116
|
-
const fileEntry = state.rpc.internalFiles.get(latestId)
|
|
117
|
-
if (fileEntry) {
|
|
118
|
-
state.rpc.exposedFiles.set(latestId, fileEntry)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
for (const entry of group.explicit) {
|
|
124
|
-
state.rpc.invokedFunctions.add(entry.id)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function updateWiringReferences(
|
|
130
|
-
state: InspectorState,
|
|
131
|
-
oldId: string,
|
|
132
|
-
newId: string
|
|
133
|
-
): void {
|
|
134
|
-
// HTTP routes
|
|
135
|
-
if (state.http) {
|
|
136
|
-
for (const methods of Object.values(state.http.meta)) {
|
|
137
|
-
for (const meta of Object.values(methods)) {
|
|
138
|
-
if (meta.pikkuFuncId === oldId) {
|
|
139
|
-
meta.pikkuFuncId = newId
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Channels: connect/disconnect/message slots + action-routed message wirings
|
|
146
|
-
if (state.channels) {
|
|
147
|
-
for (const channel of Object.values(state.channels.meta)) {
|
|
148
|
-
for (const slot of [
|
|
149
|
-
channel.connect,
|
|
150
|
-
channel.disconnect,
|
|
151
|
-
channel.message,
|
|
152
|
-
]) {
|
|
153
|
-
if (slot && slot.pikkuFuncId === oldId) {
|
|
154
|
-
slot.pikkuFuncId = newId
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
for (const routes of Object.values(channel.messageWirings)) {
|
|
158
|
-
for (const message of Object.values(routes)) {
|
|
159
|
-
if (message.pikkuFuncId === oldId) {
|
|
160
|
-
message.pikkuFuncId = newId
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// CLI programs: commands and nested subcommands. This also covers
|
|
168
|
-
// CLI-over-channel generation, which reads command funcIds from this meta.
|
|
169
|
-
if (state.cli) {
|
|
170
|
-
const updateCommands = (commands: Record<string, CLICommandMeta>): void => {
|
|
171
|
-
for (const command of Object.values(commands)) {
|
|
172
|
-
if (command.pikkuFuncId === oldId) {
|
|
173
|
-
command.pikkuFuncId = newId
|
|
174
|
-
}
|
|
175
|
-
if (command.subcommands) {
|
|
176
|
-
updateCommands(command.subcommands)
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
for (const program of Object.values(state.cli.meta.programs)) {
|
|
181
|
-
updateCommands(program.commands)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Scheduled tasks
|
|
186
|
-
if (state.scheduledTasks) {
|
|
187
|
-
for (const task of Object.values(state.scheduledTasks.meta)) {
|
|
188
|
-
if (task.pikkuFuncId === oldId) {
|
|
189
|
-
task.pikkuFuncId = newId
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Queue workers
|
|
195
|
-
if (state.queueWorkers) {
|
|
196
|
-
for (const worker of Object.values(state.queueWorkers.meta)) {
|
|
197
|
-
if (worker.pikkuFuncId === oldId) {
|
|
198
|
-
worker.pikkuFuncId = newId
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Trigger sources (TriggerSourceMeta carries the handler's pikkuFuncId).
|
|
204
|
-
// Gateways/workflows/agents reference functions by bare rpc name and are
|
|
205
|
-
// resolved at runtime via state.rpc.internalMeta, so they need no rewrite here.
|
|
206
|
-
if (state.triggers) {
|
|
207
|
-
for (const source of Object.values(state.triggers.sourceMeta)) {
|
|
208
|
-
if (source.pikkuFuncId === oldId) {
|
|
209
|
-
source.pikkuFuncId = newId
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// MCP resources, tools, and prompts
|
|
215
|
-
if (state.mcpEndpoints) {
|
|
216
|
-
for (const collection of [
|
|
217
|
-
state.mcpEndpoints.resourcesMeta,
|
|
218
|
-
state.mcpEndpoints.toolsMeta,
|
|
219
|
-
state.mcpEndpoints.promptsMeta,
|
|
220
|
-
]) {
|
|
221
|
-
for (const endpoint of Object.values(collection)) {
|
|
222
|
-
if (endpoint.pikkuFuncId === oldId) {
|
|
223
|
-
endpoint.pikkuFuncId = newId
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { test, describe } from 'node:test'
|
|
2
|
-
import { strict as assert } from 'node:assert'
|
|
3
|
-
import {
|
|
4
|
-
mkdtempSync,
|
|
5
|
-
rmSync,
|
|
6
|
-
statSync,
|
|
7
|
-
utimesSync,
|
|
8
|
-
writeFileSync,
|
|
9
|
-
} from 'node:fs'
|
|
10
|
-
import { tmpdir } from 'node:os'
|
|
11
|
-
import { join } from 'node:path'
|
|
12
|
-
import {
|
|
13
|
-
readDiskTSSchemas,
|
|
14
|
-
writeDiskTSSchemas,
|
|
15
|
-
type SchemaDep,
|
|
16
|
-
} from './schema-generator.js'
|
|
17
|
-
import type { InspectorLogger } from '../types.js'
|
|
18
|
-
|
|
19
|
-
const silentLogger = {
|
|
20
|
-
debug: () => {},
|
|
21
|
-
info: () => {},
|
|
22
|
-
warn: () => {},
|
|
23
|
-
error: () => {},
|
|
24
|
-
} as unknown as InspectorLogger
|
|
25
|
-
|
|
26
|
-
const KEY = 'test-key'
|
|
27
|
-
const SCHEMAS = { Thing: { type: 'object' } }
|
|
28
|
-
|
|
29
|
-
/** A cache directory and one source file the cached schemas claim to derive from. */
|
|
30
|
-
function makeCache(sourceContent: string): {
|
|
31
|
-
cacheDir: string
|
|
32
|
-
source: string
|
|
33
|
-
deps: SchemaDep[]
|
|
34
|
-
cleanup: () => void
|
|
35
|
-
} {
|
|
36
|
-
const dir = mkdtempSync(join(tmpdir(), 'pikku-schema-cache-'))
|
|
37
|
-
const source = join(dir, 'thing.ts')
|
|
38
|
-
writeFileSync(source, sourceContent)
|
|
39
|
-
const stat = statSync(source)
|
|
40
|
-
return {
|
|
41
|
-
cacheDir: dir,
|
|
42
|
-
source,
|
|
43
|
-
deps: [{ path: source, mtimeMs: stat.mtimeMs, size: stat.size }],
|
|
44
|
-
cleanup: () => rmSync(dir, { recursive: true, force: true }),
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
describe('TS schema disk cache', () => {
|
|
49
|
-
test('serves the cached schemas while their sources are untouched', () => {
|
|
50
|
-
const { cacheDir, deps, cleanup } = makeCache('export interface Thing {}')
|
|
51
|
-
try {
|
|
52
|
-
writeDiskTSSchemas(silentLogger, cacheDir, KEY, SCHEMAS, deps)
|
|
53
|
-
const hit = readDiskTSSchemas(silentLogger, cacheDir, KEY)
|
|
54
|
-
assert.deepEqual(hit?.schemas, SCHEMAS)
|
|
55
|
-
} finally {
|
|
56
|
-
cleanup()
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
test('discards the cache when a type it was built from changes', () => {
|
|
61
|
-
const { cacheDir, source, deps, cleanup } = makeCache(
|
|
62
|
-
'export interface Thing {}'
|
|
63
|
-
)
|
|
64
|
-
try {
|
|
65
|
-
writeDiskTSSchemas(silentLogger, cacheDir, KEY, SCHEMAS, deps)
|
|
66
|
-
// The shape changes but the name does not, so the cache key — which hashes
|
|
67
|
-
// only the synthesized custom-types source — is identical. This is the case
|
|
68
|
-
// that used to serve a stale schema, and it survived `rm -rf .pikku`
|
|
69
|
-
// because the cache lives under node_modules/.cache.
|
|
70
|
-
writeFileSync(source, 'export interface Thing { added: string }')
|
|
71
|
-
assert.equal(readDiskTSSchemas(silentLogger, cacheDir, KEY), null)
|
|
72
|
-
} finally {
|
|
73
|
-
cleanup()
|
|
74
|
-
}
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
test('discards the cache when a source is rewritten to the same size', () => {
|
|
78
|
-
const { cacheDir, source, deps, cleanup } = makeCache('export type A = 1')
|
|
79
|
-
try {
|
|
80
|
-
writeDiskTSSchemas(silentLogger, cacheDir, KEY, SCHEMAS, deps)
|
|
81
|
-
// Same byte count, different type: size alone would call this unchanged, so
|
|
82
|
-
// the mtime half of the fingerprint is what has to catch it.
|
|
83
|
-
writeFileSync(source, 'export type A = 2')
|
|
84
|
-
utimesSync(source, new Date(), new Date(Date.now() + 5000))
|
|
85
|
-
assert.equal(readDiskTSSchemas(silentLogger, cacheDir, KEY), null)
|
|
86
|
-
} finally {
|
|
87
|
-
cleanup()
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
test('discards the cache when a source it was built from is deleted', () => {
|
|
92
|
-
const { cacheDir, source, deps, cleanup } = makeCache('export type A = 1')
|
|
93
|
-
try {
|
|
94
|
-
writeDiskTSSchemas(silentLogger, cacheDir, KEY, SCHEMAS, deps)
|
|
95
|
-
rmSync(source)
|
|
96
|
-
assert.equal(readDiskTSSchemas(silentLogger, cacheDir, KEY), null)
|
|
97
|
-
} finally {
|
|
98
|
-
cleanup()
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('discards a cache written before deps were tracked', () => {
|
|
103
|
-
const { cacheDir, cleanup } = makeCache('export type A = 1')
|
|
104
|
-
try {
|
|
105
|
-
writeFileSync(
|
|
106
|
-
join(cacheDir, 'ts-schemas.json'),
|
|
107
|
-
JSON.stringify({ key: KEY, schemas: SCHEMAS })
|
|
108
|
-
)
|
|
109
|
-
assert.equal(readDiskTSSchemas(silentLogger, cacheDir, KEY), null)
|
|
110
|
-
} finally {
|
|
111
|
-
cleanup()
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
test('still misses on a different key', () => {
|
|
116
|
-
const { cacheDir, deps, cleanup } = makeCache('export type A = 1')
|
|
117
|
-
try {
|
|
118
|
-
writeDiskTSSchemas(silentLogger, cacheDir, KEY, SCHEMAS, deps)
|
|
119
|
-
assert.equal(readDiskTSSchemas(silentLogger, cacheDir, 'other'), null)
|
|
120
|
-
} finally {
|
|
121
|
-
cleanup()
|
|
122
|
-
}
|
|
123
|
-
})
|
|
124
|
-
})
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { test, describe, before, after } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs'
|
|
4
|
-
import { tmpdir } from 'os'
|
|
5
|
-
import { join } from 'path'
|
|
6
|
-
import { fileURLToPath } from 'url'
|
|
7
|
-
|
|
8
|
-
import { getInitialInspectorState } from '../inspector.js'
|
|
9
|
-
import { generateAllSchemas, schemaRuntimeFile } from './schema-generator.js'
|
|
10
|
-
|
|
11
|
-
const debugLines: string[] = []
|
|
12
|
-
const logger = {
|
|
13
|
-
debug(message: string) {
|
|
14
|
-
debugLines.push(message)
|
|
15
|
-
},
|
|
16
|
-
info() {},
|
|
17
|
-
warn() {},
|
|
18
|
-
error() {},
|
|
19
|
-
} as any
|
|
20
|
-
|
|
21
|
-
const UNRELATED_FILE_COUNT = 300
|
|
22
|
-
|
|
23
|
-
let projectDir: string
|
|
24
|
-
|
|
25
|
-
before(() => {
|
|
26
|
-
projectDir = mkdtempSync(join(tmpdir(), 'pikku-schema-gen-'))
|
|
27
|
-
mkdirSync(join(projectDir, 'src'))
|
|
28
|
-
writeFileSync(
|
|
29
|
-
join(projectDir, 'tsconfig.json'),
|
|
30
|
-
JSON.stringify({
|
|
31
|
-
compilerOptions: {
|
|
32
|
-
target: 'ESNext',
|
|
33
|
-
module: 'NodeNext',
|
|
34
|
-
moduleResolution: 'NodeNext',
|
|
35
|
-
strict: true,
|
|
36
|
-
skipLibCheck: true,
|
|
37
|
-
},
|
|
38
|
-
include: ['src/**/*.ts'],
|
|
39
|
-
})
|
|
40
|
-
)
|
|
41
|
-
writeFileSync(
|
|
42
|
-
join(projectDir, 'src', 'types.ts'),
|
|
43
|
-
'export type Thing = { id: string; count: number }\n'
|
|
44
|
-
)
|
|
45
|
-
// Files the tsconfig picks up but no generated custom type references. They
|
|
46
|
-
// stand in for the bulk of a real project, which the schema program has no
|
|
47
|
-
// reason to load.
|
|
48
|
-
for (let i = 0; i < UNRELATED_FILE_COUNT; i += 1) {
|
|
49
|
-
writeFileSync(
|
|
50
|
-
join(projectDir, 'src', `unrelated-${i}.ts`),
|
|
51
|
-
`export type Unrelated${i} = { a: string; b: number; c: boolean }\n`
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
after(() => {
|
|
57
|
-
rmSync(projectDir, { recursive: true, force: true })
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('generateAllSchemas', () => {
|
|
61
|
-
// NOTE: this must be the first test in the file. The module-level caches are
|
|
62
|
-
// per-process, so only the first call actually builds a program — a later call
|
|
63
|
-
// reuses it and would measure a few MB no matter what.
|
|
64
|
-
//
|
|
65
|
-
// The schema generator builds a second ts.Program over the whole tsconfig —
|
|
66
|
-
// on a real tree that is ~2.5k files and ~600MB, and it used to be pinned at
|
|
67
|
-
// module scope for the life of the process. Combined with the main inspector
|
|
68
|
-
// program it pushed peak heap past 2GB and OOM'd `pikku all` (#982).
|
|
69
|
-
//
|
|
70
|
-
// Only the program is released; cachedTSSchemas (the small result cache that
|
|
71
|
-
// powers the same-process fast path) is deliberately kept.
|
|
72
|
-
test('does not retain the schema ts.Program after returning', async (t) => {
|
|
73
|
-
if (typeof global.gc !== 'function') {
|
|
74
|
-
t.skip('requires --expose-gc')
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const state = getInitialInspectorState(projectDir) as any
|
|
79
|
-
// A distinct type keeps this off the cachedTSSchemas fast path, so the
|
|
80
|
-
// program is genuinely rebuilt (and therefore genuinely retainable) here.
|
|
81
|
-
state.functions.typesMap.addCustomType(
|
|
82
|
-
'RetentionProbeInput',
|
|
83
|
-
'{ probe: string; n: number }',
|
|
84
|
-
[]
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
global.gc!()
|
|
88
|
-
const before = process.memoryUsage().heapUsed
|
|
89
|
-
|
|
90
|
-
await generateAllSchemas(
|
|
91
|
-
logger,
|
|
92
|
-
{ tsconfig: join(projectDir, 'tsconfig.json') },
|
|
93
|
-
state
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
global.gc!()
|
|
97
|
-
const retainedMB = (process.memoryUsage().heapUsed - before) / 1024 / 1024
|
|
98
|
-
|
|
99
|
-
// A retained program (with its SourceFiles and TypeChecker) measures tens of
|
|
100
|
-
// MB even for this one-file fixture; the schemas themselves are a few KB.
|
|
101
|
-
assert.ok(
|
|
102
|
-
retainedMB < 10,
|
|
103
|
-
`expected the schema program to be released, but ${retainedMB.toFixed(1)}MB is still retained after GC`
|
|
104
|
-
)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
// The virtual file explicitly imports every type it needs, so it is a complete
|
|
108
|
-
// root on its own. Rooting the program at the whole tsconfig instead made it
|
|
109
|
-
// load the entire project (2572 files on a real tree vs 870), for no gain in
|
|
110
|
-
// what could be resolved (#982).
|
|
111
|
-
test('scopes the schema program to the virtual file import closure', async () => {
|
|
112
|
-
debugLines.length = 0
|
|
113
|
-
|
|
114
|
-
const state = getInitialInspectorState(projectDir) as any
|
|
115
|
-
state.functions.typesMap.addCustomType(
|
|
116
|
-
'ScopingProbeInput',
|
|
117
|
-
'{ scope: string }',
|
|
118
|
-
[]
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
await generateAllSchemas(
|
|
122
|
-
logger,
|
|
123
|
-
{ tsconfig: join(projectDir, 'tsconfig.json') },
|
|
124
|
-
state
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
const line = debugLines.find((l) => l.includes('Created schema program'))
|
|
128
|
-
assert.ok(
|
|
129
|
-
line,
|
|
130
|
-
`expected a "Created schema program" debug line, got: ${debugLines.join(' | ')}`
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
const fileCount = Number(line!.match(/(\d+) files/)?.[1])
|
|
134
|
-
assert.ok(
|
|
135
|
-
Number.isFinite(fileCount),
|
|
136
|
-
`could not parse a file count out of: ${line}`
|
|
137
|
-
)
|
|
138
|
-
// The virtual file plus types.ts plus the default lib. Rooting at the whole
|
|
139
|
-
// tsconfig would pull in all UNRELATED_FILE_COUNT files on top of that.
|
|
140
|
-
assert.ok(
|
|
141
|
-
fileCount < UNRELATED_FILE_COUNT,
|
|
142
|
-
`expected the schema program to skip the ${UNRELATED_FILE_COUNT} unreferenced files, but it loaded ${fileCount}`
|
|
143
|
-
)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
test('produces a schema for a custom type', async () => {
|
|
147
|
-
const state = getInitialInspectorState(projectDir) as any
|
|
148
|
-
state.functions.typesMap.addCustomType(
|
|
149
|
-
'ThingInput',
|
|
150
|
-
'{ id: string; count: number }',
|
|
151
|
-
[]
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
const schemas = await generateAllSchemas(
|
|
155
|
-
logger,
|
|
156
|
-
{ tsconfig: join(projectDir, 'tsconfig.json') },
|
|
157
|
-
state
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
assert.ok(
|
|
161
|
-
Object.keys(schemas).length > 0,
|
|
162
|
-
'expected at least one generated schema'
|
|
163
|
-
)
|
|
164
|
-
})
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* A schema imported from a built workspace package resolves to that package's
|
|
169
|
-
* declaration file, and a `.d.ts` has no runtime exports at all — every one of
|
|
170
|
-
* them is a type. Importing it to read the zod object yields an empty module and
|
|
171
|
-
* every schema in it is reported missing, so the emitted JS beside it is what has
|
|
172
|
-
* to be imported.
|
|
173
|
-
*/
|
|
174
|
-
describe('schemaRuntimeFile', () => {
|
|
175
|
-
test('a source file the developer wrote is imported as it is', () => {
|
|
176
|
-
const written = join(projectDir, 'src', 'types.ts')
|
|
177
|
-
assert.equal(schemaRuntimeFile(written), written)
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
test('a declaration file resolves to the JS emitted beside it', () => {
|
|
181
|
-
writeFileSync(
|
|
182
|
-
join(projectDir, 'src', 'reindex.d.ts'),
|
|
183
|
-
'export declare const a: number\n'
|
|
184
|
-
)
|
|
185
|
-
writeFileSync(join(projectDir, 'src', 'reindex.js'), 'export const a = 1\n')
|
|
186
|
-
assert.equal(
|
|
187
|
-
schemaRuntimeFile(join(projectDir, 'src', 'reindex.d.ts')),
|
|
188
|
-
join(projectDir, 'src', 'reindex.js')
|
|
189
|
-
)
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
test('.d.mts resolves to the ESM file beside it', () => {
|
|
193
|
-
writeFileSync(
|
|
194
|
-
join(projectDir, 'src', 'esm.d.mts'),
|
|
195
|
-
'export declare const a: number\n'
|
|
196
|
-
)
|
|
197
|
-
writeFileSync(join(projectDir, 'src', 'esm.mjs'), 'export const a = 1\n')
|
|
198
|
-
assert.equal(
|
|
199
|
-
schemaRuntimeFile(join(projectDir, 'src', 'esm.d.mts')),
|
|
200
|
-
join(projectDir, 'src', 'esm.mjs')
|
|
201
|
-
)
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
test('.d.cts resolves to the CommonJS file beside it', () => {
|
|
205
|
-
// Its own test rather than a second assertion above: the two extensions are
|
|
206
|
-
// separate branches, and one covering both is one that passes while half of
|
|
207
|
-
// it is broken.
|
|
208
|
-
writeFileSync(
|
|
209
|
-
join(projectDir, 'src', 'commonjs.d.cts'),
|
|
210
|
-
'export declare const a: number\n'
|
|
211
|
-
)
|
|
212
|
-
writeFileSync(join(projectDir, 'src', 'commonjs.cjs'), 'exports.a = 1\n')
|
|
213
|
-
assert.equal(
|
|
214
|
-
schemaRuntimeFile(join(projectDir, 'src', 'commonjs.d.cts')),
|
|
215
|
-
join(projectDir, 'src', 'commonjs.cjs')
|
|
216
|
-
)
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
test('a declaration file with no emitted JS keeps its own path', () => {
|
|
220
|
-
// So the "could not find exported schema" error still names a file the
|
|
221
|
-
// developer can open, rather than one that was never written.
|
|
222
|
-
const orphan = join(projectDir, 'src', 'types-only.d.ts')
|
|
223
|
-
writeFileSync(orphan, 'export declare const a: number\n')
|
|
224
|
-
assert.equal(schemaRuntimeFile(orphan), orphan)
|
|
225
|
-
})
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
describe('a Zod schema that cannot be a wire contract', () => {
|
|
229
|
-
test('names .transform() and its own error code, not a downstream one', async () => {
|
|
230
|
-
// A transform used to surface as PKU724 — the *bad `#pikku/*` import* error
|
|
231
|
-
// — because the schema failed to convert, its generated leaf never got
|
|
232
|
-
// written, and the type that leaf feeds resolved to an error type. The
|
|
233
|
-
// message sent you to an import block that was fine.
|
|
234
|
-
// Inside the package, so the fixture's `import 'zod'` resolves the same
|
|
235
|
-
// way a project's would; a tmpdir has no node_modules above it.
|
|
236
|
-
const dir = mkdtempSync(
|
|
237
|
-
join(fileURLToPath(new URL('../../', import.meta.url)), '.zod-transform-')
|
|
238
|
-
)
|
|
239
|
-
try {
|
|
240
|
-
const file = join(dir, 'schema.ts')
|
|
241
|
-
writeFileSync(
|
|
242
|
-
file,
|
|
243
|
-
`import * as z from 'zod'\n` +
|
|
244
|
-
`export const CreateAssessment = z.object({ patientId: z.string(), when: z.string().transform((s) => new Date(s)) })\n`
|
|
245
|
-
)
|
|
246
|
-
const state = getInitialInspectorState(dir) as any
|
|
247
|
-
state.schemaLookup.set('CreateAssessmentInput', {
|
|
248
|
-
variableName: 'CreateAssessment',
|
|
249
|
-
sourceFile: file,
|
|
250
|
-
vendor: 'zod',
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
const errors: string[] = []
|
|
254
|
-
await assert.rejects(() =>
|
|
255
|
-
generateAllSchemas(
|
|
256
|
-
{ ...logger, error: (m: string) => errors.push(m) },
|
|
257
|
-
{ tsconfig: join(projectDir, 'tsconfig.json') },
|
|
258
|
-
state
|
|
259
|
-
)
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
const reported = errors.join('\n')
|
|
263
|
-
assert.match(reported, /PKU491/)
|
|
264
|
-
assert.match(reported, /CreateAssessmentInput/)
|
|
265
|
-
assert.match(reported, /\.transform\(\)/)
|
|
266
|
-
assert.match(reported, /\.when/)
|
|
267
|
-
assert.doesNotMatch(reported, /PKU724/)
|
|
268
|
-
} finally {
|
|
269
|
-
rmSync(dir, { recursive: true, force: true })
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
})
|