@moku-labs/worker 0.1.0
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.md +376 -0
- package/dist/cli.cjs +743 -0
- package/dist/cli.d.cts +139 -0
- package/dist/cli.d.mts +139 -0
- package/dist/cli.mjs +740 -0
- package/dist/config-AjH57AmD.d.cts +36 -0
- package/dist/config-AjH57AmD.d.mts +36 -0
- package/dist/index.cjs +544 -0
- package/dist/index.d.cts +962 -0
- package/dist/index.d.mts +960 -0
- package/dist/index.mjs +491 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/storage-BaQ6BBtl.cjs +990 -0
- package/dist/storage-bL-U_fkA.mjs +882 -0
- package/package.json +77 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { a as defineDurableObject, c as coreConfig, d as stagePlugin, i as durableObjectsPlugin, l as createCore, n as queuesPlugin, o as d1Plugin, r as kvPlugin, s as bindingsPlugin, t as storagePlugin, u as createPlugin$1 } from "./storage-bL-U_fkA.mjs";
|
|
3
|
+
import { envPlugin, logPlugin } from "@moku-labs/common";
|
|
4
|
+
//#region src/plugins/server/api.ts
|
|
5
|
+
/**
|
|
6
|
+
* Builds the `app.server.*` surface the consumer's Worker default export reads.
|
|
7
|
+
*
|
|
8
|
+
* @param ctx - Plugin context: config, compiled state table, emit, require, has.
|
|
9
|
+
* @returns The server API object with `handle` and `scheduled` methods.
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // Wired in index.ts as: api: ctx => createServerApi(ctx)
|
|
13
|
+
* const serverApi = createServerApi(ctx);
|
|
14
|
+
* const response = await serverApi.handle(request, env, exec);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
const createServerApi = (ctx) => {
|
|
18
|
+
/**
|
|
19
|
+
* Route one HTTP request and return its `Response` (or 404 Not Found).
|
|
20
|
+
*
|
|
21
|
+
* Allocates a fresh `RequestContext` on the call stack carrying the
|
|
22
|
+
* per-request `env` — never stored on state (SB4). Response flows through
|
|
23
|
+
* the return value, not `emit` (F8; spec/07 §1).
|
|
24
|
+
*
|
|
25
|
+
* @param request - The incoming Cloudflare `Request`.
|
|
26
|
+
* @param env - Per-request Cloudflare bindings; threaded on the stack, never stored.
|
|
27
|
+
* @param exec - `ExecutionContext` for `waitUntil` / `passThroughOnException`.
|
|
28
|
+
* @returns The matched handler's `Response`, or `404 Not Found`.
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const res = await serverApi.handle(new Request("https://example.com/"), env, exec);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
const handle = async (request, env, exec) => {
|
|
35
|
+
const url = new URL(request.url);
|
|
36
|
+
const requestId = crypto.randomUUID();
|
|
37
|
+
const startTime = Date.now();
|
|
38
|
+
ctx.emit("request:start", {
|
|
39
|
+
method: request.method,
|
|
40
|
+
path: url.pathname,
|
|
41
|
+
requestId
|
|
42
|
+
});
|
|
43
|
+
const match = ctx.state.match(request.method, url.pathname);
|
|
44
|
+
if (!match) return new Response("Not Found", { status: 404 });
|
|
45
|
+
ctx.emit("server:matched", {
|
|
46
|
+
path: url.pathname,
|
|
47
|
+
method: request.method
|
|
48
|
+
});
|
|
49
|
+
const rc = {
|
|
50
|
+
request,
|
|
51
|
+
env,
|
|
52
|
+
exec,
|
|
53
|
+
params: match.params,
|
|
54
|
+
url,
|
|
55
|
+
require: ctx.require,
|
|
56
|
+
has: ctx.has
|
|
57
|
+
};
|
|
58
|
+
const response = await match.endpoint.handler(rc);
|
|
59
|
+
ctx.emit("request:end", {
|
|
60
|
+
method: request.method,
|
|
61
|
+
path: url.pathname,
|
|
62
|
+
status: response.status,
|
|
63
|
+
ms: Date.now() - startTime
|
|
64
|
+
});
|
|
65
|
+
return response;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Cron entry. Dispatches the `ScheduledController` through the same endpoint
|
|
69
|
+
* table as `handle` and **awaits** the matched handler so Cloudflare does not
|
|
70
|
+
* kill the isolate before the work finishes.
|
|
71
|
+
*
|
|
72
|
+
* Awaited API method — not `emit` — because the Worker must `await` cron work
|
|
73
|
+
* (F8; spec/07 §3). The `env` is threaded on the stack, never stored on state (SB4).
|
|
74
|
+
*
|
|
75
|
+
* @param controller - Cloudflare `ScheduledController` (`cron`, `scheduledTime`).
|
|
76
|
+
* @param env - Per-request Cloudflare bindings; threaded, never stored.
|
|
77
|
+
* @param exec - `ExecutionContext` for `waitUntil` / `passThroughOnException`.
|
|
78
|
+
* @returns Resolves after all matched cron work completes (or immediately if no match).
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* await serverApi.scheduled(controller, env, exec);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
const scheduled = async (controller, env, exec) => {
|
|
85
|
+
const match = ctx.state.match("ALL", controller.cron);
|
|
86
|
+
if (!match) return;
|
|
87
|
+
const cronUrl = new URL(`https://cron/${controller.cron}`);
|
|
88
|
+
const rc = {
|
|
89
|
+
request: new Request(cronUrl.href),
|
|
90
|
+
env,
|
|
91
|
+
exec,
|
|
92
|
+
params: match.params,
|
|
93
|
+
url: cronUrl,
|
|
94
|
+
require: ctx.require,
|
|
95
|
+
has: ctx.has
|
|
96
|
+
};
|
|
97
|
+
await match.endpoint.handler(rc);
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
handle,
|
|
101
|
+
scheduled
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/plugins/server/helpers.ts
|
|
106
|
+
/**
|
|
107
|
+
* Produce an `Endpoint` value from a path, method, and handler.
|
|
108
|
+
*
|
|
109
|
+
* @param path - Endpoint path string.
|
|
110
|
+
* @param method - HTTP method literal or `"ALL"`.
|
|
111
|
+
* @param handler - The function invoked when this endpoint matches.
|
|
112
|
+
* @returns An `Endpoint` value object.
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* makeEndpoint("/api", "GET", handler); // { path: "/api", method: "GET", handler }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
const makeEndpoint = (path, method, handler) => ({
|
|
119
|
+
path,
|
|
120
|
+
method,
|
|
121
|
+
handler
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* Build a typed `Endpoint`. `{name}` → required param; `{name?}` → optional param.
|
|
125
|
+
*
|
|
126
|
+
* PURE factory (spec/03 §1): no ctx, no lifecycle, no side effects; safe to run
|
|
127
|
+
* before `createApp`. Each verb method (`get`, `post`, …, `all`) returns the
|
|
128
|
+
* truthful Endpoint value — `method: "ALL"` is never used as a `"get"` sentinel.
|
|
129
|
+
*
|
|
130
|
+
* @param path - Endpoint path, optionally with `{name}` / `{name?}` params.
|
|
131
|
+
* @returns A builder whose verb methods each return a typed `Endpoint`.
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* endpoint("/api/data/{lang?}").get(({ params }) =>
|
|
135
|
+
* Response.json({ lang: params.lang ?? "en" })
|
|
136
|
+
* );
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
const endpoint = (path) => ({
|
|
140
|
+
/**
|
|
141
|
+
* Build a GET endpoint bound to this path.
|
|
142
|
+
*
|
|
143
|
+
* @param handler - The handler invoked when a GET request matches.
|
|
144
|
+
* @returns A GET `Endpoint`.
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* endpoint("/health").get(() => new Response("ok"));
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
get: (handler) => makeEndpoint(path, "GET", handler),
|
|
151
|
+
/**
|
|
152
|
+
* Build a POST endpoint bound to this path.
|
|
153
|
+
*
|
|
154
|
+
* @param handler - The handler invoked when a POST request matches.
|
|
155
|
+
* @returns A POST `Endpoint`.
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* endpoint("/users").post(({ request }) => Response.json({ created: true }, { status: 201 }));
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
post: (handler) => makeEndpoint(path, "POST", handler),
|
|
162
|
+
/**
|
|
163
|
+
* Build a PUT endpoint bound to this path.
|
|
164
|
+
*
|
|
165
|
+
* @param handler - The handler invoked when a PUT request matches.
|
|
166
|
+
* @returns A PUT `Endpoint`.
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* endpoint("/users/{id}").put(({ params }) => Response.json({ updated: params.id }));
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
put: (handler) => makeEndpoint(path, "PUT", handler),
|
|
173
|
+
/**
|
|
174
|
+
* Build a PATCH endpoint bound to this path.
|
|
175
|
+
*
|
|
176
|
+
* @param handler - The handler invoked when a PATCH request matches.
|
|
177
|
+
* @returns A PATCH `Endpoint`.
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* endpoint("/users/{id}").patch(({ params }) => Response.json({ patched: params.id }));
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
patch: (handler) => makeEndpoint(path, "PATCH", handler),
|
|
184
|
+
/**
|
|
185
|
+
* Build a DELETE endpoint bound to this path.
|
|
186
|
+
*
|
|
187
|
+
* @param handler - The handler invoked when a DELETE request matches.
|
|
188
|
+
* @returns A DELETE `Endpoint`.
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* endpoint("/users/{id}").delete(() => new Response(null, { status: 204 }));
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
delete: (handler) => makeEndpoint(path, "DELETE", handler),
|
|
195
|
+
/**
|
|
196
|
+
* Build a HEAD endpoint bound to this path.
|
|
197
|
+
*
|
|
198
|
+
* @param handler - The handler invoked when a HEAD request matches.
|
|
199
|
+
* @returns A HEAD `Endpoint`.
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* endpoint("/health").head(() => new Response(null, { status: 200 }));
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
head: (handler) => makeEndpoint(path, "HEAD", handler),
|
|
206
|
+
/**
|
|
207
|
+
* Build an OPTIONS endpoint bound to this path.
|
|
208
|
+
*
|
|
209
|
+
* @param handler - The handler invoked when an OPTIONS request matches.
|
|
210
|
+
* @returns An OPTIONS `Endpoint`.
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* endpoint("/api").options(() => new Response(null, { headers: { Allow: "GET, POST" } }));
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
options: (handler) => makeEndpoint(path, "OPTIONS", handler),
|
|
217
|
+
/**
|
|
218
|
+
* Build an ALL-method endpoint bound to this path (matches any verb).
|
|
219
|
+
*
|
|
220
|
+
* @param handler - The handler invoked when any request method matches.
|
|
221
|
+
* @returns An ALL-method `Endpoint`.
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* endpoint("0 * * * *").all(async () => new Response("cron done"));
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
all: (handler) => makeEndpoint(path, "ALL", handler)
|
|
228
|
+
});
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/plugins/server/state.ts
|
|
231
|
+
/** Specificity weight for a literal path segment. */
|
|
232
|
+
const LITERAL_WEIGHT = 2;
|
|
233
|
+
/** Specificity weight for a required param segment `{name}`. */
|
|
234
|
+
const REQUIRED_PARAM_WEIGHT = 1;
|
|
235
|
+
/** Specificity weight for an optional param segment `{name?}`. */
|
|
236
|
+
const OPTIONAL_PARAM_WEIGHT = 0;
|
|
237
|
+
/**
|
|
238
|
+
* Parse one path segment string into a typed `PathSegment`.
|
|
239
|
+
*
|
|
240
|
+
* `{name}` → required param; `{name?}` → optional param; anything else → literal.
|
|
241
|
+
*
|
|
242
|
+
* @param raw - A single path segment token (no leading slash).
|
|
243
|
+
* @returns The parsed `PathSegment`.
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* parseSegment("{id}") // → { value: "id", param: true, optional: false }
|
|
247
|
+
* parseSegment("{id?}") // → { value: "id", param: true, optional: true }
|
|
248
|
+
* parseSegment("api") // → { value: "api", param: false, optional: false }
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
const parseSegment = (raw) => {
|
|
252
|
+
if (raw.startsWith("{") && raw.endsWith("}")) {
|
|
253
|
+
const inner = raw.slice(1, -1);
|
|
254
|
+
if (inner.endsWith("?")) return {
|
|
255
|
+
value: inner.slice(0, -1),
|
|
256
|
+
param: true,
|
|
257
|
+
optional: true
|
|
258
|
+
};
|
|
259
|
+
return {
|
|
260
|
+
value: inner,
|
|
261
|
+
param: true,
|
|
262
|
+
optional: false
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
value: raw,
|
|
267
|
+
param: false,
|
|
268
|
+
optional: false
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Compute the specificity weight for a single `PathSegment`.
|
|
273
|
+
*
|
|
274
|
+
* @param segment - The parsed segment to score.
|
|
275
|
+
* @returns `2` for literal, `1` for required param, `0` for optional param.
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* segmentWeight({ value: "api", param: false, optional: false }) // 2
|
|
279
|
+
* segmentWeight({ value: "id", param: true, optional: false }) // 1
|
|
280
|
+
* segmentWeight({ value: "q", param: true, optional: true }) // 0
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
const segmentWeight = (segment) => {
|
|
284
|
+
if (!segment.param) return LITERAL_WEIGHT;
|
|
285
|
+
if (!segment.optional) return REQUIRED_PARAM_WEIGHT;
|
|
286
|
+
return OPTIONAL_PARAM_WEIGHT;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Convert an `Endpoint` to its compiled form (parsed segments + specificity score).
|
|
290
|
+
*
|
|
291
|
+
* @param endpoint - The declarative endpoint value from config.
|
|
292
|
+
* @returns A `CompiledEndpoint` ready for the matcher table.
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* const compiled = compileEndpoint(endpoint("/api/{id}").get(handler));
|
|
296
|
+
* // compiled.specificity === 3 (literal "api" = 2, required "{id}" = 1)
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
const compileEndpoint = (endpoint) => {
|
|
300
|
+
const segments = endpoint.path.split("/").filter(Boolean).map((part) => parseSegment(part));
|
|
301
|
+
return {
|
|
302
|
+
endpoint,
|
|
303
|
+
segments,
|
|
304
|
+
specificity: segments.reduce((total, segment) => total + segmentWeight(segment), 0)
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
/**
|
|
308
|
+
* Try to match one compiled endpoint against a request method and split path tokens.
|
|
309
|
+
*
|
|
310
|
+
* Returns the extracted params map on success, or `undefined` if the endpoint
|
|
311
|
+
* does not match. Uses `undefined` (not `null`) per the unicorn/no-null rule.
|
|
312
|
+
*
|
|
313
|
+
* @param compiled - A single compiled endpoint.
|
|
314
|
+
* @param method - The request method string (e.g. `"GET"`).
|
|
315
|
+
* @param tokens - The request path split into non-empty segments.
|
|
316
|
+
* @returns Extracted params record on match, or `undefined` for no match.
|
|
317
|
+
* @example
|
|
318
|
+
* ```typescript
|
|
319
|
+
* const compiled = compileEndpoint(endpoint("/users/{id}").get(handler));
|
|
320
|
+
* tryMatchEndpoint(compiled, "GET", ["users", "42"]) // → { id: "42" }
|
|
321
|
+
* tryMatchEndpoint(compiled, "POST", ["users"]) // → undefined
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
const tryMatchEndpoint = (compiled, method, tokens) => {
|
|
325
|
+
if (compiled.endpoint.method !== "ALL" && compiled.endpoint.method !== method) return;
|
|
326
|
+
const { segments } = compiled;
|
|
327
|
+
const mandatoryCount = segments.filter((segment) => !segment.optional).length;
|
|
328
|
+
if (tokens.length < mandatoryCount || tokens.length > segments.length) return;
|
|
329
|
+
const params = {};
|
|
330
|
+
for (const [index, segment] of segments.entries()) {
|
|
331
|
+
const token = tokens[index];
|
|
332
|
+
if (segment.param) if (token === void 0) {
|
|
333
|
+
if (!segment.optional) return void 0;
|
|
334
|
+
params[segment.value] = void 0;
|
|
335
|
+
} else params[segment.value] = token;
|
|
336
|
+
else if (token !== segment.value) return;
|
|
337
|
+
}
|
|
338
|
+
return params;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Sort comparator placing higher-specificity endpoints first.
|
|
342
|
+
* Tie-break: method-specific endpoints before `ALL` so explicit methods win.
|
|
343
|
+
*
|
|
344
|
+
* @param a - First compiled endpoint.
|
|
345
|
+
* @param b - Second compiled endpoint.
|
|
346
|
+
* @returns Negative, zero, or positive sort key.
|
|
347
|
+
* @example
|
|
348
|
+
* ```typescript
|
|
349
|
+
* const a = compileEndpoint(endpoint("/api/{id}").get(handler)); // specificity 3
|
|
350
|
+
* const b = compileEndpoint(endpoint("/api/{id?}").get(handler)); // specificity 2
|
|
351
|
+
* [b, a].sort(bySpecificityDesc); // → [a, b] — higher specificity first
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
const bySpecificityDesc = (a, b) => {
|
|
355
|
+
const delta = b.specificity - a.specificity;
|
|
356
|
+
if (delta !== 0) return delta;
|
|
357
|
+
if (a.endpoint.method !== "ALL" && b.endpoint.method === "ALL") return -1;
|
|
358
|
+
if (a.endpoint.method === "ALL" && b.endpoint.method !== "ALL") return 1;
|
|
359
|
+
return 0;
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Find the best-matching compiled endpoint in the table for the given method + path.
|
|
363
|
+
*
|
|
364
|
+
* Iterates the table (assumed sorted high-to-low specificity) and returns the
|
|
365
|
+
* first match. Internally re-sorts on every call so it is safe to call before
|
|
366
|
+
* `onInit` compiles the table.
|
|
367
|
+
*
|
|
368
|
+
* @param table - The compiled endpoint table.
|
|
369
|
+
* @param method - Request method string (e.g. `"GET"`, `"ALL"`).
|
|
370
|
+
* @param tokens - Path split into non-empty string tokens.
|
|
371
|
+
* @returns The match result, or `null` when no endpoint matches.
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* const result = findBestMatch(state.table, "GET", ["api", "users"]);
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
const findBestMatch = (table, method, tokens) => {
|
|
378
|
+
const sorted = table.toSorted(bySpecificityDesc);
|
|
379
|
+
for (const compiled of sorted) {
|
|
380
|
+
const params = tryMatchEndpoint(compiled, method, tokens);
|
|
381
|
+
if (params !== void 0) return {
|
|
382
|
+
endpoint: compiled.endpoint,
|
|
383
|
+
params
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* Compile and sort the endpoint table in-place.
|
|
390
|
+
*
|
|
391
|
+
* Called by `onInit` — the one-time per-isolate setup. Sorts `state.table` by
|
|
392
|
+
* specificity (descending), validates that no endpoint path contains duplicate
|
|
393
|
+
* `{param}` names, and sets `state.compiled = true` to guard re-entry.
|
|
394
|
+
*
|
|
395
|
+
* @param state - The mutable server state whose `table` should be compiled.
|
|
396
|
+
* @throws {Error} With `[moku-worker]` prefix when a path has duplicate param names.
|
|
397
|
+
* @example
|
|
398
|
+
* ```typescript
|
|
399
|
+
* // Called inside serverPlugin.onInit:
|
|
400
|
+
* compileServerState(ctx.state);
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
const compileServerState = (state) => {
|
|
404
|
+
if (state.compiled) return;
|
|
405
|
+
state.table.sort(bySpecificityDesc);
|
|
406
|
+
for (const compiled of state.table) {
|
|
407
|
+
const seen = /* @__PURE__ */ new Set();
|
|
408
|
+
for (const segment of compiled.segments) {
|
|
409
|
+
if (!segment.param) continue;
|
|
410
|
+
if (seen.has(segment.value)) throw new Error(`[moku-worker] endpoint path "${compiled.endpoint.path}" has duplicate param "{${segment.value}}".\n Each {param} name in a path must be unique.`);
|
|
411
|
+
seen.add(segment.value);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
state.compiled = true;
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* Creates the initial (uncompiled) server state from a declarative endpoint list.
|
|
418
|
+
*
|
|
419
|
+
* Copies `endpoints` into a fresh mutable `CompiledEndpoint[]` — does NOT mutate
|
|
420
|
+
* the frozen config array. Sets `compiled = false`; `onInit` calls
|
|
421
|
+
* `compileServerState` to sort/validate and set `compiled = true`.
|
|
422
|
+
*
|
|
423
|
+
* The `match(method, path)` method is safe to call before `onInit` because
|
|
424
|
+
* `findBestMatch` re-sorts on every invocation.
|
|
425
|
+
*
|
|
426
|
+
* @param endpoints - The frozen declarative endpoint table from `config.endpoints`.
|
|
427
|
+
* @returns A fresh `ServerState` with `compiled = false`.
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* const state = createServerState(config.endpoints);
|
|
431
|
+
* const hit = state.match("GET", "/api/users");
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
434
|
+
const createServerState = (endpoints) => {
|
|
435
|
+
const table = endpoints.map((ep) => compileEndpoint(ep));
|
|
436
|
+
/**
|
|
437
|
+
* Match a method + pathname against the compiled table.
|
|
438
|
+
*
|
|
439
|
+
* @param method - Request method (or `"ALL"` for cron dispatch).
|
|
440
|
+
* @param path - Request URL pathname (or cron expression string).
|
|
441
|
+
* @returns Matched endpoint + extracted params, or `null` for no match.
|
|
442
|
+
* @example
|
|
443
|
+
* ```typescript
|
|
444
|
+
* state.match("GET", "/api/users");
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
const match = (method, path) => {
|
|
448
|
+
return findBestMatch(table, method, path.split("/").filter(Boolean));
|
|
449
|
+
};
|
|
450
|
+
return {
|
|
451
|
+
table,
|
|
452
|
+
compiled: false,
|
|
453
|
+
match
|
|
454
|
+
};
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Standard tier — HTTP routing + request/scheduled dispatch over a compiled
|
|
458
|
+
* endpoint table. Emits `server:matched` (per-plugin) plus global
|
|
459
|
+
* `request:start` / `request:end` declared in `WorkerEvents`.
|
|
460
|
+
*
|
|
461
|
+
* @see README.md
|
|
462
|
+
*/
|
|
463
|
+
const serverPlugin = createPlugin$1("server", {
|
|
464
|
+
events: (register) => register.map({ "server:matched": "An endpoint matched a request" }),
|
|
465
|
+
depends: [bindingsPlugin],
|
|
466
|
+
config: { endpoints: [] },
|
|
467
|
+
createState: ({ config }) => createServerState(config.endpoints),
|
|
468
|
+
api: (ctx) => createServerApi(ctx),
|
|
469
|
+
onInit: (ctx) => {
|
|
470
|
+
compileServerState(ctx.state);
|
|
471
|
+
},
|
|
472
|
+
helpers: { endpoint }
|
|
473
|
+
});
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/plugins/d1/types.ts
|
|
476
|
+
var types_exports = /* @__PURE__ */ __exportAll({});
|
|
477
|
+
//#endregion
|
|
478
|
+
//#region src/plugins/durable-objects/types.ts
|
|
479
|
+
var types_exports$1 = /* @__PURE__ */ __exportAll({});
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/plugins/queues/types.ts
|
|
482
|
+
var types_exports$2 = /* @__PURE__ */ __exportAll({});
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/plugins/server/types.ts
|
|
485
|
+
var types_exports$3 = /* @__PURE__ */ __exportAll({});
|
|
486
|
+
//#endregion
|
|
487
|
+
//#region src/plugins/storage/types.ts
|
|
488
|
+
var types_exports$4 = /* @__PURE__ */ __exportAll({});
|
|
489
|
+
const { createApp, createPlugin } = createCore(coreConfig, { plugins: [bindingsPlugin, serverPlugin] });
|
|
490
|
+
//#endregion
|
|
491
|
+
export { types_exports as D1, types_exports$1 as DurableObjects, types_exports$2 as Queues, types_exports$3 as Server, types_exports$4 as Storage, bindingsPlugin, createApp, createPlugin, d1Plugin, defineDurableObject, durableObjectsPlugin, endpoint, envPlugin, kvPlugin, logPlugin, queuesPlugin, serverPlugin, stagePlugin, storagePlugin };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|