@lovable.dev/mcp-js 1.0.0 → 2.0.1
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/README.internal.md +3 -3
- package/README.md +36 -3
- package/dist/{authorize-DIYS7zBP.d.cts → authorize-9hTTG-qJ.d.cts} +1 -1
- package/dist/{authorize-CqYsQYfa.d.ts → authorize-DNUlPT7k.d.ts} +1 -1
- package/dist/base-7cq6HCPO.d.ts +36 -0
- package/dist/base-CHNFFrMG.d.cts +36 -0
- package/dist/cli/extract-manifest.cjs +7 -12
- package/dist/cli/extract-manifest.js +7 -12
- package/dist/{authorize-9wHgaqeQ.js → cors-BLWQyVL5.js} +79 -28
- package/dist/{authorize-DBk5GkN_.cjs → cors-BvJ-XM16.cjs} +90 -33
- package/dist/{errors-DSe96Ffd.js → errors-Bwd1L0Rf.js} +23 -8
- package/dist/{errors-CN6Da8iz.cjs → errors-Cr95rSkB.cjs} +23 -8
- package/dist/forwarded-B37IdmMX.cjs +31 -0
- package/dist/forwarded-BBXvFeh6.js +26 -0
- package/dist/index.cjs +38 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +38 -1
- package/dist/{io-Dx0nPc1-.d.cts → io-CDV3mud0.d.cts} +1 -1
- package/dist/{io-DP3icuxa.d.ts → io-D_5cuaEL.d.ts} +1 -1
- package/dist/mcp-BsvVuQvW.cjs +353 -0
- package/dist/mcp-CMph8_rD.js +348 -0
- package/dist/{package-C_8WAZRS.cjs → package-D8nZVoHm.cjs} +1 -1
- package/dist/{package-CLhOKbJP.js → package-DSwNopNG.js} +1 -1
- package/dist/protocols/mcp/index.cjs +1 -1
- package/dist/protocols/mcp/index.d.cts +3 -33
- package/dist/protocols/mcp/index.d.ts +3 -33
- package/dist/protocols/mcp/index.js +1 -1
- package/dist/protocols/oauth-metadata.cjs +27 -10
- package/dist/protocols/oauth-metadata.d.cts +4 -3
- package/dist/protocols/oauth-metadata.d.ts +4 -3
- package/dist/protocols/oauth-metadata.js +21 -4
- package/dist/schema-CW1jKvio.js +65 -0
- package/dist/schema-D3vClxb7.cjs +76 -0
- package/dist/stacks/supabase/index.cjs +6 -6
- package/dist/stacks/supabase/index.d.cts +1 -1
- package/dist/stacks/supabase/index.d.ts +1 -1
- package/dist/stacks/supabase/index.js +4 -4
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.d.cts +1 -1
- package/dist/stacks/supabase/vite.d.ts +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +5 -5
- package/dist/stacks/tanstack/index.d.cts +2 -2
- package/dist/stacks/tanstack/index.d.ts +2 -2
- package/dist/stacks/tanstack/index.js +4 -4
- package/dist/stacks/tanstack/vite.cjs +8 -4
- package/dist/stacks/tanstack/vite.d.cts +4 -1
- package/dist/stacks/tanstack/vite.d.ts +4 -1
- package/dist/stacks/tanstack/vite.js +8 -4
- package/dist/{types-C0Wgm9zp.d.cts → types-3CZPz364.d.cts} +65 -29
- package/dist/{types-C0Wgm9zp.d.ts → types-3CZPz364.d.ts} +65 -29
- package/package.json +5 -1
- package/dist/forwarded-Bhwcj22u.cjs +0 -48
- package/dist/forwarded-BlLF3dfc.js +0 -43
- package/dist/mcp-BDsG9I0t.js +0 -174
- package/dist/mcp-CS1MF39p.cjs +0 -179
package/README.internal.md
CHANGED
|
@@ -4,7 +4,7 @@ Architecture rationale, source layout, and dev scripts for contributors. The use
|
|
|
4
4
|
|
|
5
5
|
## Folder meaning
|
|
6
6
|
|
|
7
|
-
- **`protocols/`** is the wire layer — one folder per externally observable HTTP surface. `protocols/mcp` is the public MCP-over-HTTP surface; `protocols/oauth-metadata` serves the RFC 9728 protected-resource metadata required to bootstrap protected MCP clients. `protocols/gate.ts` is the CORS → auth → method gate
|
|
7
|
+
- **`protocols/`** is the wire layer — one folder per externally observable HTTP surface. `protocols/mcp` is the public MCP-over-HTTP surface; `protocols/oauth-metadata` serves the RFC 9728 protected-resource metadata required to bootstrap protected MCP clients. `protocols/gate.ts` is the shared CORS → auth → method gate both surfaces run behind.
|
|
8
8
|
- **`auth/`** (unpublished) is the cross-cutting OAuth middleware — bearer verification, issuer/JWKS discovery, the `auth.oauth.*` config namespace. It depends only on `core/`; `protocols/mcp` depends on it for the bearer gate, so it isn't a wire-format peer of `mcp` and doesn't live under `protocols/`.
|
|
9
9
|
- **`stacks/`** is the framework integration. Two are published: `stacks/tanstack/` (file-router routes emitted by a Vite plugin) and `stacks/supabase/` (a single Deno-served function with URL-suffix dispatch, emitted by the Supabase Vite plugin). Future entries (classic Vite, Cloudflare Workers, …) live alongside them. Each stack forwards to `protocols/{mcp,oauth-metadata}` directly — protocol logic stays shared, only ctx unwrapping and route emission differ.
|
|
10
10
|
- **`manifest/`** is the stack-agnostic snapshot writer. `lovable-mcp-extract-manifest` is one CLI for every stack: it asks Vite for the resolved config, finds any plugin whose name matches `@lovable.dev/mcp-js*`, and reads `mcpEntry` + `urlPath` off its `api`. TanStack publishes its configured route path (`/mcp`); Supabase publishes `/functions/v1/<functionName>`. The projection (`extract.ts` + `listing.ts`) is pure and derives from the same `defineMcp` definition the live `tools/list` serves, so the committed manifest and the served catalog can't drift.
|
|
@@ -79,7 +79,7 @@ The published `.d.ts` files import only from `zod` and `vite`. Three SDK concern
|
|
|
79
79
|
- **`ZodRawShape`, `ZodType`, `infer`** — sourced directly from `zod` (a peer dep). zod v3 and v4 both export all three at the top level; `ZodType` is non-deprecated in both versions with safe generic defaults. We re-export them from `core/types.ts` as `ZodRawShape` / `ZodSchema` / `ShapeOutput` (`ZodSchema` is a no-generic alias for `ZodType`). No dependency on `@modelcontextprotocol/sdk/server/zod-compat.js` types.
|
|
80
80
|
- **`ContentBlock` union** — `TextContent | ImageContent | AudioContent | EmbeddedResource | ResourceLink`, structurally matching MCP's wire format. Consumers can type custom content (`return { content: [<ImageContent>, <TextContent>] }`) without referencing the SDK.
|
|
81
81
|
|
|
82
|
-
The **runtime** still hands off to the MCP
|
|
82
|
+
The **runtime** still hands off to the MCP SDKs — `createMcpHandler` + `McpServer` come from `@modelcontextprotocol/server` (v2), and `objectFromShape` + `safeParseAsync` + `toJsonSchemaCompat` come from `@modelcontextprotocol/sdk`'s zod-compat modules for input validation and JSON-Schema serialization. Those are runtime dependencies, not type-surface dependencies.
|
|
83
83
|
|
|
84
84
|
### 8. Stale-route cleanup on every regen
|
|
85
85
|
|
|
@@ -141,7 +141,7 @@ src/
|
|
|
141
141
|
vite.ts # mcpPlugin (Vite-lifecycle trigger; exposes McpPluginApi with urlPath=/functions/v1/<functionName>)
|
|
142
142
|
index.ts # barrel
|
|
143
143
|
tests/
|
|
144
|
-
core/{content,define,errors,forwarded,logger,promise,supabase-config,url}.test.ts
|
|
144
|
+
core/{content,cors,define,errors,forwarded,logger,promise,supabase-config,url}.test.ts
|
|
145
145
|
auth/{claims,config,context,oauth,oauth-fallback,oauth-logging,oauth-metrics}.test.ts # OAuth config + bearer verification
|
|
146
146
|
metrics/{config,dispatch,otlp,recorder}.test.ts
|
|
147
147
|
protocols/
|
package/README.md
CHANGED
|
@@ -49,7 +49,13 @@ That's the whole authoring surface. No imperative server construction, no route
|
|
|
49
49
|
|
|
50
50
|
The table shows the default `path: "/mcp"`; `mcpPlugin({ path: "/api/public/mcp" })` moves the MCP route accordingly.
|
|
51
51
|
|
|
52
|
-
MCP (`POST <path>`, `/mcp` by default) is the wire format clients speak directly. All runtime routes import the same `defineMcp` result, so they stay in sync on which tools exist and which auth
|
|
52
|
+
MCP (`POST <path>`, `/mcp` by default) is the wire format clients speak directly. All runtime routes import the same `defineMcp` result, so they stay in sync on which tools exist and which auth and browser-origin policies protect them. If `defineMcp({ auth: ... })` is omitted, the handlers stay unauthenticated; if OAuth auth is configured, clients must pass `Authorization: Bearer <token>`. Rate limiting belongs at the app host or edge.
|
|
53
|
+
|
|
54
|
+
The MCP endpoint supports both protocol eras on one URL. Finalized `2026-07-28` clients send the version, client capabilities, and mirrored HTTP headers on every request; the server returns typed results plus cache metadata on discovery and list responses without initialization or protocol sessions. Initialization-era clients continue through a stateless compatibility path: `initialize`, `tools/list`, and `tools/call` work as independent requests, and the endpoint never mints `Mcp-Session-Id`. `GET` and `DELETE` return `405` because there is no standalone stream or session to manage.
|
|
55
|
+
|
|
56
|
+
`outputSchema` accepts either the existing object-shape shorthand (`{ value: z.string() }`) or a JSON-Schema-representable Zod schema such as `z.array(z.string())`; `structuredContent` may be any JSON value. The server returns the schema's parsed output, so coercions are reflected in the result; transforms with no truthful JSON Schema are rejected when `defineMcp` runs. Modern clients receive the natural schema and value. The compatibility path wraps non-object schemas and values under `result` for initialization-era clients whose wire format requires an object root.
|
|
57
|
+
|
|
58
|
+
Browser-origin checks apply to the MCP and OAuth metadata routes before authentication. Omit `allowedOrigins` to allow requests with no `Origin` header (Go, desktop, CLI, and server clients) plus same-origin browser requests. Set `allowedOrigins: ["https://client.example.com"]` for exact additional origins, or `allowedOrigins: "any"` to intentionally restore wildcard CORS. Exact and same origins are reflected with `Vary: Origin`; other browser origins receive `403` without an OAuth challenge. Reverse-proxy adapters compare against the effective public origin after applying only their configured trusted forwarded headers.
|
|
53
59
|
|
|
54
60
|
The OAuth metadata route is emitted by default and returns `404` until OAuth auth is configured. Disable it with `mcpPlugin({ protectedResourceMetadataRoute: false })` only if the app owns `/.well-known/oauth-protected-resource` itself.
|
|
55
61
|
|
|
@@ -160,6 +166,23 @@ and no `node:async_hooks` dependency, so it works on any runtime and is safe
|
|
|
160
166
|
under concurrency by construction. A handler that hands `ctx` to deferred work (a
|
|
161
167
|
detached timer, an un-awaited promise) keeps full access to it.
|
|
162
168
|
|
|
169
|
+
Beyond auth, `ToolContext` carries the per-request runtime surface:
|
|
170
|
+
|
|
171
|
+
| Member | Purpose |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `signal` | An `AbortSignal` that aborts when the client cancels the request or disconnects. Pass it to `fetch` and other cancelable APIs, or check `signal.aborted` between steps of long work. |
|
|
174
|
+
| `client` | What the calling client declared about itself (`McpClientInfo`): `protocol` (`"2026-07-28"` or `"legacy"`), and for modern requests the envelope's `info`, `capabilities`, and `extensions`. Unverified client input — use it to adapt output, never to authorize. |
|
|
175
|
+
| `progress(update)` | Sends a `notifications/progress` frame (`{ progress, total?, message? }`) for long-running tools. A no-op unless the client requested progress by sending `_meta.progressToken`; safe to call unconditionally. |
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
handler: async ({ url }, ctx) => {
|
|
179
|
+
await ctx.progress({ progress: 0, total: 2, message: "fetching" });
|
|
180
|
+
const res = await fetch(url, { signal: ctx.signal });
|
|
181
|
+
await ctx.progress({ progress: 1, total: 2, message: "processing" });
|
|
182
|
+
return { content: [{ type: "text", text: await res.text() }] };
|
|
183
|
+
};
|
|
184
|
+
```
|
|
185
|
+
|
|
163
186
|
**Security contract for the bearer token.** `getToken()` is the only way to read
|
|
164
187
|
the raw token. `ToolContext` holds the verified context in a private field, so the
|
|
165
188
|
credential can't be read off the instance via `JSON.stringify`, object spread, or
|
|
@@ -205,9 +228,19 @@ The request-derived `resource` default trusts the incoming `Host` to name this s
|
|
|
205
228
|
|
|
206
229
|
Use `auth.oauth.issuer(...)`, set `resource` or `acceptedAudiences` to anchor the accepted audience, and optionally set `jwksUri` and `requiredScopes`. For Supabase project auth, set `acceptedAudiences: "authenticated"`, keep app/business checks in app code, and forward `ctx.getToken()` to Supabase for RLS-backed data access.
|
|
207
230
|
|
|
231
|
+
## Security posture
|
|
232
|
+
|
|
233
|
+
For anyone evaluating an app that exposes an MCP server with this package:
|
|
234
|
+
|
|
235
|
+
- **Verification runs in the app runtime.** When `defineMcp({ auth })` configures OAuth, the emitted TanStack route or Supabase Edge Function verifies the bearer before dispatching a tool. Clients call the app's published endpoint; the SDK does not relay MCP calls through a shared Lovable MCP protocol gateway, although a Lovable-hosted TanStack app still receives ordinary web traffic through Lovable's hosting proxy. The SDK may fetch issuer metadata and JWKS. When default usage telemetry is emitted, it sends the operational fields described below, including the verified token `sub`; it never sends the bearer, full claims, tool arguments, or tool results.
|
|
236
|
+
- **No protocol sessions.** Every HTTP request gets a fresh MCP server, auth context, and tool callback closure. The endpoint never mints a session ID or retains MCP protocol or auth session state across requests. Durable application state belongs in the app's own storage.
|
|
237
|
+
- **Audience validation is explicit.** A populated token `aud` must exactly match `resource` or `acceptedAudiences`. For issuer compatibility, the default also accepts a matching `resource` claim when `aud` is absent or an empty array; set `acceptResourceClaim: false` to require a matching `aud` (strict RFC 9068).
|
|
238
|
+
- **The deployment controls the SDK version.** TanStack resolves the app's declared dependency through its package manager and lockfile. The generated Supabase function exact-pins the SDK release that emitted it. Updating or regenerating the dependency and redeploying is how an app adopts a newer SDK.
|
|
239
|
+
- **Controls are divided between layers.** When OAuth is configured, the SDK owns bearer verification; it also owns origin policy (`allowedOrigins`) and redacts unexpected errors. `ToolError` and returned `isError` results are intentionally caller-visible. Per-tool authorization, data access (including RLS), rate limiting, and WAF belong to the app and its host.
|
|
240
|
+
|
|
208
241
|
## Debug logging
|
|
209
242
|
|
|
210
|
-
The runtime logs operational events (tool invocations, auth outcomes, warnings, errors) at `info` by default. Raise the level to `debug` to trace OAuth discovery, JWKS resolution, and token verification when a deployed MCP server returns `401`/`500` and you need to see why. Logs go to `console` (so they land in your platform's function logs) and **never include the bearer token
|
|
243
|
+
The runtime logs operational events (tool invocations, auth outcomes, warnings, errors) at `info` by default. Raise the level to `debug` to trace OAuth discovery, JWKS resolution, and token verification when a deployed MCP server returns `401`/`500` and you need to see why. Logs go to `console` (so they land in your platform's function logs) and **never include the bearer token or full claims**. Depending on the log level, operational fields include the JWT header `alg`/`kid`, `issuer`, `jwks_uri`, token scopes, and `jose` error codes. The default `info` level excludes end-user identifiers; the token `sub`/`client_id` appears only at `debug`, as the `oauth.verify.principal` event.
|
|
211
244
|
|
|
212
245
|
Enable it either way:
|
|
213
246
|
|
|
@@ -228,7 +261,7 @@ A `500` on an OAuth-protected route is always one of two causes, logged at `erro
|
|
|
228
261
|
|
|
229
262
|
## Usage metrics
|
|
230
263
|
|
|
231
|
-
Each MCP server records per-invocation telemetry — tool name, JSON-RPC method, outcome, and
|
|
264
|
+
Each MCP server records per-invocation telemetry — tool name, JSON-RPC method, outcome, latency, request and response sizes when available, and the verified token `sub` for authenticated calls — as **OTLP/HTTP JSON logs**, one awaited POST per record, bounded by a 3-second timeout; a failed or slow POST is logged (`metrics.failed`) and swallowed, never failing the request. Outcomes cover tool results (`ok`, `tool_error`, `handler_error`, `transport_error`), the authorization gate — `auth_challenge` for an unauthenticated request answered with the `401` challenge (so probe traffic produces records), and the `auth_*` discovery/JWKS failure outcomes — and `origin_rejected` for a request refused `403` by the origin policy. Tool-call records also carry the protocol era (`mcp.protocol`: `2026-07-28` or `legacy`); records emitted before protocol classification (auth challenges, transport errors, origin rejections) have no protocol attribute. The bearer token, full claims, tool arguments, and response payloads are never captured. Emission is **on by default** and self-disables at runtime when `LOVABLE_API_KEY` is absent: records are dropped and one `metrics.disabled_no_api_key` warning is logged per process, so a local `vite dev` stays quiet. With `metrics: false` the recorder never runs — on Deno and Cloudflare Workers that also means `LOVABLE_MCP_LOG_LEVEL` is never applied (use `setLogLevel()` instead).
|
|
232
265
|
|
|
233
266
|
Configure it on the MCP definition:
|
|
234
267
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { E as McpProtocolEra } from "./types-3CZPz364.js";
|
|
2
|
+
//#region src/metrics/impl/base.d.ts
|
|
3
|
+
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_challenge" | "origin_rejected" | "auth_discovery_config_error" | "auth_discovery_unreachable" | "auth_jwks_config_error" | "auth_jwks_unreachable";
|
|
4
|
+
/** One recorded MCP call. Carries no arguments or response payloads — only the
|
|
5
|
+
* tool name, the JSON-RPC method, the result class, timing, and byte sizes.
|
|
6
|
+
* `errorText` is the sole exception: it is logged locally for debugging and
|
|
7
|
+
* deliberately never included in the OTLP payload (see `emit` / `buildLogsPayload`). */
|
|
8
|
+
interface InvocationRecord {
|
|
9
|
+
/** Tool name for `tools/call`; `null` for list/initialize, transport faults, and `authorize` records. */
|
|
10
|
+
tool: string | null;
|
|
11
|
+
/** JSON-RPC method, e.g. `tools/call`; `authorize` for records from the authorization gate. */
|
|
12
|
+
method: string;
|
|
13
|
+
outcome: InvocationOutcome;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
reqBytes?: number;
|
|
16
|
+
resBytes?: number;
|
|
17
|
+
/** HTTP request method. Set on `auth_challenge` so probe traffic (GET vs
|
|
18
|
+
* POST discovery) is distinguishable; absent on tool-call records. */
|
|
19
|
+
httpMethod?: string;
|
|
20
|
+
/** Wire surface that served the call; absent on records emitted before
|
|
21
|
+
* protocol classification (auth gate, transport faults). */
|
|
22
|
+
protocol?: McpProtocolEra;
|
|
23
|
+
/** The app end-user that made the call (the access token `sub`), or
|
|
24
|
+
* `undefined` when the request is unauthenticated. Not a Lovable identity. */
|
|
25
|
+
endUserId?: string;
|
|
26
|
+
/** Caller-visible `isError` text (`tool_error`) or a bounded internal-exception
|
|
27
|
+
* summary (`handler_error`). Local log only — never sent to the collector. */
|
|
28
|
+
errorText?: string;
|
|
29
|
+
}
|
|
30
|
+
/** A per-request telemetry recorder. `emit` logs the invocation and sends it as a
|
|
31
|
+
* single OTLP POST, awaited so it lands before the request ends. */
|
|
32
|
+
interface MetricsRecorder {
|
|
33
|
+
emit(ev: InvocationRecord): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { MetricsRecorder as t };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { E as McpProtocolEra } from "./types-3CZPz364.cjs";
|
|
2
|
+
//#region src/metrics/impl/base.d.ts
|
|
3
|
+
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_challenge" | "origin_rejected" | "auth_discovery_config_error" | "auth_discovery_unreachable" | "auth_jwks_config_error" | "auth_jwks_unreachable";
|
|
4
|
+
/** One recorded MCP call. Carries no arguments or response payloads — only the
|
|
5
|
+
* tool name, the JSON-RPC method, the result class, timing, and byte sizes.
|
|
6
|
+
* `errorText` is the sole exception: it is logged locally for debugging and
|
|
7
|
+
* deliberately never included in the OTLP payload (see `emit` / `buildLogsPayload`). */
|
|
8
|
+
interface InvocationRecord {
|
|
9
|
+
/** Tool name for `tools/call`; `null` for list/initialize, transport faults, and `authorize` records. */
|
|
10
|
+
tool: string | null;
|
|
11
|
+
/** JSON-RPC method, e.g. `tools/call`; `authorize` for records from the authorization gate. */
|
|
12
|
+
method: string;
|
|
13
|
+
outcome: InvocationOutcome;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
reqBytes?: number;
|
|
16
|
+
resBytes?: number;
|
|
17
|
+
/** HTTP request method. Set on `auth_challenge` so probe traffic (GET vs
|
|
18
|
+
* POST discovery) is distinguishable; absent on tool-call records. */
|
|
19
|
+
httpMethod?: string;
|
|
20
|
+
/** Wire surface that served the call; absent on records emitted before
|
|
21
|
+
* protocol classification (auth gate, transport faults). */
|
|
22
|
+
protocol?: McpProtocolEra;
|
|
23
|
+
/** The app end-user that made the call (the access token `sub`), or
|
|
24
|
+
* `undefined` when the request is unauthenticated. Not a Lovable identity. */
|
|
25
|
+
endUserId?: string;
|
|
26
|
+
/** Caller-visible `isError` text (`tool_error`) or a bounded internal-exception
|
|
27
|
+
* summary (`handler_error`). Local log only — never sent to the collector. */
|
|
28
|
+
errorText?: string;
|
|
29
|
+
}
|
|
30
|
+
/** A per-request telemetry recorder. `emit` logs the invocation and sends it as a
|
|
31
|
+
* single OTLP POST, awaited so it lands before the request ends. */
|
|
32
|
+
interface MetricsRecorder {
|
|
33
|
+
emit(ev: InvocationRecord): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { MetricsRecorder as t };
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const
|
|
2
|
+
const require_schema = require("../schema-D3vClxb7.cjs");
|
|
3
|
+
const require_package = require("../package-D8nZVoHm.cjs");
|
|
3
4
|
const require_fs_errors = require("../fs-errors-CWNzOV75.cjs");
|
|
4
5
|
let node_fs = require("node:fs");
|
|
5
6
|
let node_path = require("node:path");
|
|
6
7
|
let vite = require("vite");
|
|
7
8
|
let node_crypto = require("node:crypto");
|
|
8
|
-
let _modelcontextprotocol_sdk_server_zod_compat_js = require("@modelcontextprotocol/sdk/server/zod-compat.js");
|
|
9
|
-
let _modelcontextprotocol_sdk_server_zod_json_schema_compat_js = require("@modelcontextprotocol/sdk/server/zod-json-schema-compat.js");
|
|
10
9
|
//#region src/manifest/listing.ts
|
|
11
|
-
function
|
|
12
|
-
if (!
|
|
13
|
-
|
|
14
|
-
return (0, _modelcontextprotocol_sdk_server_zod_json_schema_compat_js.toJsonSchemaCompat)((0, _modelcontextprotocol_sdk_server_zod_compat_js.objectFromShape)(shape));
|
|
15
|
-
} catch {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
10
|
+
function schemaToJsonSchema(definition, pipeStrategy) {
|
|
11
|
+
if (!definition) return null;
|
|
12
|
+
return require_schema.jsonSchemaFromDefinition(definition, pipeStrategy);
|
|
18
13
|
}
|
|
19
14
|
function buildMcpListing(mcp) {
|
|
20
15
|
return {
|
|
@@ -28,8 +23,8 @@ function buildMcpListing(mcp) {
|
|
|
28
23
|
title: tool.title,
|
|
29
24
|
description: tool.description,
|
|
30
25
|
annotations: tool.annotations,
|
|
31
|
-
inputSchema:
|
|
32
|
-
outputSchema:
|
|
26
|
+
inputSchema: schemaToJsonSchema(tool.inputSchema, "input"),
|
|
27
|
+
outputSchema: schemaToJsonSchema(tool.outputSchema, "output")
|
|
33
28
|
}))
|
|
34
29
|
};
|
|
35
30
|
}
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as
|
|
2
|
+
import { t as jsonSchemaFromDefinition } from "../schema-CW1jKvio.js";
|
|
3
|
+
import { t as version } from "../package-DSwNopNG.js";
|
|
3
4
|
import { t as isFileMissing } from "../fs-errors-PA2t1TIp.js";
|
|
4
5
|
import { lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
6
|
import { dirname, resolve } from "node:path";
|
|
6
7
|
import { createServer, resolveConfig } from "vite";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
8
|
-
import { objectFromShape } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
9
|
-
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
|
|
10
9
|
//#region src/manifest/listing.ts
|
|
11
|
-
function
|
|
12
|
-
if (!
|
|
13
|
-
|
|
14
|
-
return toJsonSchemaCompat(objectFromShape(shape));
|
|
15
|
-
} catch {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
10
|
+
function schemaToJsonSchema(definition, pipeStrategy) {
|
|
11
|
+
if (!definition) return null;
|
|
12
|
+
return jsonSchemaFromDefinition(definition, pipeStrategy);
|
|
18
13
|
}
|
|
19
14
|
function buildMcpListing(mcp) {
|
|
20
15
|
return {
|
|
@@ -28,8 +23,8 @@ function buildMcpListing(mcp) {
|
|
|
28
23
|
title: tool.title,
|
|
29
24
|
description: tool.description,
|
|
30
25
|
annotations: tool.annotations,
|
|
31
|
-
inputSchema:
|
|
32
|
-
outputSchema:
|
|
26
|
+
inputSchema: schemaToJsonSchema(tool.inputSchema, "input"),
|
|
27
|
+
outputSchema: schemaToJsonSchema(tool.outputSchema, "output")
|
|
33
28
|
}))
|
|
34
29
|
};
|
|
35
30
|
}
|
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
import { i as log, n as applyLogLevelFromEnv, o as parseSafeUrl, r as describeError, s as trimTrailingSlash, t as LOG_LEVEL_ENV_VAR, u as resolveMetricsConfig } from "./logger-Ctv5xUSD.js";
|
|
2
|
-
import { t as version } from "./package-
|
|
2
|
+
import { t as version } from "./package-DSwNopNG.js";
|
|
3
3
|
import "./metadata-path-CAkNcfyY.js";
|
|
4
4
|
import { createLocalJWKSet, decodeProtectedHeader, errors, jwtVerify } from "jose";
|
|
5
|
-
//#region src/core/cors.ts
|
|
6
|
-
const EXPOSE_HEADERS = "WWW-Authenticate, Mcp-Session-Id, Mcp-Protocol-Version";
|
|
7
|
-
const ALLOW_HEADERS = "Authorization, Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID";
|
|
8
|
-
/** Add permissive CORS to a built response, preserving its other headers. */
|
|
9
|
-
function withCors(response) {
|
|
10
|
-
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
11
|
-
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
12
|
-
return response;
|
|
13
|
-
}
|
|
14
|
-
function corsPreflightResponse(allowMethods) {
|
|
15
|
-
return new Response(null, {
|
|
16
|
-
status: 204,
|
|
17
|
-
headers: {
|
|
18
|
-
"Access-Control-Allow-Origin": "*",
|
|
19
|
-
"Access-Control-Allow-Methods": allowMethods,
|
|
20
|
-
"Access-Control-Allow-Headers": ALLOW_HEADERS,
|
|
21
|
-
"Access-Control-Max-Age": "86400"
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
5
|
//#region src/core/http.ts
|
|
27
6
|
const JSON_HEADERS = { "Content-Type": "application/json" };
|
|
28
7
|
/** Strip the body from a built response, preserving status and headers, for HEAD. */
|
|
@@ -34,7 +13,7 @@ function headResponse(response) {
|
|
|
34
13
|
});
|
|
35
14
|
}
|
|
36
15
|
function finalizeWireResponse(request, response) {
|
|
37
|
-
return
|
|
16
|
+
return request.method === "HEAD" ? headResponse(response) : response;
|
|
38
17
|
}
|
|
39
18
|
function methodNotAllowed(allow) {
|
|
40
19
|
return new Response(JSON.stringify({ error: "method not allowed" }), {
|
|
@@ -49,7 +28,8 @@ function methodNotAllowed(allow) {
|
|
|
49
28
|
//#endregion
|
|
50
29
|
//#region src/metrics/otlp.ts
|
|
51
30
|
const SCOPE_NAME = "@lovable.dev/mcp-js";
|
|
52
|
-
const
|
|
31
|
+
const TOOL_EVENT_NAME = "mcp.tool.invocation";
|
|
32
|
+
const GATE_EVENT_NAME = "mcp.gate.rejection";
|
|
53
33
|
const SEVERITY_INFO = 9;
|
|
54
34
|
function strAttr(key, value) {
|
|
55
35
|
return {
|
|
@@ -64,8 +44,9 @@ function intAttr(key, value) {
|
|
|
64
44
|
};
|
|
65
45
|
}
|
|
66
46
|
function toLogRecord(rec) {
|
|
47
|
+
const eventName = rec.method === "authorize" ? GATE_EVENT_NAME : TOOL_EVENT_NAME;
|
|
67
48
|
const attributes = [
|
|
68
|
-
strAttr("event.name",
|
|
49
|
+
strAttr("event.name", eventName),
|
|
69
50
|
strAttr("mcp.method", rec.method),
|
|
70
51
|
strAttr("mcp.outcome", rec.outcome),
|
|
71
52
|
intAttr("mcp.duration_ms", rec.durationMs)
|
|
@@ -74,6 +55,7 @@ function toLogRecord(rec) {
|
|
|
74
55
|
if (rec.reqBytes !== void 0) attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
|
|
75
56
|
if (rec.resBytes !== void 0) attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
|
|
76
57
|
if (rec.httpMethod !== void 0) attributes.push(strAttr("http.request.method", rec.httpMethod));
|
|
58
|
+
if (rec.protocol !== void 0) attributes.push(strAttr("mcp.protocol", rec.protocol));
|
|
77
59
|
attributes.push(strAttr("mcp.stack", rec.stack));
|
|
78
60
|
if (rec.endUserId !== void 0) attributes.push(strAttr("mcp.end_user_id", rec.endUserId));
|
|
79
61
|
return {
|
|
@@ -81,7 +63,7 @@ function toLogRecord(rec) {
|
|
|
81
63
|
observedTimeUnixNano: rec.timeUnixNano,
|
|
82
64
|
severityNumber: SEVERITY_INFO,
|
|
83
65
|
severityText: "INFO",
|
|
84
|
-
body: { stringValue:
|
|
66
|
+
body: { stringValue: eventName },
|
|
85
67
|
attributes
|
|
86
68
|
};
|
|
87
69
|
}
|
|
@@ -171,12 +153,13 @@ var BaseMetricRecorder = class {
|
|
|
171
153
|
}
|
|
172
154
|
async emit(ev) {
|
|
173
155
|
await this.applyLazyLogLevel();
|
|
174
|
-
if (ev.outcome !== "auth_challenge") log.info("tool.invoked", {
|
|
156
|
+
if (ev.outcome !== "auth_challenge" && ev.outcome !== "origin_rejected") log.info(ev.method === "authorize" ? "gate.rejected" : "tool.invoked", {
|
|
175
157
|
tool: ev.tool,
|
|
176
158
|
method: ev.method,
|
|
177
159
|
outcome: ev.outcome,
|
|
178
160
|
durationMs: ev.durationMs,
|
|
179
161
|
stack: this.stack,
|
|
162
|
+
...ev.protocol !== void 0 && { protocol: ev.protocol },
|
|
180
163
|
...ev.errorText !== void 0 && { errorText: ev.errorText }
|
|
181
164
|
});
|
|
182
165
|
if (!this.config.enabled) return;
|
|
@@ -948,4 +931,72 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
948
931
|
} };
|
|
949
932
|
}
|
|
950
933
|
//#endregion
|
|
951
|
-
|
|
934
|
+
//#region src/core/cors.ts
|
|
935
|
+
const EXPOSE_HEADERS = "WWW-Authenticate, MCP-Protocol-Version, Mcp-Session-Id";
|
|
936
|
+
const ALLOW_HEADERS = [
|
|
937
|
+
"Authorization",
|
|
938
|
+
"Content-Type",
|
|
939
|
+
"MCP-Protocol-Version",
|
|
940
|
+
"Mcp-Method",
|
|
941
|
+
"Mcp-Name",
|
|
942
|
+
"Mcp-Session-Id",
|
|
943
|
+
"Last-Event-ID"
|
|
944
|
+
];
|
|
945
|
+
const MCP_PARAM_HEADER = /^mcp-param-[!#$%&'*+.^_`|~0-9A-Za-z-]+$/i;
|
|
946
|
+
function appendVary(headers, value) {
|
|
947
|
+
const values = new Set((headers.get("Vary") ?? "").split(",").map((entry) => entry.trim()).filter(Boolean));
|
|
948
|
+
values.add(value);
|
|
949
|
+
headers.set("Vary", [...values].join(", "));
|
|
950
|
+
}
|
|
951
|
+
function addCorsHeaders(response, allowedOrigin, variesByOrigin) {
|
|
952
|
+
if (allowedOrigin !== void 0) {
|
|
953
|
+
response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
|
|
954
|
+
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
955
|
+
}
|
|
956
|
+
if (variesByOrigin) appendVary(response.headers, "Origin");
|
|
957
|
+
return response;
|
|
958
|
+
}
|
|
959
|
+
function allowedRequestHeaders(request) {
|
|
960
|
+
const allowed = new Map(ALLOW_HEADERS.map((header) => [header.toLowerCase(), header]));
|
|
961
|
+
for (const header of (request.headers.get("Access-Control-Request-Headers") ?? "").split(",")) {
|
|
962
|
+
const normalized = header.trim();
|
|
963
|
+
if (MCP_PARAM_HEADER.test(normalized)) allowed.set(normalized.toLowerCase(), normalized);
|
|
964
|
+
}
|
|
965
|
+
return [...allowed.values()].join(", ");
|
|
966
|
+
}
|
|
967
|
+
function evaluateCors(request, allowedOrigins) {
|
|
968
|
+
const origin = request.headers.get("Origin");
|
|
969
|
+
const allowAny = allowedOrigins === "any";
|
|
970
|
+
const originAllowed = origin === null || origin === new URL(request.url).origin || allowedOrigins?.includes(origin) === true;
|
|
971
|
+
if (!allowAny && !originAllowed) {
|
|
972
|
+
const response = Response.json({ error: "origin not allowed" }, {
|
|
973
|
+
status: 403,
|
|
974
|
+
headers: { "Cache-Control": "no-store" }
|
|
975
|
+
});
|
|
976
|
+
appendVary(response.headers, "Origin");
|
|
977
|
+
return {
|
|
978
|
+
ok: false,
|
|
979
|
+
response
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
const allowedOrigin = allowAny ? "*" : origin ?? void 0;
|
|
983
|
+
return {
|
|
984
|
+
ok: true,
|
|
985
|
+
withHeaders: (response) => addCorsHeaders(response, allowedOrigin, !allowAny),
|
|
986
|
+
preflight: (allowMethods) => {
|
|
987
|
+
const requestedHeaders = request.headers.get("Access-Control-Request-Headers");
|
|
988
|
+
const response = new Response(null, {
|
|
989
|
+
status: 204,
|
|
990
|
+
headers: {
|
|
991
|
+
"Access-Control-Allow-Methods": allowMethods,
|
|
992
|
+
"Access-Control-Allow-Headers": allowedRequestHeaders(request),
|
|
993
|
+
"Access-Control-Max-Age": "86400"
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
if (requestedHeaders) appendVary(response.headers, "Access-Control-Request-Headers");
|
|
997
|
+
return addCorsHeaders(response, allowedOrigin, !allowAny);
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
//#endregion
|
|
1002
|
+
export { oauthConfigurationErrorResponse as a, createRecorderForRuntime as c, JSON_HEADERS as d, finalizeWireResponse as f, normalizeHttpMethod as i, createNoopRecorder as l, createRequestAuthorizer as n, assertResourcePathShape as o, methodNotAllowed as p, getOAuthRuntime as r, resolveProtectedResource as s, evaluateCors as t, nowMs as u };
|
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
const require_logger = require("./logger-CN1q-KNn.cjs");
|
|
2
|
-
const require_package = require("./package-
|
|
2
|
+
const require_package = require("./package-D8nZVoHm.cjs");
|
|
3
3
|
require("./metadata-path-zd7NSNoa.cjs");
|
|
4
4
|
let jose = require("jose");
|
|
5
|
-
//#region src/core/cors.ts
|
|
6
|
-
const EXPOSE_HEADERS = "WWW-Authenticate, Mcp-Session-Id, Mcp-Protocol-Version";
|
|
7
|
-
const ALLOW_HEADERS = "Authorization, Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID";
|
|
8
|
-
/** Add permissive CORS to a built response, preserving its other headers. */
|
|
9
|
-
function withCors(response) {
|
|
10
|
-
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
11
|
-
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
12
|
-
return response;
|
|
13
|
-
}
|
|
14
|
-
function corsPreflightResponse(allowMethods) {
|
|
15
|
-
return new Response(null, {
|
|
16
|
-
status: 204,
|
|
17
|
-
headers: {
|
|
18
|
-
"Access-Control-Allow-Origin": "*",
|
|
19
|
-
"Access-Control-Allow-Methods": allowMethods,
|
|
20
|
-
"Access-Control-Allow-Headers": ALLOW_HEADERS,
|
|
21
|
-
"Access-Control-Max-Age": "86400"
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
5
|
//#region src/core/http.ts
|
|
27
6
|
const JSON_HEADERS = { "Content-Type": "application/json" };
|
|
28
7
|
/** Strip the body from a built response, preserving status and headers, for HEAD. */
|
|
@@ -34,7 +13,7 @@ function headResponse(response) {
|
|
|
34
13
|
});
|
|
35
14
|
}
|
|
36
15
|
function finalizeWireResponse(request, response) {
|
|
37
|
-
return
|
|
16
|
+
return request.method === "HEAD" ? headResponse(response) : response;
|
|
38
17
|
}
|
|
39
18
|
function methodNotAllowed(allow) {
|
|
40
19
|
return new Response(JSON.stringify({ error: "method not allowed" }), {
|
|
@@ -49,7 +28,8 @@ function methodNotAllowed(allow) {
|
|
|
49
28
|
//#endregion
|
|
50
29
|
//#region src/metrics/otlp.ts
|
|
51
30
|
const SCOPE_NAME = "@lovable.dev/mcp-js";
|
|
52
|
-
const
|
|
31
|
+
const TOOL_EVENT_NAME = "mcp.tool.invocation";
|
|
32
|
+
const GATE_EVENT_NAME = "mcp.gate.rejection";
|
|
53
33
|
const SEVERITY_INFO = 9;
|
|
54
34
|
function strAttr(key, value) {
|
|
55
35
|
return {
|
|
@@ -64,8 +44,9 @@ function intAttr(key, value) {
|
|
|
64
44
|
};
|
|
65
45
|
}
|
|
66
46
|
function toLogRecord(rec) {
|
|
47
|
+
const eventName = rec.method === "authorize" ? GATE_EVENT_NAME : TOOL_EVENT_NAME;
|
|
67
48
|
const attributes = [
|
|
68
|
-
strAttr("event.name",
|
|
49
|
+
strAttr("event.name", eventName),
|
|
69
50
|
strAttr("mcp.method", rec.method),
|
|
70
51
|
strAttr("mcp.outcome", rec.outcome),
|
|
71
52
|
intAttr("mcp.duration_ms", rec.durationMs)
|
|
@@ -74,6 +55,7 @@ function toLogRecord(rec) {
|
|
|
74
55
|
if (rec.reqBytes !== void 0) attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
|
|
75
56
|
if (rec.resBytes !== void 0) attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
|
|
76
57
|
if (rec.httpMethod !== void 0) attributes.push(strAttr("http.request.method", rec.httpMethod));
|
|
58
|
+
if (rec.protocol !== void 0) attributes.push(strAttr("mcp.protocol", rec.protocol));
|
|
77
59
|
attributes.push(strAttr("mcp.stack", rec.stack));
|
|
78
60
|
if (rec.endUserId !== void 0) attributes.push(strAttr("mcp.end_user_id", rec.endUserId));
|
|
79
61
|
return {
|
|
@@ -81,7 +63,7 @@ function toLogRecord(rec) {
|
|
|
81
63
|
observedTimeUnixNano: rec.timeUnixNano,
|
|
82
64
|
severityNumber: SEVERITY_INFO,
|
|
83
65
|
severityText: "INFO",
|
|
84
|
-
body: { stringValue:
|
|
66
|
+
body: { stringValue: eventName },
|
|
85
67
|
attributes
|
|
86
68
|
};
|
|
87
69
|
}
|
|
@@ -171,12 +153,13 @@ var BaseMetricRecorder = class {
|
|
|
171
153
|
}
|
|
172
154
|
async emit(ev) {
|
|
173
155
|
await this.applyLazyLogLevel();
|
|
174
|
-
if (ev.outcome !== "auth_challenge") require_logger.log.info("tool.invoked", {
|
|
156
|
+
if (ev.outcome !== "auth_challenge" && ev.outcome !== "origin_rejected") require_logger.log.info(ev.method === "authorize" ? "gate.rejected" : "tool.invoked", {
|
|
175
157
|
tool: ev.tool,
|
|
176
158
|
method: ev.method,
|
|
177
159
|
outcome: ev.outcome,
|
|
178
160
|
durationMs: ev.durationMs,
|
|
179
161
|
stack: this.stack,
|
|
162
|
+
...ev.protocol !== void 0 && { protocol: ev.protocol },
|
|
180
163
|
...ev.errorText !== void 0 && { errorText: ev.errorText }
|
|
181
164
|
});
|
|
182
165
|
if (!this.config.enabled) return;
|
|
@@ -948,6 +931,74 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
948
931
|
} };
|
|
949
932
|
}
|
|
950
933
|
//#endregion
|
|
934
|
+
//#region src/core/cors.ts
|
|
935
|
+
const EXPOSE_HEADERS = "WWW-Authenticate, MCP-Protocol-Version, Mcp-Session-Id";
|
|
936
|
+
const ALLOW_HEADERS = [
|
|
937
|
+
"Authorization",
|
|
938
|
+
"Content-Type",
|
|
939
|
+
"MCP-Protocol-Version",
|
|
940
|
+
"Mcp-Method",
|
|
941
|
+
"Mcp-Name",
|
|
942
|
+
"Mcp-Session-Id",
|
|
943
|
+
"Last-Event-ID"
|
|
944
|
+
];
|
|
945
|
+
const MCP_PARAM_HEADER = /^mcp-param-[!#$%&'*+.^_`|~0-9A-Za-z-]+$/i;
|
|
946
|
+
function appendVary(headers, value) {
|
|
947
|
+
const values = new Set((headers.get("Vary") ?? "").split(",").map((entry) => entry.trim()).filter(Boolean));
|
|
948
|
+
values.add(value);
|
|
949
|
+
headers.set("Vary", [...values].join(", "));
|
|
950
|
+
}
|
|
951
|
+
function addCorsHeaders(response, allowedOrigin, variesByOrigin) {
|
|
952
|
+
if (allowedOrigin !== void 0) {
|
|
953
|
+
response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
|
|
954
|
+
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
955
|
+
}
|
|
956
|
+
if (variesByOrigin) appendVary(response.headers, "Origin");
|
|
957
|
+
return response;
|
|
958
|
+
}
|
|
959
|
+
function allowedRequestHeaders(request) {
|
|
960
|
+
const allowed = new Map(ALLOW_HEADERS.map((header) => [header.toLowerCase(), header]));
|
|
961
|
+
for (const header of (request.headers.get("Access-Control-Request-Headers") ?? "").split(",")) {
|
|
962
|
+
const normalized = header.trim();
|
|
963
|
+
if (MCP_PARAM_HEADER.test(normalized)) allowed.set(normalized.toLowerCase(), normalized);
|
|
964
|
+
}
|
|
965
|
+
return [...allowed.values()].join(", ");
|
|
966
|
+
}
|
|
967
|
+
function evaluateCors(request, allowedOrigins) {
|
|
968
|
+
const origin = request.headers.get("Origin");
|
|
969
|
+
const allowAny = allowedOrigins === "any";
|
|
970
|
+
const originAllowed = origin === null || origin === new URL(request.url).origin || allowedOrigins?.includes(origin) === true;
|
|
971
|
+
if (!allowAny && !originAllowed) {
|
|
972
|
+
const response = Response.json({ error: "origin not allowed" }, {
|
|
973
|
+
status: 403,
|
|
974
|
+
headers: { "Cache-Control": "no-store" }
|
|
975
|
+
});
|
|
976
|
+
appendVary(response.headers, "Origin");
|
|
977
|
+
return {
|
|
978
|
+
ok: false,
|
|
979
|
+
response
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
const allowedOrigin = allowAny ? "*" : origin ?? void 0;
|
|
983
|
+
return {
|
|
984
|
+
ok: true,
|
|
985
|
+
withHeaders: (response) => addCorsHeaders(response, allowedOrigin, !allowAny),
|
|
986
|
+
preflight: (allowMethods) => {
|
|
987
|
+
const requestedHeaders = request.headers.get("Access-Control-Request-Headers");
|
|
988
|
+
const response = new Response(null, {
|
|
989
|
+
status: 204,
|
|
990
|
+
headers: {
|
|
991
|
+
"Access-Control-Allow-Methods": allowMethods,
|
|
992
|
+
"Access-Control-Allow-Headers": allowedRequestHeaders(request),
|
|
993
|
+
"Access-Control-Max-Age": "86400"
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
if (requestedHeaders) appendVary(response.headers, "Access-Control-Request-Headers");
|
|
997
|
+
return addCorsHeaders(response, allowedOrigin, !allowAny);
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
//#endregion
|
|
951
1002
|
Object.defineProperty(exports, "JSON_HEADERS", {
|
|
952
1003
|
enumerable: true,
|
|
953
1004
|
get: function() {
|
|
@@ -960,12 +1011,6 @@ Object.defineProperty(exports, "assertResourcePathShape", {
|
|
|
960
1011
|
return assertResourcePathShape;
|
|
961
1012
|
}
|
|
962
1013
|
});
|
|
963
|
-
Object.defineProperty(exports, "corsPreflightResponse", {
|
|
964
|
-
enumerable: true,
|
|
965
|
-
get: function() {
|
|
966
|
-
return corsPreflightResponse;
|
|
967
|
-
}
|
|
968
|
-
});
|
|
969
1014
|
Object.defineProperty(exports, "createNoopRecorder", {
|
|
970
1015
|
enumerable: true,
|
|
971
1016
|
get: function() {
|
|
@@ -984,6 +1029,12 @@ Object.defineProperty(exports, "createRequestAuthorizer", {
|
|
|
984
1029
|
return createRequestAuthorizer;
|
|
985
1030
|
}
|
|
986
1031
|
});
|
|
1032
|
+
Object.defineProperty(exports, "evaluateCors", {
|
|
1033
|
+
enumerable: true,
|
|
1034
|
+
get: function() {
|
|
1035
|
+
return evaluateCors;
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
987
1038
|
Object.defineProperty(exports, "finalizeWireResponse", {
|
|
988
1039
|
enumerable: true,
|
|
989
1040
|
get: function() {
|
|
@@ -1002,6 +1053,12 @@ Object.defineProperty(exports, "methodNotAllowed", {
|
|
|
1002
1053
|
return methodNotAllowed;
|
|
1003
1054
|
}
|
|
1004
1055
|
});
|
|
1056
|
+
Object.defineProperty(exports, "normalizeHttpMethod", {
|
|
1057
|
+
enumerable: true,
|
|
1058
|
+
get: function() {
|
|
1059
|
+
return normalizeHttpMethod;
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1005
1062
|
Object.defineProperty(exports, "nowMs", {
|
|
1006
1063
|
enumerable: true,
|
|
1007
1064
|
get: function() {
|