@pikku/inspector 0.11.2 → 0.12.0
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 +11 -1
- package/OPTIMIZATION-PLAN.md +195 -0
- package/dist/add/add-ai-agent.d.ts +2 -0
- package/dist/add/add-ai-agent.js +314 -0
- package/dist/add/add-channel.js +69 -61
- package/dist/add/add-cli.js +36 -18
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.js +250 -75
- package/dist/add/add-http-route.d.ts +19 -10
- package/dist/add/add-http-route.js +152 -66
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +159 -0
- package/dist/add/add-keyed-wiring.d.ts +12 -0
- package/dist/add/add-keyed-wiring.js +97 -0
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.js +14 -9
- package/dist/add/add-middleware.d.ts +1 -4
- package/dist/add/add-middleware.js +364 -79
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +152 -40
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.js +14 -0
- package/dist/add/add-schedule.js +11 -5
- package/dist/add/add-secret.d.ts +3 -0
- package/dist/add/add-secret.js +82 -0
- package/dist/add/add-trigger.d.ts +2 -0
- package/dist/add/add-trigger.js +87 -0
- package/dist/add/add-variable.d.ts +1 -0
- package/dist/add/add-variable.js +8 -0
- package/dist/add/add-workflow-graph.d.ts +3 -2
- package/dist/add/add-workflow-graph.js +143 -406
- package/dist/add/add-workflow.js +6 -4
- package/dist/error-codes.d.ts +14 -1
- package/dist/error-codes.js +19 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -4
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +91 -14
- package/dist/schema-generator.d.ts +1 -0
- package/dist/schema-generator.js +1 -0
- package/dist/types-map.js +10 -1
- package/dist/types.d.ts +163 -39
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +41 -0
- package/dist/utils/contract-hashes.d.ts +35 -0
- package/dist/utils/contract-hashes.js +202 -0
- package/dist/utils/custom-types-generator.d.ts +9 -0
- package/dist/utils/custom-types-generator.js +71 -0
- package/dist/utils/detect-schema-vendor.d.ts +22 -0
- package/dist/utils/detect-schema-vendor.js +76 -0
- package/dist/utils/ensure-function-metadata.d.ts +5 -2
- package/dist/utils/ensure-function-metadata.js +220 -6
- package/dist/utils/extract-function-name.d.ts +5 -16
- package/dist/utils/extract-function-name.js +86 -291
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/get-property-value.d.ts +6 -1
- package/dist/utils/get-property-value.js +28 -3
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +7 -30
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +2 -2
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +9 -10
- package/dist/utils/post-process.js +231 -24
- package/dist/utils/resolve-external-package.d.ts +12 -0
- package/dist/utils/resolve-external-package.js +34 -0
- package/dist/utils/resolve-function-types.d.ts +6 -0
- package/dist/utils/resolve-function-types.js +29 -0
- package/dist/utils/resolve-identifier.d.ts +10 -0
- package/dist/utils/resolve-identifier.js +36 -0
- package/dist/utils/resolve-versions.d.ts +2 -0
- package/dist/utils/resolve-versions.js +78 -0
- package/dist/utils/schema-generator.d.ts +9 -0
- package/dist/utils/schema-generator.js +209 -0
- package/dist/utils/serialize-inspector-state.d.ts +59 -22
- package/dist/utils/serialize-inspector-state.js +92 -20
- package/dist/utils/serialize-mcp-json.d.ts +2 -0
- package/dist/utils/serialize-mcp-json.js +99 -0
- package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
- package/dist/utils/serialize-middleware-groups-meta.js +28 -0
- package/dist/utils/serialize-openapi-json.d.ts +85 -0
- package/dist/utils/serialize-openapi-json.js +151 -0
- package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
- package/dist/utils/serialize-permissions-groups-meta.js +31 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
- package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
- package/dist/utils/workflow/graph/index.d.ts +2 -0
- package/dist/utils/workflow/graph/index.js +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
- package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
- package/dist/visit.js +11 -6
- package/package.json +14 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +82 -79
- package/src/add/add-cli.ts +49 -20
- package/src/add/add-file-with-factory.ts +2 -0
- package/src/add/add-functions.ts +330 -86
- package/src/add/add-http-route.ts +245 -88
- package/src/add/add-http-routes.ts +228 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +26 -15
- package/src/add/add-mcp-resource.ts +27 -15
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +24 -19
- package/src/add/add-rpc-invocations.ts +17 -0
- package/src/add/add-schedule.ts +16 -11
- package/src/add/add-secret.ts +140 -0
- package/src/add/add-trigger.ts +154 -0
- package/src/add/add-variable.ts +9 -0
- package/src/add/add-workflow-graph.ts +180 -522
- package/src/add/add-workflow.ts +5 -4
- package/src/error-codes.ts +24 -1
- package/src/index.ts +22 -13
- package/src/inspector.ts +129 -17
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +175 -58
- package/src/utils/compute-required-schemas.ts +49 -0
- package/src/utils/contract-hashes.test.ts +528 -0
- package/src/utils/contract-hashes.ts +290 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/ensure-function-metadata.ts +324 -7
- package/src/utils/extract-function-name.ts +101 -351
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +34 -20
- package/src/utils/filter-inspector-state.ts +140 -31
- package/src/utils/get-property-value.ts +42 -4
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +129 -67
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +10 -10
- package/src/utils/post-process.ts +283 -43
- package/src/utils/resolve-external-package.ts +42 -0
- package/src/utils/resolve-function-types.ts +42 -0
- package/src/utils/resolve-identifier.ts +46 -0
- package/src/utils/resolve-versions.test.ts +249 -0
- package/src/utils/resolve-versions.ts +105 -0
- package/src/utils/schema-generator.ts +329 -0
- package/src/utils/serialize-inspector-state.ts +163 -40
- package/src/utils/serialize-mcp-json.ts +145 -0
- package/src/utils/serialize-middleware-groups-meta.ts +33 -0
- package/src/utils/serialize-openapi-json.ts +277 -0
- package/src/utils/serialize-permissions-groups-meta.ts +35 -0
- package/src/utils/test-data/inspector-state.json +69 -66
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
- package/src/utils/workflow/graph/index.ts +5 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
- package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
- package/src/visit.ts +12 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-forge-credential.d.ts +0 -8
- package/dist/add/add-forge-credential.js +0 -77
- package/dist/add/add-forge-node.d.ts +0 -7
- package/dist/add/add-forge-node.js +0 -77
- package/dist/add/add-mcp-tool.d.ts +0 -2
- package/dist/add/add-mcp-tool.js +0 -81
- package/dist/utils/extract-service-metadata.d.ts +0 -19
- package/dist/utils/extract-service-metadata.js +0 -244
- package/dist/utils/write-service-metadata.d.ts +0 -13
- package/dist/utils/write-service-metadata.js +0 -37
- package/src/add/add-forge-credential.ts +0 -119
- package/src/add/add-forge-node.ts +0 -132
- package/src/add/add-mcp-tool.ts +0 -141
- package/src/utils/extract-service-metadata.ts +0 -353
- package/src/utils/write-service-metadata.ts +0 -51
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { FunctionsMeta, JSONValue, parseVersionedId } from '@pikku/core'
|
|
2
|
+
import { TypesMap } from '../types-map.js'
|
|
3
|
+
import { ErrorCode } from '../error-codes.js'
|
|
4
|
+
import { canonicalJSON, hashString } from './hash.js'
|
|
5
|
+
|
|
6
|
+
export type ContractEntry = {
|
|
7
|
+
functionKey: string
|
|
8
|
+
version: number
|
|
9
|
+
contractHash: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type VersionValidateError = {
|
|
13
|
+
code: ErrorCode
|
|
14
|
+
message: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type VersionManifestEntry = {
|
|
18
|
+
latest: number
|
|
19
|
+
versions: Record<string, string>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type VersionManifest = {
|
|
23
|
+
manifestVersion: 1
|
|
24
|
+
contracts: Record<string, VersionManifestEntry>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createEmptyManifest(): VersionManifest {
|
|
28
|
+
return {
|
|
29
|
+
manifestVersion: 1,
|
|
30
|
+
contracts: {},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function serializeManifest(manifest: VersionManifest): string {
|
|
35
|
+
const sortedContracts: Record<string, VersionManifestEntry> = {}
|
|
36
|
+
|
|
37
|
+
for (const key of Object.keys(manifest.contracts).sort()) {
|
|
38
|
+
const entry = manifest.contracts[key]
|
|
39
|
+
const sortedVersions: Record<string, string> = {}
|
|
40
|
+
const numericKeys = Object.keys(entry.versions)
|
|
41
|
+
.map(Number)
|
|
42
|
+
.sort((a, b) => a - b)
|
|
43
|
+
for (const vk of numericKeys) {
|
|
44
|
+
sortedVersions[String(vk)] = entry.versions[String(vk)]
|
|
45
|
+
}
|
|
46
|
+
sortedContracts[key] = {
|
|
47
|
+
latest: entry.latest,
|
|
48
|
+
versions: sortedVersions,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const sorted: VersionManifest = {
|
|
53
|
+
manifestVersion: manifest.manifestVersion,
|
|
54
|
+
contracts: sortedContracts,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return JSON.stringify(sorted, null, 2) + '\n'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function computeContractHash(data: {
|
|
61
|
+
functionKey: string
|
|
62
|
+
inputSchema: unknown
|
|
63
|
+
outputSchema: unknown
|
|
64
|
+
}): string {
|
|
65
|
+
return hashString(canonicalJSON(data), 16)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveSchema(
|
|
69
|
+
typeNames: string[] | null | undefined,
|
|
70
|
+
allSchemas: Record<string, JSONValue>,
|
|
71
|
+
typesMap: TypesMap
|
|
72
|
+
): unknown {
|
|
73
|
+
if (!typeNames) {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
const filtered = typeNames.filter((n) => n !== 'void')
|
|
77
|
+
if (filtered.length === 0) {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
const parts: unknown[] = []
|
|
81
|
+
for (const name of filtered) {
|
|
82
|
+
let key: string
|
|
83
|
+
try {
|
|
84
|
+
key = typesMap.getUniqueName(name)
|
|
85
|
+
} catch {
|
|
86
|
+
key = name
|
|
87
|
+
}
|
|
88
|
+
const schema = allSchemas[key]
|
|
89
|
+
if (schema) {
|
|
90
|
+
parts.push(schema)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (parts.length === 0) {
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
return parts.length === 1 ? parts[0] : parts
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function buildCurrentContracts(
|
|
100
|
+
functionsMeta: FunctionsMeta,
|
|
101
|
+
allSchemas: Record<string, JSONValue>,
|
|
102
|
+
typesMap: TypesMap
|
|
103
|
+
): Map<string, ContractEntry> {
|
|
104
|
+
const result = new Map<string, ContractEntry>()
|
|
105
|
+
|
|
106
|
+
for (const [funcId, meta] of Object.entries(functionsMeta)) {
|
|
107
|
+
if (meta.remote === true) {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const parsed = parseVersionedId(funcId)
|
|
112
|
+
const functionKey = parsed.baseName
|
|
113
|
+
const version = parsed.version ?? meta.version ?? 1
|
|
114
|
+
|
|
115
|
+
const inputSchema = resolveSchema(meta.inputs, allSchemas, typesMap)
|
|
116
|
+
const outputSchema = resolveSchema(meta.outputs, allSchemas, typesMap)
|
|
117
|
+
|
|
118
|
+
const contractHash = computeContractHash({
|
|
119
|
+
functionKey,
|
|
120
|
+
inputSchema,
|
|
121
|
+
outputSchema,
|
|
122
|
+
})
|
|
123
|
+
result.set(funcId, { functionKey, version, contractHash })
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function computeContractHashes(
|
|
130
|
+
allSchemas: Record<string, JSONValue>,
|
|
131
|
+
typesMap: TypesMap,
|
|
132
|
+
functionsMeta: FunctionsMeta
|
|
133
|
+
): Map<string, ContractEntry> {
|
|
134
|
+
const contracts = buildCurrentContracts(functionsMeta, allSchemas, typesMap)
|
|
135
|
+
|
|
136
|
+
for (const [funcId, entry] of contracts) {
|
|
137
|
+
const meta = functionsMeta[funcId]
|
|
138
|
+
if (meta) {
|
|
139
|
+
meta.contractHash = entry.contractHash
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return contracts
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function groupByFunctionKey(
|
|
147
|
+
contracts: Map<string, ContractEntry>
|
|
148
|
+
): Map<string, ContractEntry[]> {
|
|
149
|
+
const grouped = new Map<string, ContractEntry[]>()
|
|
150
|
+
for (const entry of contracts.values()) {
|
|
151
|
+
const existing = grouped.get(entry.functionKey) ?? []
|
|
152
|
+
existing.push(entry)
|
|
153
|
+
grouped.set(entry.functionKey, existing)
|
|
154
|
+
}
|
|
155
|
+
return grouped
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function validateContracts(
|
|
159
|
+
manifest: VersionManifest,
|
|
160
|
+
currentContracts: Map<string, ContractEntry>
|
|
161
|
+
): { valid: boolean; errors: VersionValidateError[] } {
|
|
162
|
+
const errors: VersionValidateError[] = []
|
|
163
|
+
const grouped = groupByFunctionKey(currentContracts)
|
|
164
|
+
const reportedKeys = new Set<string>()
|
|
165
|
+
|
|
166
|
+
for (const [functionKey, entries] of grouped) {
|
|
167
|
+
const manifestEntry = manifest.contracts[functionKey]
|
|
168
|
+
if (!manifestEntry) {
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const { version, contractHash } of entries) {
|
|
173
|
+
const existingHash = manifestEntry.versions[String(version)]
|
|
174
|
+
|
|
175
|
+
if (existingHash !== undefined) {
|
|
176
|
+
if (existingHash !== contractHash) {
|
|
177
|
+
reportedKeys.add(`${functionKey}@${version}`)
|
|
178
|
+
errors.push({
|
|
179
|
+
code: ErrorCode.FUNCTION_VERSION_MODIFIED,
|
|
180
|
+
message: `Contract for ${functionKey}@v${version} has changed (recorded: ${existingHash}, current: ${contractHash}). Existing versions are immutable.`,
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
if (version <= manifestEntry.latest) {
|
|
185
|
+
errors.push({
|
|
186
|
+
code: ErrorCode.VERSION_REGRESSION_OR_CONFLICT,
|
|
187
|
+
message: `Version ${version} for ${functionKey} is <= latest (${manifestEntry.latest}) but not recorded. Possible merge conflict.`,
|
|
188
|
+
})
|
|
189
|
+
} else if (version > manifestEntry.latest + 1) {
|
|
190
|
+
errors.push({
|
|
191
|
+
code: ErrorCode.VERSION_GAP_NOT_ALLOWED,
|
|
192
|
+
message: `Version ${version} for ${functionKey} skips versions. Latest is ${manifestEntry.latest}, next must be ${manifestEntry.latest + 1}.`,
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (const [functionKey, manifestEntry] of Object.entries(
|
|
200
|
+
manifest.contracts
|
|
201
|
+
)) {
|
|
202
|
+
const latestHash = manifestEntry.versions[String(manifestEntry.latest)]
|
|
203
|
+
const currentEntries = grouped.get(functionKey)
|
|
204
|
+
if (!currentEntries) {
|
|
205
|
+
continue
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const currentLatest = currentEntries.find(
|
|
209
|
+
(e) => e.version === manifestEntry.latest
|
|
210
|
+
)
|
|
211
|
+
if (
|
|
212
|
+
currentLatest &&
|
|
213
|
+
currentLatest.contractHash !== latestHash &&
|
|
214
|
+
!reportedKeys.has(`${functionKey}@${manifestEntry.latest}`)
|
|
215
|
+
) {
|
|
216
|
+
errors.push({
|
|
217
|
+
code: ErrorCode.CONTRACT_CHANGED_REQUIRES_BUMP,
|
|
218
|
+
message: `Contract for ${functionKey} changed. Set \`version: ${manifestEntry.latest + 1}\` on the function or run 'pikku versions update'.`,
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
for (const [functionKey, manifestEntry] of Object.entries(
|
|
224
|
+
manifest.contracts
|
|
225
|
+
)) {
|
|
226
|
+
const numericKeys = Object.keys(manifestEntry.versions).map(Number)
|
|
227
|
+
if (numericKeys.length === 0) {
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
const maxVersion = Math.max(...numericKeys)
|
|
231
|
+
if (manifestEntry.latest !== maxVersion) {
|
|
232
|
+
errors.push({
|
|
233
|
+
code: ErrorCode.MANIFEST_INTEGRITY_ERROR,
|
|
234
|
+
message: `Manifest integrity error for ${functionKey}: latest field (${manifestEntry.latest}) inconsistent with version keys (max: ${maxVersion}).`,
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { valid: errors.length === 0, errors }
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function updateManifest(
|
|
243
|
+
existing: VersionManifest,
|
|
244
|
+
currentContracts: Map<string, ContractEntry>
|
|
245
|
+
): VersionManifest {
|
|
246
|
+
const manifest: VersionManifest = {
|
|
247
|
+
manifestVersion: existing.manifestVersion,
|
|
248
|
+
contracts: JSON.parse(JSON.stringify(existing.contracts)),
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const grouped = groupByFunctionKey(currentContracts)
|
|
252
|
+
|
|
253
|
+
for (const [functionKey, entries] of grouped) {
|
|
254
|
+
if (!manifest.contracts[functionKey]) {
|
|
255
|
+
manifest.contracts[functionKey] = { latest: 0, versions: {} }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const entry = manifest.contracts[functionKey]
|
|
259
|
+
for (const { version, contractHash } of entries) {
|
|
260
|
+
entry.versions[String(version)] = contractHash
|
|
261
|
+
entry.latest = Math.max(entry.latest, version)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return manifest
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function extractContractsFromMeta(
|
|
269
|
+
functionsMeta: FunctionsMeta
|
|
270
|
+
): Map<string, ContractEntry> {
|
|
271
|
+
const result = new Map<string, ContractEntry>()
|
|
272
|
+
|
|
273
|
+
for (const [funcId, meta] of Object.entries(functionsMeta)) {
|
|
274
|
+
if (meta.remote === true || !meta.contractHash) {
|
|
275
|
+
continue
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const parsed = parseVersionedId(funcId)
|
|
279
|
+
const functionKey = parsed.baseName
|
|
280
|
+
const version = parsed.version ?? meta.version ?? 1
|
|
281
|
+
|
|
282
|
+
result.set(funcId, {
|
|
283
|
+
functionKey,
|
|
284
|
+
version,
|
|
285
|
+
contractHash: meta.contractHash,
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return result
|
|
290
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { TypesMap } from '../types-map.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NOTE: Code generation normally belongs in @pikku/cli, not the inspector.
|
|
5
|
+
* This is here because the schema generator needs the custom types content
|
|
6
|
+
* as a virtual TypeScript source file (in-memory, no disk write) so that
|
|
7
|
+
* ts-json-schema-generator can discover inline/custom types from typesMap.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export function sanitizeTypeName(name: string): string {
|
|
11
|
+
return name.replace(/[^a-zA-Z0-9_$]/g, '_')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function generateCustomTypes(
|
|
15
|
+
typesMap: TypesMap,
|
|
16
|
+
requiredTypes: Set<string>
|
|
17
|
+
) {
|
|
18
|
+
const typeDeclarations = Array.from(typesMap.customTypes.entries())
|
|
19
|
+
.filter(([_name, { type }]) => {
|
|
20
|
+
const hasUndefinedGeneric =
|
|
21
|
+
/\b(Name|In|Out|Key)\b/.test(type) && /\[.*\]/.test(type)
|
|
22
|
+
return !hasUndefinedGeneric
|
|
23
|
+
})
|
|
24
|
+
.map(([originalName, { type, references }]) => {
|
|
25
|
+
const name = sanitizeTypeName(originalName)
|
|
26
|
+
references.forEach((refName) => {
|
|
27
|
+
if (refName !== '__object' && !refName.startsWith('__object_')) {
|
|
28
|
+
requiredTypes.add(refName)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const typeString = type
|
|
33
|
+
const typeNameRegex = /\b[A-Z][a-zA-Z0-9]*\b/g
|
|
34
|
+
const potentialTypes = typeString.match(typeNameRegex) || []
|
|
35
|
+
|
|
36
|
+
potentialTypes.forEach((typeName) => {
|
|
37
|
+
if (
|
|
38
|
+
typeString.includes(`"${typeName}"`) ||
|
|
39
|
+
[
|
|
40
|
+
'Pick',
|
|
41
|
+
'Omit',
|
|
42
|
+
'Partial',
|
|
43
|
+
'Required',
|
|
44
|
+
'Record',
|
|
45
|
+
'Readonly',
|
|
46
|
+
].includes(typeName)
|
|
47
|
+
) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const typeMeta = typesMap.getTypeMeta(typeName)
|
|
53
|
+
if (typeMeta.path) {
|
|
54
|
+
requiredTypes.add(typeMeta.originalName)
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// Type not found in map (ambient/builtin type)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
if (name === type) return null
|
|
62
|
+
return `export type ${name} = ${type}`
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const importsByPath = new Map<string, Set<string>>()
|
|
66
|
+
for (const typeName of requiredTypes) {
|
|
67
|
+
try {
|
|
68
|
+
const typeMeta = typesMap.getTypeMeta(typeName)
|
|
69
|
+
if (typeMeta.path) {
|
|
70
|
+
if (!importsByPath.has(typeMeta.path)) {
|
|
71
|
+
importsByPath.set(typeMeta.path, new Set())
|
|
72
|
+
}
|
|
73
|
+
importsByPath.get(typeMeta.path)!.add(typeMeta.originalName)
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// Type not found in map
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const importLines = Array.from(importsByPath.entries())
|
|
81
|
+
.map(
|
|
82
|
+
([path, types]) =>
|
|
83
|
+
`import type { ${Array.from(types).join(', ')} } from '${path}'`
|
|
84
|
+
)
|
|
85
|
+
.join('\n')
|
|
86
|
+
|
|
87
|
+
return `${importLines}\n\n${typeDeclarations.filter(Boolean).join('\n')}`
|
|
88
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import type { SchemaVendor, InspectorLogger } from '../types.js'
|
|
3
|
+
import { ErrorCode } from '../error-codes.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Detect the schema vendor by tracing the type back to its library origin.
|
|
7
|
+
* This handles locally-defined schemas like `export const MySchema = z.object({...})`
|
|
8
|
+
* by checking where the type itself originates from.
|
|
9
|
+
*
|
|
10
|
+
* Supports multiple schema libraries in the same project (e.g., during migration
|
|
11
|
+
* from one library to another).
|
|
12
|
+
*/
|
|
13
|
+
export const detectSchemaVendor = (
|
|
14
|
+
identifier: ts.Identifier,
|
|
15
|
+
checker: ts.TypeChecker
|
|
16
|
+
): SchemaVendor => {
|
|
17
|
+
const type = checker.getTypeAtLocation(identifier)
|
|
18
|
+
if (!type) return 'unknown'
|
|
19
|
+
|
|
20
|
+
// Check the type's symbol declarations to find the library origin
|
|
21
|
+
const checkTypeOrigin = (t: ts.Type): SchemaVendor | null => {
|
|
22
|
+
const symbol = t.getSymbol() || t.aliasSymbol
|
|
23
|
+
if (symbol) {
|
|
24
|
+
const decls = symbol.getDeclarations()
|
|
25
|
+
if (decls) {
|
|
26
|
+
for (const decl of decls) {
|
|
27
|
+
const fileName = decl.getSourceFile().fileName
|
|
28
|
+
if (fileName.includes('node_modules/zod')) return 'zod'
|
|
29
|
+
if (fileName.includes('node_modules/valibot')) return 'valibot'
|
|
30
|
+
if (fileName.includes('node_modules/arktype')) return 'arktype'
|
|
31
|
+
if (fileName.includes('node_modules/@effect/schema')) return 'effect'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check base types for class/interface hierarchies
|
|
37
|
+
const baseTypes = t.getBaseTypes?.()
|
|
38
|
+
if (baseTypes) {
|
|
39
|
+
for (const baseType of baseTypes) {
|
|
40
|
+
const result = checkTypeOrigin(baseType)
|
|
41
|
+
if (result) return result
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const vendor = checkTypeOrigin(type)
|
|
49
|
+
if (vendor) return vendor
|
|
50
|
+
|
|
51
|
+
// Fallback: check type arguments (for generic types like z.ZodObject<...>)
|
|
52
|
+
if ((type as ts.TypeReference).typeArguments) {
|
|
53
|
+
for (const arg of (type as ts.TypeReference).typeArguments || []) {
|
|
54
|
+
const result = checkTypeOrigin(arg)
|
|
55
|
+
if (result) return result
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return 'unknown'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detect schema vendor and log a fatal error if unknown.
|
|
64
|
+
* Returns the vendor if successful, or undefined if unknown (after logging error).
|
|
65
|
+
*
|
|
66
|
+
* @param identifier - The TypeScript identifier for the schema variable
|
|
67
|
+
* @param checker - TypeScript type checker
|
|
68
|
+
* @param logger - Inspector logger for error reporting
|
|
69
|
+
* @param context - Description of what the schema is for (e.g., "Credential 'myCredential'")
|
|
70
|
+
* @param sourceFile - Source file path for error message
|
|
71
|
+
*/
|
|
72
|
+
export const detectSchemaVendorOrError = (
|
|
73
|
+
identifier: ts.Identifier,
|
|
74
|
+
checker: ts.TypeChecker,
|
|
75
|
+
logger: InspectorLogger,
|
|
76
|
+
context: string,
|
|
77
|
+
sourceFile: string
|
|
78
|
+
): Exclude<SchemaVendor, 'unknown'> | undefined => {
|
|
79
|
+
const vendor = detectSchemaVendor(identifier, checker)
|
|
80
|
+
if (vendor === 'unknown') {
|
|
81
|
+
logger.critical(
|
|
82
|
+
ErrorCode.INLINE_SCHEMA,
|
|
83
|
+
`${context} schema vendor could not be determined from '${sourceFile}'. ` +
|
|
84
|
+
`Supported vendors: zod, valibot, arktype, @effect/schema. ` +
|
|
85
|
+
`Ensure your schema is imported from a supported validation library.`
|
|
86
|
+
)
|
|
87
|
+
return undefined
|
|
88
|
+
}
|
|
89
|
+
return vendor
|
|
90
|
+
}
|