@decocms/runtime 1.0.0-alpha.5 → 1.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/config-schema.json +19 -8
- package/package.json +11 -17
- package/src/asset-server/index.test.ts +306 -0
- package/src/asset-server/index.ts +217 -34
- package/src/bindings/binder.ts +2 -5
- package/src/bindings/index.ts +0 -33
- package/src/bindings/language-model/utils.ts +0 -91
- package/src/bindings.ts +146 -139
- package/src/client.ts +1 -145
- package/src/cors.ts +140 -0
- package/src/events.ts +472 -0
- package/src/index.ts +206 -202
- package/src/mcp.ts +7 -166
- package/src/oauth.ts +495 -0
- package/src/proxy.ts +1 -208
- package/src/state.ts +3 -31
- package/src/tools.ts +645 -0
- package/src/wrangler.ts +6 -5
- package/tsconfig.json +1 -1
- package/src/admin.ts +0 -16
- package/src/auth.ts +0 -233
- package/src/bindings/deconfig/helpers.ts +0 -107
- package/src/bindings/deconfig/index.ts +0 -1
- package/src/bindings/deconfig/resources.ts +0 -689
- package/src/bindings/deconfig/types.ts +0 -106
- package/src/bindings/language-model/ai-sdk.ts +0 -90
- package/src/bindings/language-model/index.ts +0 -4
- package/src/bindings/resources/bindings.ts +0 -99
- package/src/bindings/resources/helpers.ts +0 -95
- package/src/bindings/resources/schemas.ts +0 -265
- package/src/bindings/views.ts +0 -14
- package/src/drizzle.ts +0 -201
- package/src/http-client-transport.ts +0 -1
- package/src/mastra.ts +0 -670
- package/src/mcp-client.ts +0 -139
- package/src/resources.ts +0 -168
- package/src/views.ts +0 -26
- package/src/well-known.ts +0 -20
package/src/index.ts
CHANGED
|
@@ -1,56 +1,49 @@
|
|
|
1
1
|
/* oxlint-disable no-explicit-any */
|
|
2
|
-
import type { ExecutionContext } from "@cloudflare/workers-types";
|
|
3
2
|
import { decodeJwt } from "jose";
|
|
4
3
|
import type { z } from "zod";
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
StateParser,
|
|
10
|
-
} from "./auth.ts";
|
|
11
|
-
import {
|
|
12
|
-
createContractBinding,
|
|
13
|
-
createIntegrationBinding,
|
|
14
|
-
workspaceClient,
|
|
5
|
+
BindingRegistry,
|
|
6
|
+
initializeBindings,
|
|
7
|
+
ResolvedBindings,
|
|
15
8
|
} from "./bindings.ts";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
9
|
+
import { type CORSOptions, handlePreflight, withCORS } from "./cors.ts";
|
|
10
|
+
import { createOAuthHandlers } from "./oauth.ts";
|
|
11
|
+
import { State } from "./state.ts";
|
|
18
12
|
import {
|
|
19
13
|
createMCPServer,
|
|
20
14
|
type CreateMCPServerOptions,
|
|
21
15
|
MCPServer,
|
|
22
|
-
} from "./
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
} from "./tools.ts";
|
|
17
|
+
export {
|
|
18
|
+
createPrompt,
|
|
19
|
+
createPublicPrompt,
|
|
20
|
+
type Prompt,
|
|
21
|
+
type PromptArgsRawShape,
|
|
22
|
+
type PromptExecutionContext,
|
|
23
|
+
type CreatedPrompt,
|
|
24
|
+
type GetPromptResult,
|
|
25
|
+
} from "./tools.ts";
|
|
26
|
+
import type { Binding } from "./wrangler.ts";
|
|
27
|
+
export { proxyConnectionForId, BindingOf } from "./bindings.ts";
|
|
28
|
+
export { type CORSOptions, type CORSOrigin } from "./cors.ts";
|
|
27
29
|
export {
|
|
28
30
|
createMCPFetchStub,
|
|
29
31
|
type CreateStubAPIOptions,
|
|
30
32
|
type ToolBinder,
|
|
31
33
|
} from "./mcp.ts";
|
|
32
|
-
export interface WorkspaceDB {
|
|
33
|
-
query: (params: {
|
|
34
|
-
sql: string;
|
|
35
|
-
params: string[];
|
|
36
|
-
}) => Promise<{ result: QueryResult[] }>;
|
|
37
|
-
}
|
|
38
34
|
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
DECO_APP_DEPLOYMENT_ID: string;
|
|
48
|
-
DECO_BINDINGS: string;
|
|
49
|
-
DECO_API_TOKEN: string;
|
|
50
|
-
DECO_WORKSPACE_DB: WorkspaceDB & {
|
|
51
|
-
forContext: (ctx: RequestContext) => WorkspaceDB;
|
|
52
|
-
};
|
|
35
|
+
export type { BindingRegistry } from "./bindings.ts";
|
|
36
|
+
|
|
37
|
+
export interface DefaultEnv<
|
|
38
|
+
TSchema extends z.ZodTypeAny = any,
|
|
39
|
+
TBindings extends BindingRegistry = BindingRegistry,
|
|
40
|
+
> {
|
|
41
|
+
MESH_REQUEST_CONTEXT: RequestContext<TSchema, TBindings>;
|
|
42
|
+
MESH_APP_DEPLOYMENT_ID: string;
|
|
53
43
|
IS_LOCAL: boolean;
|
|
44
|
+
MESH_URL?: string;
|
|
45
|
+
MESH_RUNTIME_TOKEN?: string;
|
|
46
|
+
MESH_APP_NAME?: string;
|
|
54
47
|
[key: string]: unknown;
|
|
55
48
|
}
|
|
56
49
|
|
|
@@ -58,7 +51,7 @@ export interface BindingsObject {
|
|
|
58
51
|
bindings?: Binding[];
|
|
59
52
|
}
|
|
60
53
|
|
|
61
|
-
export const
|
|
54
|
+
export const MCPBindings = {
|
|
62
55
|
parse: (bindings?: string): Binding[] => {
|
|
63
56
|
if (!bindings) return [];
|
|
64
57
|
try {
|
|
@@ -75,19 +68,16 @@ export const WorkersMCPBindings = {
|
|
|
75
68
|
export interface UserDefaultExport<
|
|
76
69
|
TUserEnv = Record<string, unknown>,
|
|
77
70
|
TSchema extends z.ZodTypeAny = never,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
interface BindingTypeMap {
|
|
89
|
-
mcp: MCPBinding;
|
|
90
|
-
contract: ContractBinding;
|
|
71
|
+
TBindings extends BindingRegistry = BindingRegistry,
|
|
72
|
+
TEnv extends TUserEnv & DefaultEnv<TSchema, TBindings> = TUserEnv &
|
|
73
|
+
DefaultEnv<TSchema, TBindings>,
|
|
74
|
+
> extends CreateMCPServerOptions<TEnv, TSchema, TBindings> {
|
|
75
|
+
fetch?: (req: Request, env: TEnv, ctx: any) => Promise<Response> | Response;
|
|
76
|
+
/**
|
|
77
|
+
* CORS configuration options.
|
|
78
|
+
* Set to `false` to disable CORS handling entirely.
|
|
79
|
+
*/
|
|
80
|
+
cors?: CORSOptions | false;
|
|
91
81
|
}
|
|
92
82
|
|
|
93
83
|
export interface User {
|
|
@@ -102,55 +92,30 @@ export interface User {
|
|
|
102
92
|
};
|
|
103
93
|
}
|
|
104
94
|
|
|
105
|
-
export interface RequestContext<
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
export interface RequestContext<
|
|
96
|
+
TSchema extends z.ZodTypeAny = any,
|
|
97
|
+
TBindings extends BindingRegistry = BindingRegistry,
|
|
98
|
+
> {
|
|
99
|
+
state: ResolvedBindings<z.infer<TSchema>, TBindings>;
|
|
108
100
|
token: string;
|
|
109
|
-
|
|
101
|
+
meshUrl: string;
|
|
102
|
+
authorization?: string | null;
|
|
110
103
|
ensureAuthenticated: (options?: {
|
|
111
104
|
workspaceHint?: string;
|
|
112
105
|
}) => User | undefined;
|
|
113
106
|
callerApp?: string;
|
|
114
|
-
|
|
107
|
+
connectionId?: string;
|
|
115
108
|
}
|
|
116
109
|
|
|
117
|
-
// 2. Map binding type to its creator function
|
|
118
|
-
type CreatorByType = {
|
|
119
|
-
[K in keyof BindingTypeMap]: (
|
|
120
|
-
value: BindingTypeMap[K],
|
|
121
|
-
env: DefaultEnv,
|
|
122
|
-
) => unknown;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// 3. Strongly type creatorByType
|
|
126
|
-
const creatorByType: CreatorByType = {
|
|
127
|
-
mcp: createIntegrationBinding,
|
|
128
|
-
contract: createContractBinding,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
110
|
const withDefaultBindings = ({
|
|
132
111
|
env,
|
|
133
112
|
server,
|
|
134
|
-
ctx,
|
|
135
113
|
url,
|
|
136
114
|
}: {
|
|
137
115
|
env: DefaultEnv;
|
|
138
116
|
server: MCPServer<any, any>;
|
|
139
|
-
ctx: RequestContext;
|
|
140
117
|
url?: string;
|
|
141
118
|
}) => {
|
|
142
|
-
const client = workspaceClient(ctx, env.DECO_API_URL);
|
|
143
|
-
const createWorkspaceDB = (ctx: RequestContext): WorkspaceDB => {
|
|
144
|
-
const client = workspaceClient(ctx, env.DECO_API_URL);
|
|
145
|
-
return {
|
|
146
|
-
query: ({ sql, params }) => {
|
|
147
|
-
return client.DATABASES_RUN_SQL({
|
|
148
|
-
sql,
|
|
149
|
-
params,
|
|
150
|
-
});
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
};
|
|
154
119
|
env["SELF"] = new Proxy(
|
|
155
120
|
{},
|
|
156
121
|
{
|
|
@@ -169,15 +134,6 @@ const withDefaultBindings = ({
|
|
|
169
134
|
},
|
|
170
135
|
);
|
|
171
136
|
|
|
172
|
-
const workspaceDbBinding = {
|
|
173
|
-
...createWorkspaceDB(ctx),
|
|
174
|
-
forContext: createWorkspaceDB,
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
env["DECO_API"] = MCPClient;
|
|
178
|
-
env["DECO_WORKSPACE_API"] = client;
|
|
179
|
-
env["DECO_WORKSPACE_DB"] = workspaceDbBinding;
|
|
180
|
-
|
|
181
137
|
env["IS_LOCAL"] =
|
|
182
138
|
(url?.startsWith("http://localhost") ||
|
|
183
139
|
url?.startsWith("http://127.0.0.1")) ??
|
|
@@ -194,13 +150,9 @@ export class UnauthorizedError extends Error {
|
|
|
194
150
|
}
|
|
195
151
|
}
|
|
196
152
|
|
|
197
|
-
const
|
|
198
|
-
const AUTH_START_ENDPOINT = "/oauth/start";
|
|
199
|
-
const AUTH_LOGOUT_ENDPOINT = "/oauth/logout";
|
|
200
|
-
const AUTHENTICATED = (user?: unknown, workspace?: string) => () => {
|
|
153
|
+
const AUTHENTICATED = (user?: unknown) => () => {
|
|
201
154
|
return {
|
|
202
155
|
...((user as User) ?? {}),
|
|
203
|
-
workspace,
|
|
204
156
|
} as User;
|
|
205
157
|
};
|
|
206
158
|
|
|
@@ -208,107 +160,171 @@ export const withBindings = <TEnv>({
|
|
|
208
160
|
env: _env,
|
|
209
161
|
server,
|
|
210
162
|
tokenOrContext,
|
|
211
|
-
origin,
|
|
212
163
|
url,
|
|
213
|
-
|
|
164
|
+
authToken,
|
|
214
165
|
}: {
|
|
215
166
|
env: TEnv;
|
|
216
167
|
server: MCPServer<TEnv, any>;
|
|
168
|
+
// token is x-mesh-token
|
|
217
169
|
tokenOrContext?: string | RequestContext;
|
|
218
|
-
|
|
170
|
+
// authToken is the authorization header
|
|
171
|
+
authToken?: string | null;
|
|
219
172
|
url?: string;
|
|
220
|
-
branch?: string | null;
|
|
221
173
|
}): TEnv => {
|
|
222
|
-
branch ??= undefined;
|
|
223
174
|
const env = _env as DefaultEnv<any>;
|
|
175
|
+
const authorization = authToken ? authToken.split(" ")[1] : undefined;
|
|
224
176
|
|
|
225
|
-
const apiUrl = env.DECO_API_URL ?? "https://api.decocms.com";
|
|
226
177
|
let context;
|
|
227
178
|
if (typeof tokenOrContext === "string") {
|
|
228
179
|
const decoded = decodeJwt(tokenOrContext);
|
|
229
|
-
|
|
180
|
+
// Support both new JWT format (fields directly on payload) and legacy format (nested in metadata)
|
|
181
|
+
const metadata =
|
|
182
|
+
(decoded.metadata as {
|
|
183
|
+
state?: Record<string, unknown>;
|
|
184
|
+
meshUrl?: string;
|
|
185
|
+
connectionId?: string;
|
|
186
|
+
}) ?? {};
|
|
230
187
|
|
|
231
188
|
context = {
|
|
232
|
-
|
|
189
|
+
authorization,
|
|
190
|
+
state: decoded.state ?? metadata.state,
|
|
233
191
|
token: tokenOrContext,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
ensureAuthenticated: AUTHENTICATED(decoded.user
|
|
237
|
-
branch,
|
|
192
|
+
meshUrl: (decoded.meshUrl as string) ?? metadata.meshUrl,
|
|
193
|
+
connectionId: (decoded.connectionId as string) ?? metadata.connectionId,
|
|
194
|
+
ensureAuthenticated: AUTHENTICATED(decoded.user ?? decoded.sub),
|
|
238
195
|
} as RequestContext<any>;
|
|
239
196
|
} else if (typeof tokenOrContext === "object") {
|
|
240
197
|
context = tokenOrContext;
|
|
241
198
|
const decoded = decodeJwt(tokenOrContext.token);
|
|
242
|
-
|
|
199
|
+
// Support both new JWT format (fields directly on payload) and legacy format (nested in metadata)
|
|
200
|
+
const metadata =
|
|
201
|
+
(decoded.metadata as {
|
|
202
|
+
state?: Record<string, unknown>;
|
|
203
|
+
meshUrl?: string;
|
|
204
|
+
connectionId?: string;
|
|
205
|
+
}) ?? {};
|
|
243
206
|
const appName = decoded.appName as string | undefined;
|
|
207
|
+
context.authorization ??= authorization;
|
|
244
208
|
context.callerApp = appName;
|
|
245
|
-
context.
|
|
246
|
-
|
|
209
|
+
context.connectionId ??=
|
|
210
|
+
(decoded.connectionId as string) ?? metadata.connectionId;
|
|
211
|
+
context.ensureAuthenticated = AUTHENTICATED(decoded.user ?? decoded.sub);
|
|
247
212
|
} else {
|
|
248
213
|
context = {
|
|
249
|
-
state:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
authUri.searchParams.set("client_id", env.DECO_APP_NAME);
|
|
257
|
-
authUri.searchParams.set(
|
|
258
|
-
"redirect_uri",
|
|
259
|
-
new URL(AUTH_CALLBACK_ENDPOINT, origin ?? env.DECO_APP_ENTRYPOINT)
|
|
260
|
-
.href,
|
|
261
|
-
);
|
|
262
|
-
workspaceHint &&
|
|
263
|
-
authUri.searchParams.set("workspace_hint", workspaceHint);
|
|
264
|
-
throw new UnauthorizedError("Unauthorized", authUri);
|
|
214
|
+
state: {},
|
|
215
|
+
authorization,
|
|
216
|
+
token: undefined,
|
|
217
|
+
meshUrl: undefined,
|
|
218
|
+
connectionId: undefined,
|
|
219
|
+
ensureAuthenticated: () => {
|
|
220
|
+
throw new Error("Unauthorized");
|
|
265
221
|
},
|
|
266
|
-
}
|
|
222
|
+
} as unknown as RequestContext<any>;
|
|
267
223
|
}
|
|
268
224
|
|
|
269
|
-
env.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
for (const binding of bindings) {
|
|
273
|
-
env[binding.name] = creatorByType[binding.type](binding as any, env);
|
|
274
|
-
}
|
|
225
|
+
env.MESH_REQUEST_CONTEXT = context;
|
|
226
|
+
context.state = initializeBindings(context);
|
|
275
227
|
|
|
276
228
|
withDefaultBindings({
|
|
277
229
|
env,
|
|
278
230
|
server,
|
|
279
|
-
ctx: env.DECO_REQUEST_CONTEXT,
|
|
280
231
|
url,
|
|
281
232
|
});
|
|
282
233
|
|
|
283
234
|
return env as TEnv;
|
|
284
235
|
};
|
|
285
236
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
req: Request,
|
|
292
|
-
env: TEnv & DefaultEnv<TSchema>,
|
|
293
|
-
ctx: ExecutionContext,
|
|
294
|
-
) => {
|
|
295
|
-
const url = new URL(req.url);
|
|
296
|
-
if (url.pathname === AUTH_CALLBACK_ENDPOINT) {
|
|
297
|
-
return handleAuthCallback(req, {
|
|
298
|
-
apiUrl: env.DECO_API_URL,
|
|
299
|
-
appName: env.DECO_APP_NAME,
|
|
300
|
-
});
|
|
237
|
+
const DEFAULT_CORS_OPTIONS = {
|
|
238
|
+
origin: (origin: string) => {
|
|
239
|
+
// Allow localhost and configured origins
|
|
240
|
+
if (origin.includes("localhost") || origin.includes("127.0.0.1")) {
|
|
241
|
+
return origin;
|
|
301
242
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
243
|
+
// TODO: Configure allowed origins from environment
|
|
244
|
+
return origin;
|
|
245
|
+
},
|
|
246
|
+
credentials: true,
|
|
247
|
+
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
248
|
+
allowHeaders: ["Content-Type", "Authorization", "mcp-protocol-version"],
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export const withRuntime = <
|
|
252
|
+
TUserEnv,
|
|
253
|
+
TSchema extends z.ZodTypeAny = never,
|
|
254
|
+
TBindings extends BindingRegistry = BindingRegistry,
|
|
255
|
+
TEnv extends TUserEnv & DefaultEnv<TSchema, TBindings> = TUserEnv &
|
|
256
|
+
DefaultEnv<TSchema, TBindings>,
|
|
257
|
+
>(
|
|
258
|
+
userFns: UserDefaultExport<TUserEnv, TSchema, TBindings>,
|
|
259
|
+
) => {
|
|
260
|
+
const server = createMCPServer<TUserEnv, TSchema, TBindings>(userFns);
|
|
261
|
+
const corsOptions = userFns.cors ?? DEFAULT_CORS_OPTIONS;
|
|
262
|
+
const oauth = userFns.oauth;
|
|
263
|
+
const oauthHandlers = oauth ? createOAuthHandlers(oauth) : null;
|
|
264
|
+
|
|
265
|
+
const fetcher = async (req: Request, env: TEnv, ctx: any) => {
|
|
266
|
+
const url = new URL(req.url);
|
|
267
|
+
|
|
268
|
+
// OAuth routes (when configured)
|
|
269
|
+
if (oauthHandlers) {
|
|
270
|
+
// Protected resource metadata (RFC9728) - both paths MUST be supported
|
|
271
|
+
if (
|
|
272
|
+
url.pathname === "/.well-known/oauth-protected-resource" ||
|
|
273
|
+
url.pathname === "/mcp/.well-known/oauth-protected-resource"
|
|
274
|
+
) {
|
|
275
|
+
return oauthHandlers.handleProtectedResourceMetadata(req);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Authorization server metadata (RFC8414)
|
|
279
|
+
if (url.pathname === "/.well-known/oauth-authorization-server") {
|
|
280
|
+
return oauthHandlers.handleAuthorizationServerMetadata(req);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Authorization endpoint - redirects to external OAuth provider
|
|
284
|
+
if (url.pathname === "/authorize") {
|
|
285
|
+
return oauthHandlers.handleAuthorize(req);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// OAuth callback - receives code from external OAuth provider
|
|
289
|
+
if (url.pathname === "/oauth/callback") {
|
|
290
|
+
return oauthHandlers.handleOAuthCallback(req);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Token endpoint - exchanges code for tokens
|
|
294
|
+
if (url.pathname === "/token" && req.method === "POST") {
|
|
295
|
+
return oauthHandlers.handleToken(req);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Dynamic client registration (RFC7591)
|
|
299
|
+
if (
|
|
300
|
+
(url.pathname === "/register" || url.pathname === "/mcp/register") &&
|
|
301
|
+
req.method === "POST"
|
|
302
|
+
) {
|
|
303
|
+
return oauthHandlers.handleClientRegistration(req);
|
|
304
|
+
}
|
|
310
305
|
}
|
|
306
|
+
|
|
307
|
+
// MCP endpoint
|
|
311
308
|
if (url.pathname === "/mcp") {
|
|
309
|
+
if (req.method === "GET") {
|
|
310
|
+
return new Response("Method not allowed", { status: 405 });
|
|
311
|
+
}
|
|
312
|
+
// If OAuth is configured, require authentication
|
|
313
|
+
if (oauthHandlers && !oauthHandlers.hasAuth(req)) {
|
|
314
|
+
// Clone request to check method without consuming the original body
|
|
315
|
+
const clonedReq = req.clone();
|
|
316
|
+
try {
|
|
317
|
+
const body = (await clonedReq.json()) as { method?: string };
|
|
318
|
+
// Allow tools/list to pass without auth
|
|
319
|
+
if (body?.method !== "tools/list") {
|
|
320
|
+
return oauthHandlers.createUnauthorizedResponse(req);
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
// If body parsing fails, require auth
|
|
324
|
+
return oauthHandlers.createUnauthorizedResponse(req);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
312
328
|
return server.fetch(req, env, ctx);
|
|
313
329
|
}
|
|
314
330
|
|
|
@@ -334,55 +350,43 @@ export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
|
|
|
334
350
|
});
|
|
335
351
|
}
|
|
336
352
|
|
|
337
|
-
if (url.pathname.startsWith(DeconfigResource.WatchPathNameBase)) {
|
|
338
|
-
return DeconfigResource.watchAPI(req, env);
|
|
339
|
-
}
|
|
340
353
|
return (
|
|
341
354
|
userFns.fetch?.(req, env, ctx) ||
|
|
342
355
|
new Response("Not found", { status: 404 })
|
|
343
356
|
);
|
|
344
357
|
};
|
|
358
|
+
|
|
345
359
|
return {
|
|
346
|
-
fetch: async (
|
|
347
|
-
req
|
|
348
|
-
|
|
349
|
-
ctx: ExecutionContext,
|
|
350
|
-
) => {
|
|
351
|
-
const referer = req.headers.get("referer");
|
|
352
|
-
const isFetchRequest = req.headers.has(DECO_MCP_CLIENT_HEADER);
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
const bindings = withBindings({
|
|
356
|
-
env,
|
|
357
|
-
server,
|
|
358
|
-
branch:
|
|
359
|
-
req.headers.get("x-deco-branch") ??
|
|
360
|
-
new URL(req.url).searchParams.get("__b"),
|
|
361
|
-
tokenOrContext: await getReqToken(req, env),
|
|
362
|
-
origin:
|
|
363
|
-
referer ?? req.headers.get("origin") ?? new URL(req.url).origin,
|
|
364
|
-
url: req.url,
|
|
365
|
-
});
|
|
366
|
-
return await State.run(
|
|
367
|
-
{ req, env: bindings, ctx },
|
|
368
|
-
async () => await fetcher(req, bindings, ctx),
|
|
369
|
-
);
|
|
370
|
-
} catch (error) {
|
|
371
|
-
if (error instanceof UnauthorizedError) {
|
|
372
|
-
if (!isFetchRequest) {
|
|
373
|
-
const url = new URL(req.url);
|
|
374
|
-
error.redirectTo.searchParams.set(
|
|
375
|
-
"state",
|
|
376
|
-
StateParser.stringify({
|
|
377
|
-
next: url.searchParams.get("next") ?? referer ?? req.url,
|
|
378
|
-
}),
|
|
379
|
-
);
|
|
380
|
-
return Response.redirect(error.redirectTo, 302);
|
|
381
|
-
}
|
|
382
|
-
return new Response(null, { status: 401 });
|
|
383
|
-
}
|
|
384
|
-
throw error;
|
|
360
|
+
fetch: async (req: Request, env: TEnv, ctx?: any) => {
|
|
361
|
+
if (new URL(req.url).pathname === "/_healthcheck") {
|
|
362
|
+
return new Response("OK", { status: 200 });
|
|
385
363
|
}
|
|
364
|
+
// Handle CORS preflight (OPTIONS) requests
|
|
365
|
+
if (corsOptions !== false && req.method === "OPTIONS") {
|
|
366
|
+
const options = corsOptions ?? {};
|
|
367
|
+
return handlePreflight(req, options);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const bindings = withBindings({
|
|
371
|
+
authToken: req.headers.get("authorization") ?? null,
|
|
372
|
+
env: { ...process.env, ...env },
|
|
373
|
+
server,
|
|
374
|
+
tokenOrContext: req.headers.get("x-mesh-token") ?? undefined,
|
|
375
|
+
url: req.url,
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const response = await State.run(
|
|
379
|
+
{ req, env: bindings, ctx },
|
|
380
|
+
async () => await fetcher(req, bindings, ctx),
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Add CORS headers to response
|
|
384
|
+
if (corsOptions !== false) {
|
|
385
|
+
const options = corsOptions ?? {};
|
|
386
|
+
return withCORS(response, req, options);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return response;
|
|
386
390
|
},
|
|
387
391
|
};
|
|
388
392
|
};
|