Unified · Split
| @@ -29,6 +29,33 @@ const OPENAPI_SPEC_CACHE_TTL_MS = 5 * 60 * 60_000; · 28 unmodified lines | |||
| 29 | /** Cache version - bump when tool snapshot/registry/type-hint semantics change. */ | 29 | /** Cache version - bump when tool snapshot/registry/type-hint semantics change. */ |
| 30 | const TOOL_SOURCE_CACHE_VERSION = "v25"; | 30 | const TOOL_SOURCE_CACHE_VERSION = "v25"; |
| 31 | 31 | ||
| 32 | function toPreparedOpenApiSpec(value: unknown): PreparedOpenApiSpec | null { | ||
| 33 | const record = asPayload(value); | ||
| 34 | if (!Array.isArray(record.servers)) { | ||
| 35 | return null; | ||
| 36 | } | ||
| 37 | if (!record.paths || typeof record.paths !== "object" || Array.isArray(record.paths)) { | ||
| 38 | return null; | ||
| 39 | } | ||
| 40 | |||
| 41 | const servers = record.servers.filter((entry): entry is string => typeof entry === "string"); | ||
| 42 | const warnings = Array.isArray(record.warnings) | ||
| 43 | ? record.warnings.filter((entry): entry is string => typeof entry === "string") | ||
| 44 | : []; | ||
| 45 | const dtsStatus = | ||
| 46 | record.dtsStatus === "ready" || record.dtsStatus === "failed" || record.dtsStatus === "skipped" | ||
| 47 | ? record.dtsStatus | ||
| 48 | : undefined; | ||
| 49 | |||
| 50 | return { | ||
| 51 | servers, | ||
| 52 | paths: asPayload(record.paths), | ||
| 53 | warnings, | ||
| 54 | dts: typeof record.dts === "string" ? record.dts : undefined, | ||
| 55 | dtsStatus, | ||
| 56 | }; | ||
| 57 | } | ||
| 58 | |||
| 32 | export function sourceSignature(workspaceId: string, sources: Array<{ id: string; updatedAt: number; enabled: boolean }>): string { | 59 | export function sourceSignature(workspaceId: string, sources: Array<{ id: string; updatedAt: number; enabled: boolean }>): string { |
| 33 | const parts = sources | 60 | const parts = sources |
| 34 | .map((source) => `${source.id}:${source.updatedAt}:${source.enabled ? 1 : 0}`) | 61 | .map((source) => `${source.id}:${source.updatedAt}:${source.enabled ? 1 : 0}`) |
| @@ -235,9 +262,12 @@ async function loadCachedOpenApiSpec( · 200 unmodified lines | |||
| 235 | const blob = await ctx.storage.get(entry.storageId); | 262 | const blob = await ctx.storage.get(entry.storageId); |
| 236 | if (blob) { | 263 | if (blob) { |
| 237 | const json = await blob.text(); | 264 | const json = await blob.text(); |
| 238 | const prepared = JSON.parse(json) as PreparedOpenApiSpec; | 265 | const parsedResult = Result.try(() => JSON.parse(json)); |
| 239 | if (!includeDts || getDtsStatus(prepared) !== "skipped") { | 266 | if (parsedResult.isOk()) { |
| 240 | return prepared; | 267 | const prepared = toPreparedOpenApiSpec(parsedResult.value); |
| 268 | if (prepared && (!includeDts || getDtsStatus(prepared) !== "skipped")) { | ||
| 269 | return prepared; | ||
| 270 | } | ||
| 241 | } | 271 | } |
| 242 | } | 272 | } |
| 243 | } | 273 | } |