Schema.is, Schema.decode*, Schema.encode*, Schema.validate*, and Schema.parse* each compile a parser/guard from a schema. Calling them inside a function body rebuilds that function on every invocation. Adds executor/no-inline-schema-compile (oxlint plugin rule) that flags these calls when they appear inside a function body, with two severities: HIGH when the schema argument is itself an inline Schema.X(...) literal (both AST and compile rebuilt), MEDIUM when only the compiler is re-called. Fixes the 44 existing violations across 22 files by hoisting the compiled function (and where applicable, its schema literal) to module scope. Notable consolidations: - oauth-service.ts: 8 sites (5 decode + 3 encode of OAuthProviderState) collapse into two hoisted helpers - jwks-cache.ts, mcp-auth.ts: per-call guard compilation removed from JWT verify error path - React Add*Source / EditOpenApiSource components: shared decodeErrorMessage / isOpenApiSourceBindingValue hoisted migrate-connections.ts has one targeted oxlint-disable for a higher-order helper whose schema is a function parameter; the compile is hoisted into the outer closure but the rule (correctly) flags any Schema compiler call inside a function body.
Showing the changes to packages/plugins/openapi/src (4 of 25 files). Show all
Unified · Split
| @@ -86,15 +86,13 @@ export const OPENAPI_OAUTH_POPUP_NAME = "openapi-oauth"; · 85 unmodified lines | ||
| 86 | 86 | export const OPENAPI_OAUTH_CALLBACK_PATH = "/api/oauth/callback"; |
| 87 | 87 | |
| 88 | 88 | const ErrorMessage = Schema.Struct({ message: Schema.String }); |
| 89 | +const decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage); | |
| 89 | 90 | |
| 90 | 91 | const errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string => |
| 91 | − Option.match( | |
| 92 | − Option.flatMap(Exit.findErrorOption(exit), Schema.decodeUnknownOption(ErrorMessage)), | |
| 93 | − { | |
| 94 | − onNone: () => fallback, | |
| 95 | − onSome: ({ message }) => message, | |
| 96 | − }, | |
| 97 | − ); | |
| 92 | + Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), { | |
| 93 | + onNone: () => fallback, | |
| 94 | + onSome: ({ message }) => message, | |
| 95 | + }); | |
| 98 | 96 | |
| 99 | 97 | const substituteUrlVariables = (url: string, values: Record<string, string>): string => { |
| 100 | 98 | let out = url; |
| @@ -51,15 +51,14 @@ import { · 50 unmodified lines | ||
| 51 | 51 | } from "../sdk/types"; |
| 52 | 52 | |
| 53 | 53 | const ErrorMessage = Schema.Struct({ message: Schema.String }); |
| 54 | +const decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage); | |
| 55 | +const isOpenApiSourceBindingValue = Schema.is(OpenApiSourceBindingValue); | |
| 54 | 56 | |
| 55 | 57 | const errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string => |
| 56 | − Option.match( | |
| 57 | − Option.flatMap(Exit.findErrorOption(exit), Schema.decodeUnknownOption(ErrorMessage)), | |
| 58 | − { | |
| 59 | − onNone: () => fallback, | |
| 60 | − onSome: ({ message }) => message, | |
| 61 | − }, | |
| 62 | − ); | |
| 58 | + Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), { | |
| 59 | + onNone: () => fallback, | |
| 60 | + onSome: ({ message }) => message, | |
| 61 | + }); | |
| 63 | 62 | |
| 64 | 63 | type SlotDef = |
| 65 | 64 | | { |
| @@ -137,12 +136,12 @@ const effectiveBindingForScope = ( · 71 unmodified lines | ||
| 137 | 136 | const isSecretBindingValue = ( |
| 138 | 137 | value: unknown, |
| 139 | 138 | ): value is Extract<OpenApiSourceBindingValueType, { readonly kind: "secret" }> => |
| 140 | − Schema.is(OpenApiSourceBindingValue)(value) && value.kind === "secret"; | |
| 139 | + isOpenApiSourceBindingValue(value) && value.kind === "secret"; | |
| 141 | 140 | |
| 142 | 141 | const isConnectionBindingValue = ( |
| 143 | 142 | value: unknown, |
| 144 | 143 | ): value is Extract<OpenApiSourceBindingValueType, { readonly kind: "connection" }> => |
| 145 | − Schema.is(OpenApiSourceBindingValue)(value) && value.kind === "connection"; | |
| 144 | + isOpenApiSourceBindingValue(value) && value.kind === "connection"; | |
| 146 | 145 | |
| 147 | 146 | export default function EditOpenApiSource(props: { |
| 148 | 147 | readonly sourceId: string; |
| @@ -33,6 +33,7 @@ const JsonNameBody = Schema.fromJsonString( · 32 unmodified lines | ||
| 33 | 33 | name: Schema.String, |
| 34 | 34 | }), |
| 35 | 35 | ); |
| 36 | +const decodeJsonNameBody = Schema.decodeUnknownSync(JsonNameBody); | |
| 36 | 37 | |
| 37 | 38 | const memoryProvider: SecretProvider = (() => { |
| 38 | 39 | const store = new Map<string, string>(); |
| @@ -216,7 +217,7 @@ describe("OpenAPI non-JSON request body dispatch", () => { · 177 unmodified lines | ||
| 216 | 217 | expect(captured.contentType).toBe("text/xml"); |
| 217 | 218 | const body = captured.body.toString("utf8"); |
| 218 | 219 | expect(body).not.toBe("[object Object]"); |
| 219 | − expect(Schema.decodeUnknownSync(JsonNameBody)(body)).toEqual({ name: "Acme" }); | |
| 220 | + expect(decodeJsonNameBody(body)).toEqual({ name: "Acme" }); | |
| 220 | 221 | }), |
| 221 | 222 | ); |
| 222 | 223 | |
| @@ -364,7 +365,7 @@ describe("OpenAPI non-JSON request body dispatch", () => { · 141 unmodified lines | ||
| 364 | 365 | ); |
| 365 | 366 | |
| 366 | 367 | expect(captured.contentType).toBe("application/json"); |
| 367 | − expect(Schema.decodeUnknownSync(JsonNameBody)(captured.body.toString("utf8"))).toEqual({ | |
| 368 | + expect(decodeJsonNameBody(captured.body.toString("utf8"))).toEqual({ | |
| 368 | 369 | name: "Acme", |
| 369 | 370 | }); |
| 370 | 371 | }), |
| @@ -287,8 +287,10 @@ const SourceNameStorageRow = Schema.Struct({ · 286 unmodified lines | ||
| 287 | 287 | }); |
| 288 | 288 | const decodeSourceNameStorageRow = Schema.decodeUnknownSync(SourceNameStorageRow); |
| 289 | 289 | |
| 290 | +const decodeStorageString = Schema.decodeUnknownSync(Schema.String); | |
| 291 | + | |
| 290 | 292 | const decodeStorageDate = (value: unknown): Date => |
| 291 | − value instanceof Date ? value : new Date(Schema.decodeUnknownSync(Schema.String)(value)); | |
| 293 | + value instanceof Date ? value : new Date(decodeStorageString(value)); | |
| 292 | 294 | |
| 293 | 295 | interface ChildRow { |
| 294 | 296 | readonly id: string; |