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 | export const OPENAPI_OAUTH_CALLBACK_PATH = "/api/oauth/callback"; | 86 | export const OPENAPI_OAUTH_CALLBACK_PATH = "/api/oauth/callback"; |
| 87 | 87 | ||
| 88 | const ErrorMessage = Schema.Struct({ message: Schema.String }); | 88 | const ErrorMessage = Schema.Struct({ message: Schema.String }); |
| 89 | const decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage); | ||
| 89 | 90 | ||
| 90 | const errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string => | 91 | const errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string => |
| 91 | Option.match( | 92 | Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), { |
| 92 | Option.flatMap(Exit.findErrorOption(exit), Schema.decodeUnknownOption(ErrorMessage)), | 93 | onNone: () => fallback, |
| 93 | { | 94 | onSome: ({ message }) => message, |
| 94 | onNone: () => fallback, | 95 | }); |
| 95 | onSome: ({ message }) => message, | ||
| 96 | }, | ||
| 97 | ); | ||
| 98 | 96 | ||
| 99 | const substituteUrlVariables = (url: string, values: Record<string, string>): string => { | 97 | const substituteUrlVariables = (url: string, values: Record<string, string>): string => { |
| 100 | let out = url; | 98 | let out = url; |
| @@ -51,15 +51,14 @@ import { · 50 unmodified lines | |||
| 51 | } from "../sdk/types"; | 51 | } from "../sdk/types"; |
| 52 | 52 | ||
| 53 | const ErrorMessage = Schema.Struct({ message: Schema.String }); | 53 | const ErrorMessage = Schema.Struct({ message: Schema.String }); |
| 54 | const decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage); | ||
| 55 | const isOpenApiSourceBindingValue = Schema.is(OpenApiSourceBindingValue); | ||
| 54 | 56 | ||
| 55 | const errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string => | 57 | const errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string => |
| 56 | Option.match( | 58 | Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), { |
| 57 | Option.flatMap(Exit.findErrorOption(exit), Schema.decodeUnknownOption(ErrorMessage)), | 59 | onNone: () => fallback, |
| 58 | { | 60 | onSome: ({ message }) => message, |
| 59 | onNone: () => fallback, | 61 | }); |
| 60 | onSome: ({ message }) => message, | ||
| 61 | }, | ||
| 62 | ); | ||
| 63 | 62 | ||
| 64 | type SlotDef = | 63 | type SlotDef = |
| 65 | | { | 64 | | { |
| @@ -137,12 +136,12 @@ const effectiveBindingForScope = ( · 71 unmodified lines | |||
| 137 | const isSecretBindingValue = ( | 136 | const isSecretBindingValue = ( |
| 138 | value: unknown, | 137 | value: unknown, |
| 139 | ): value is Extract<OpenApiSourceBindingValueType, { readonly kind: "secret" }> => | 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 | const isConnectionBindingValue = ( | 141 | const isConnectionBindingValue = ( |
| 143 | value: unknown, | 142 | value: unknown, |
| 144 | ): value is Extract<OpenApiSourceBindingValueType, { readonly kind: "connection" }> => | 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 | export default function EditOpenApiSource(props: { | 146 | export default function EditOpenApiSource(props: { |
| 148 | readonly sourceId: string; | 147 | readonly sourceId: string; |
| @@ -33,6 +33,7 @@ const JsonNameBody = Schema.fromJsonString( · 32 unmodified lines | |||
| 33 | name: Schema.String, | 33 | name: Schema.String, |
| 34 | }), | 34 | }), |
| 35 | ); | 35 | ); |
| 36 | const decodeJsonNameBody = Schema.decodeUnknownSync(JsonNameBody); | ||
| 36 | 37 | ||
| 37 | const memoryProvider: SecretProvider = (() => { | 38 | const memoryProvider: SecretProvider = (() => { |
| 38 | const store = new Map<string, string>(); | 39 | const store = new Map<string, string>(); |
| @@ -216,7 +217,7 @@ describe("OpenAPI non-JSON request body dispatch", () => { · 177 unmodified lines | |||
| 216 | expect(captured.contentType).toBe("text/xml"); | 217 | expect(captured.contentType).toBe("text/xml"); |
| 217 | const body = captured.body.toString("utf8"); | 218 | const body = captured.body.toString("utf8"); |
| 218 | expect(body).not.toBe("[object Object]"); | 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 | expect(captured.contentType).toBe("application/json"); | 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 | name: "Acme", | 369 | name: "Acme", |
| 369 | }); | 370 | }); |
| 370 | }), | 371 | }), |
| @@ -287,8 +287,10 @@ const SourceNameStorageRow = Schema.Struct({ · 286 unmodified lines | |||
| 287 | }); | 287 | }); |
| 288 | const decodeSourceNameStorageRow = Schema.decodeUnknownSync(SourceNameStorageRow); | 288 | const decodeSourceNameStorageRow = Schema.decodeUnknownSync(SourceNameStorageRow); |
| 289 | 289 | ||
| 290 | const decodeStorageString = Schema.decodeUnknownSync(Schema.String); | ||
| 291 | |||
| 290 | const decodeStorageDate = (value: unknown): Date => | 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 | interface ChildRow { | 295 | interface ChildRow { |
| 294 | readonly id: string; | 296 | readonly id: string; |