@moku-labs/worker 0.1.3 → 0.2.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 CHANGED
@@ -33,7 +33,7 @@ export const app = createApp({
33
33
  server: {
34
34
  endpoints: [
35
35
  endpoint("/health").get(() => new Response("ok", { status: 200 })),
36
- endpoint("/api/data/{lang?}").get(({ params }) =>
36
+ endpoint("/api/data/{lang:?}").get(({ params }) =>
37
37
  Response.json({ lang: params.lang ?? "en" })
38
38
  ),
39
39
  endpoint("/users/{userId}").get(
package/dist/cli.cjs CHANGED
@@ -1,9 +1,31 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_storage = require("./storage-Sy--rX1T.cjs");
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ const require_storage = require("./storage-CgXl-dUA.cjs");
3
25
  let node_child_process = require("node:child_process");
4
26
  let node_fs_promises = require("node:fs/promises");
5
27
  let node_path = require("node:path");
6
- node_path = require_storage.__toESM(node_path, 1);
28
+ node_path = __toESM(node_path, 1);
7
29
  let node_fs = require("node:fs");
8
30
  //#region src/plugins/deploy/runner.ts
9
31
  /**
@@ -596,6 +618,9 @@ const createDeployApi = (ctx) => ({
596
618
  * generates/updates wrangler config, uploads the R2 upload dir, and runs wrangler deploy.
597
619
  * Also supports a universal path: run({ manifest }) uses a caller-supplied manifest verbatim.
598
620
  *
621
+ * Emits only the global events `deploy:phase`, `deploy:complete`, and `provision:resource`
622
+ * (declared in WorkerEvents — no per-plugin events block).
623
+ *
599
624
  * @see README.md
600
625
  */
601
626
  const deployPlugin = require_storage.createPlugin("deploy", {
package/dist/cli.d.cts CHANGED
@@ -120,6 +120,9 @@ type ExternalManifest = {
120
120
  * generates/updates wrangler config, uploads the R2 upload dir, and runs wrangler deploy.
121
121
  * Also supports a universal path: run({ manifest }) uses a caller-supplied manifest verbatim.
122
122
  *
123
+ * Emits only the global events `deploy:phase`, `deploy:complete`, and `provision:resource`
124
+ * (declared in WorkerEvents — no per-plugin events block).
125
+ *
123
126
  * @see README.md
124
127
  */
125
128
  declare const deployPlugin: import("@moku-labs/core").PluginInstance<"deploy", Config, Record<string, never>, {
package/dist/cli.d.mts CHANGED
@@ -120,6 +120,9 @@ type ExternalManifest = {
120
120
  * generates/updates wrangler config, uploads the R2 upload dir, and runs wrangler deploy.
121
121
  * Also supports a universal path: run({ manifest }) uses a caller-supplied manifest verbatim.
122
122
  *
123
+ * Emits only the global events `deploy:phase`, `deploy:complete`, and `provision:resource`
124
+ * (declared in WorkerEvents — no per-plugin events block).
125
+ *
123
126
  * @see README.md
124
127
  */
125
128
  declare const deployPlugin: import("@moku-labs/core").PluginInstance<"deploy", Config, Record<string, never>, {
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as durableObjectsPlugin, n as queuesPlugin, o as d1Plugin, r as kvPlugin, t as storagePlugin, u as createPlugin } from "./storage-DCOQ4EAg.mjs";
1
+ import { i as durableObjectsPlugin, n as queuesPlugin, o as d1Plugin, r as kvPlugin, t as storagePlugin, u as createPlugin } from "./storage-COo-F38H.mjs";
2
2
  import { spawn } from "node:child_process";
3
3
  import { readdir, stat, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
@@ -594,6 +594,9 @@ const createDeployApi = (ctx) => ({
594
594
  * generates/updates wrangler config, uploads the R2 upload dir, and runs wrangler deploy.
595
595
  * Also supports a universal path: run({ manifest }) uses a caller-supplied manifest verbatim.
596
596
  *
597
+ * Emits only the global events `deploy:phase`, `deploy:complete`, and `provision:resource`
598
+ * (declared in WorkerEvents — no per-plugin events block).
599
+ *
597
600
  * @see README.md
598
601
  */
599
602
  const deployPlugin = createPlugin("deploy", {
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_storage = require("./storage-Sy--rX1T.cjs");
2
+ const require_storage = require("./storage-CgXl-dUA.cjs");
3
3
  let _moku_labs_common = require("@moku-labs/common");
4
4
  //#region src/plugins/server/api.ts
5
5
  /**
@@ -121,17 +121,21 @@ const makeEndpoint = (path, method, handler) => ({
121
121
  handler
122
122
  });
123
123
  /**
124
- * Build a typed `Endpoint`. `{name}` → required param; `{name?}` → optional param.
124
+ * Build a typed `Endpoint`. `{name}` → required param (`string`); `{name:?}` →
125
+ * optional param (`string | undefined`). The path template flows into each
126
+ * handler's `ctx.params` ({@link PathParams}), so a required `{id}` is typed
127
+ * `string` — no `?? ""` fallback needed.
125
128
  *
126
129
  * PURE factory (spec/03 §1): no ctx, no lifecycle, no side effects; safe to run
127
130
  * before `createApp`. Each verb method (`get`, `post`, …, `all`) returns the
128
131
  * truthful Endpoint value — `method: "ALL"` is never used as a `"get"` sentinel.
129
132
  *
130
- * @param path - Endpoint path, optionally with `{name}` / `{name?}` params.
133
+ * @template Path - The path template literal, inferred from `path`.
134
+ * @param path - Endpoint path, optionally with `{name}` / `{name:?}` params.
131
135
  * @returns A builder whose verb methods each return a typed `Endpoint`.
132
136
  * @example
133
137
  * ```typescript
134
- * endpoint("/api/data/{lang?}").get(({ params }) =>
138
+ * endpoint("/api/data/{lang:?}").get(({ params }) =>
135
139
  * Response.json({ lang: params.lang ?? "en" })
136
140
  * );
137
141
  * ```
@@ -232,27 +236,28 @@ const endpoint = (path) => ({
232
236
  const LITERAL_WEIGHT = 2;
233
237
  /** Specificity weight for a required param segment `{name}`. */
234
238
  const REQUIRED_PARAM_WEIGHT = 1;
235
- /** Specificity weight for an optional param segment `{name?}`. */
239
+ /** Specificity weight for an optional param segment `{name:?}`. */
236
240
  const OPTIONAL_PARAM_WEIGHT = 0;
237
241
  /**
238
242
  * Parse one path segment string into a typed `PathSegment`.
239
243
  *
240
- * `{name}` → required param; `{name?}` → optional param; anything else → literal.
244
+ * `{name}` → required param; `{name:?}` → optional param; anything else → literal.
245
+ * The `:?` optional suffix matches the `@moku-labs/web` router pattern.
241
246
  *
242
247
  * @param raw - A single path segment token (no leading slash).
243
248
  * @returns The parsed `PathSegment`.
244
249
  * @example
245
250
  * ```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 }
251
+ * parseSegment("{id}") // → { value: "id", param: true, optional: false }
252
+ * parseSegment("{id:?}") // → { value: "id", param: true, optional: true }
253
+ * parseSegment("api") // → { value: "api", param: false, optional: false }
249
254
  * ```
250
255
  */
251
256
  const parseSegment = (raw) => {
252
257
  if (raw.startsWith("{") && raw.endsWith("}")) {
253
258
  const inner = raw.slice(1, -1);
254
- if (inner.endsWith("?")) return {
255
- value: inner.slice(0, -1),
259
+ if (inner.endsWith(":?")) return {
260
+ value: inner.slice(0, -2),
256
261
  param: true,
257
262
  optional: true
258
263
  };
@@ -347,7 +352,7 @@ const tryMatchEndpoint = (compiled, method, tokens) => {
347
352
  * @example
348
353
  * ```typescript
349
354
  * const a = compileEndpoint(endpoint("/api/{id}").get(handler)); // specificity 3
350
- * const b = compileEndpoint(endpoint("/api/{id?}").get(handler)); // specificity 2
355
+ * const b = compileEndpoint(endpoint("/api/{id:?}").get(handler)); // specificity 2
351
356
  * [b, a].sort(bySpecificityDesc); // → [a, b] — higher specificity first
352
357
  * ```
353
358
  */
@@ -390,10 +395,12 @@ const findBestMatch = (table, method, tokens) => {
390
395
  *
391
396
  * Called by `onInit` — the one-time per-isolate setup. Sorts `state.table` by
392
397
  * specificity (descending), validates that no endpoint path contains duplicate
393
- * `{param}` names, and sets `state.compiled = true` to guard re-entry.
398
+ * `{param}` names or the retired `{name?}` optional syntax, and sets
399
+ * `state.compiled = true` to guard re-entry.
394
400
  *
395
401
  * @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.
402
+ * @throws {Error} With `[moku-worker]` prefix when a path has duplicate param
403
+ * names, or uses the old `{name?}` optional syntax (now `{name:?}`).
397
404
  * @example
398
405
  * ```typescript
399
406
  * // Called inside serverPlugin.onInit:
@@ -407,6 +414,10 @@ const compileServerState = (state) => {
407
414
  const seen = /* @__PURE__ */ new Set();
408
415
  for (const segment of compiled.segments) {
409
416
  if (!segment.param) continue;
417
+ if (segment.value.endsWith("?")) {
418
+ const name = segment.value.slice(0, -1);
419
+ throw new Error(`[moku-worker] endpoint path "${compiled.endpoint.path}" uses the old optional-param syntax "{${segment.value}}".\n Optional params now use the colon form (matching @moku-labs/web): write "{${name}:?}" instead of "{${name}?}".`);
420
+ }
410
421
  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
422
  seen.add(segment.value);
412
423
  }
@@ -471,62 +482,8 @@ const serverPlugin = require_storage.createPlugin("server", {
471
482
  },
472
483
  helpers: { endpoint }
473
484
  });
474
- //#endregion
475
- //#region src/plugins/bindings/types.ts
476
- var types_exports = /* @__PURE__ */ require_storage.__exportAll({});
477
- //#endregion
478
- //#region src/plugins/d1/types.ts
479
- var types_exports$1 = /* @__PURE__ */ require_storage.__exportAll({});
480
- //#endregion
481
- //#region src/plugins/durable-objects/types.ts
482
- var types_exports$2 = /* @__PURE__ */ require_storage.__exportAll({});
483
- //#endregion
484
- //#region src/plugins/queues/types.ts
485
- var types_exports$3 = /* @__PURE__ */ require_storage.__exportAll({});
486
- //#endregion
487
- //#region src/plugins/server/types.ts
488
- var types_exports$4 = /* @__PURE__ */ require_storage.__exportAll({});
489
- //#endregion
490
- //#region src/plugins/storage/types.ts
491
- var types_exports$5 = /* @__PURE__ */ require_storage.__exportAll({});
492
485
  const { createApp, createPlugin } = require_storage.createCore(require_storage.coreConfig, { plugins: [require_storage.bindingsPlugin, serverPlugin] });
493
486
  //#endregion
494
- Object.defineProperty(exports, "Bindings", {
495
- enumerable: true,
496
- get: function() {
497
- return types_exports;
498
- }
499
- });
500
- Object.defineProperty(exports, "D1", {
501
- enumerable: true,
502
- get: function() {
503
- return types_exports$1;
504
- }
505
- });
506
- Object.defineProperty(exports, "DurableObjects", {
507
- enumerable: true,
508
- get: function() {
509
- return types_exports$2;
510
- }
511
- });
512
- Object.defineProperty(exports, "Queues", {
513
- enumerable: true,
514
- get: function() {
515
- return types_exports$3;
516
- }
517
- });
518
- Object.defineProperty(exports, "Server", {
519
- enumerable: true,
520
- get: function() {
521
- return types_exports$4;
522
- }
523
- });
524
- Object.defineProperty(exports, "Storage", {
525
- enumerable: true,
526
- get: function() {
527
- return types_exports$5;
528
- }
529
- });
530
487
  exports.bindingsPlugin = require_storage.bindingsPlugin;
531
488
  exports.createApp = createApp;
532
489
  exports.createPlugin = createPlugin;
package/dist/index.d.cts CHANGED
@@ -64,19 +64,19 @@ type Context = PluginCtx<Config$4, Record<string, never>, WorkerEvents>;
64
64
  */
65
65
  declare const bindingsPlugin: import("@moku-labs/core").PluginInstance<"bindings", Config$4, Record<string, never>, BindingsApi, {}> & Record<never, never>;
66
66
  declare namespace types_d_exports$4 {
67
- export { Api$3 as Api, CompiledEndpoint, Endpoint, EndpointHandler, MatchResult, Method, PathSegment, RequestContext, RequireFn, ServerConfig, ServerCtx, ServerEvents, ServerState };
67
+ export { Api$3 as Api, CompiledEndpoint, Endpoint, EndpointHandler, MatchResult, Method, PathParams, PathSegment, RequestContext, RequireFn, ServerConfig, ServerCtx, ServerEvents, ServerState };
68
68
  }
69
69
  /** HTTP method an endpoint matches; "ALL" matches any verb. */
70
70
  type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "ALL";
71
- /** One parsed path segment: a literal, a required `{name}`, or an optional `{name?}`. */
71
+ /** One parsed path segment: a literal, a required `{name}`, or an optional `{name:?}`. */
72
72
  type PathSegment = {
73
- /** The literal text, or the param name when a param. */readonly value: string; /** Whether this segment is a `{name}` / `{name?}` parameter. */
74
- readonly param: boolean; /** Whether the param is optional (`{name?}`). */
73
+ /** The literal text, or the param name when a param. */readonly value: string; /** Whether this segment is a `{name}` / `{name:?}` parameter. */
74
+ readonly param: boolean; /** Whether the param is optional (`{name:?}`). */
75
75
  readonly optional: boolean;
76
76
  };
77
77
  /** A declarative endpoint produced by the pure endpoint() builder. */
78
78
  type Endpoint = {
79
- /** Endpoint path, optionally with `{name}` / `{name?}` params. */readonly path: string; /** HTTP method or "ALL". */
79
+ /** Endpoint path, optionally with `{name}` / `{name:?}` params. */readonly path: string; /** HTTP method or "ALL". */
80
80
  readonly method: Method; /** The handler invoked on a match. */
81
81
  readonly handler: EndpointHandler;
82
82
  };
@@ -125,14 +125,55 @@ type ApiOf<P> = P extends {
125
125
  } ? A : never;
126
126
  /** Cross-plugin reach used inside handlers: require(plugin) returns that plugin's API. Mirrors ctx.require. */
127
127
  type RequireFn = <P extends AnyPlugin>(plugin: P) => ApiOf<P>;
128
- /** A request handler: receives the per-request context, returns a Response. */
129
- type EndpointHandler = (ctx: RequestContext) => Response | Promise<Response>;
130
- /** Fresh per-request object threaded to each EndpointHandler. */
131
- type RequestContext = {
128
+ /**
129
+ * Prettify an intersection into a single flat object type for readable hovers.
130
+ * Homomorphic mapped type preserves each property's `?` optionality modifier.
131
+ */
132
+ type Prettify<T> = { [K in keyof T]: T[K] } & {};
133
+ /**
134
+ * Required param names in a path template, as a string union (`never` if none).
135
+ * Walks each `{...}` segment: collects a bare `{name}`, skips an optional `{name:?}`.
136
+ */
137
+ type RequiredParamNames<Path extends string> = Path extends `${string}{${infer Rest}` ? Rest extends `${infer Body}}${infer Tail}` ? Body extends `${string}:?` ? RequiredParamNames<Tail> : Body | RequiredParamNames<Tail> : never : never;
138
+ /**
139
+ * Optional param names in a path template with the `:?` suffix stripped, as a
140
+ * string union (`never` if none). Collects `{name:?}`, skips a bare `{name}`.
141
+ */
142
+ type OptionalParamNames<Path extends string> = Path extends `${string}{${infer Rest}` ? Rest extends `${infer Body}}${infer Tail}` ? Body extends `${infer Name}:?` ? Name | OptionalParamNames<Tail> : OptionalParamNames<Tail> : never : never;
143
+ /**
144
+ * Map a path template to its typed `params` object: a required `{name}` becomes
145
+ * `name: string`; an optional `{name:?}` becomes `name?: string`. A non-literal
146
+ * `string` path (e.g. one assembled at runtime) widens to the permissive
147
+ * `Record<string, string | undefined>`.
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * type P = PathParams<"/boards/{id}/data/{lang:?}">;
152
+ * // { id: string; lang?: string }
153
+ * ```
154
+ */
155
+ type PathParams<Path extends string> = string extends Path ? Record<string, string | undefined> : Prettify<{ [K in RequiredParamNames<Path>]: string } & { [K in OptionalParamNames<Path>]?: string }>;
156
+ /**
157
+ * A request handler: receives the per-request context, returns a Response.
158
+ *
159
+ * @template Params - Path-params shape, inferred by the `endpoint()` builder
160
+ * from the path template ({@link PathParams}) — a required `{name}` is
161
+ * `string`, an optional `{name:?}` is `string | undefined`. Defaults to the
162
+ * permissive `Record<string, string | undefined>` for hand-written handler types.
163
+ */
164
+ type EndpointHandler<Params = Record<string, string | undefined>> = (ctx: RequestContext<Params>) => Response | Promise<Response>;
165
+ /**
166
+ * Fresh per-request object threaded to each EndpointHandler.
167
+ *
168
+ * @template Params - Path-params shape for `params`, inferred from the path
169
+ * template by the `endpoint()` builder ({@link PathParams}). Defaults to the
170
+ * permissive `Record<string, string | undefined>`.
171
+ */
172
+ type RequestContext<Params = Record<string, string | undefined>> = {
132
173
  /** The incoming request. */readonly request: Request; /** Per-request Cloudflare bindings — threaded on the stack, NEVER stored in state. */
133
174
  readonly env: WorkerEnv; /** waitUntil / passThroughOnException. */
134
- readonly exec: ExecutionContext; /** Path params extracted from the matched endpoint. */
135
- readonly params: Record<string, string | undefined>; /** Parsed request URL. */
175
+ readonly exec: ExecutionContext; /** Path params extracted from the matched endpoint, typed from the path template. */
176
+ readonly params: Params; /** Parsed request URL. */
136
177
  readonly url: URL; /** Cross-plugin reach for handlers (e.g. require(bindingsPlugin)). */
137
178
  readonly require: RequireFn; /** Presence check for an optional plugin. */
138
179
  readonly has: (name: string) => boolean;
@@ -175,12 +216,14 @@ type Api$3 = {
175
216
  /**
176
217
  * Fluent builder whose verb methods each return a typed `Endpoint`.
177
218
  *
219
+ * @template Path - The path template literal, used to infer each handler's
220
+ * typed `ctx.params` ({@link PathParams}).
178
221
  * @example
179
222
  * ```typescript
180
- * const e = endpoint("/api/{id}").get(handler);
223
+ * const e = endpoint("/api/{id}").get(({ params }) => Response.json({ id: params.id }));
181
224
  * ```
182
225
  */
183
- type EndpointBuilder = {
226
+ type EndpointBuilder<Path extends string> = {
184
227
  /**
185
228
  * Build a GET endpoint bound to this path.
186
229
  *
@@ -191,7 +234,7 @@ type EndpointBuilder = {
191
234
  * endpoint("/health").get(() => new Response("ok"));
192
235
  * ```
193
236
  */
194
- get(handler: EndpointHandler): Endpoint;
237
+ get(handler: EndpointHandler<PathParams<Path>>): Endpoint;
195
238
  /**
196
239
  * Build a POST endpoint bound to this path.
197
240
  *
@@ -202,7 +245,7 @@ type EndpointBuilder = {
202
245
  * endpoint("/users").post(({ request }) => Response.json({ created: true }, { status: 201 }));
203
246
  * ```
204
247
  */
205
- post(handler: EndpointHandler): Endpoint;
248
+ post(handler: EndpointHandler<PathParams<Path>>): Endpoint;
206
249
  /**
207
250
  * Build a PUT endpoint bound to this path.
208
251
  *
@@ -213,7 +256,7 @@ type EndpointBuilder = {
213
256
  * endpoint("/users/{id}").put(({ params }) => Response.json({ updated: params.id }));
214
257
  * ```
215
258
  */
216
- put(handler: EndpointHandler): Endpoint;
259
+ put(handler: EndpointHandler<PathParams<Path>>): Endpoint;
217
260
  /**
218
261
  * Build a PATCH endpoint bound to this path.
219
262
  *
@@ -224,7 +267,7 @@ type EndpointBuilder = {
224
267
  * endpoint("/users/{id}").patch(({ params }) => Response.json({ patched: params.id }));
225
268
  * ```
226
269
  */
227
- patch(handler: EndpointHandler): Endpoint;
270
+ patch(handler: EndpointHandler<PathParams<Path>>): Endpoint;
228
271
  /**
229
272
  * Build a DELETE endpoint bound to this path.
230
273
  *
@@ -235,7 +278,7 @@ type EndpointBuilder = {
235
278
  * endpoint("/users/{id}").delete(() => new Response(null, { status: 204 }));
236
279
  * ```
237
280
  */
238
- delete(handler: EndpointHandler): Endpoint;
281
+ delete(handler: EndpointHandler<PathParams<Path>>): Endpoint;
239
282
  /**
240
283
  * Build a HEAD endpoint bound to this path.
241
284
  *
@@ -246,7 +289,7 @@ type EndpointBuilder = {
246
289
  * endpoint("/health").head(() => new Response(null, { status: 200 }));
247
290
  * ```
248
291
  */
249
- head(handler: EndpointHandler): Endpoint;
292
+ head(handler: EndpointHandler<PathParams<Path>>): Endpoint;
250
293
  /**
251
294
  * Build an OPTIONS endpoint bound to this path.
252
295
  *
@@ -257,7 +300,7 @@ type EndpointBuilder = {
257
300
  * endpoint("/api").options(() => new Response(null, { headers: { Allow: "GET, POST" } }));
258
301
  * ```
259
302
  */
260
- options(handler: EndpointHandler): Endpoint;
303
+ options(handler: EndpointHandler<PathParams<Path>>): Endpoint;
261
304
  /**
262
305
  * Build an ALL-method endpoint bound to this path (`method: "ALL"` — matches any verb).
263
306
  *
@@ -268,25 +311,29 @@ type EndpointBuilder = {
268
311
  * endpoint("0 * * * *").all(async () => new Response("cron done"));
269
312
  * ```
270
313
  */
271
- all(handler: EndpointHandler): Endpoint;
314
+ all(handler: EndpointHandler<PathParams<Path>>): Endpoint;
272
315
  };
273
316
  /**
274
- * Build a typed `Endpoint`. `{name}` → required param; `{name?}` → optional param.
317
+ * Build a typed `Endpoint`. `{name}` → required param (`string`); `{name:?}` →
318
+ * optional param (`string | undefined`). The path template flows into each
319
+ * handler's `ctx.params` ({@link PathParams}), so a required `{id}` is typed
320
+ * `string` — no `?? ""` fallback needed.
275
321
  *
276
322
  * PURE factory (spec/03 §1): no ctx, no lifecycle, no side effects; safe to run
277
323
  * before `createApp`. Each verb method (`get`, `post`, …, `all`) returns the
278
324
  * truthful Endpoint value — `method: "ALL"` is never used as a `"get"` sentinel.
279
325
  *
280
- * @param path - Endpoint path, optionally with `{name}` / `{name?}` params.
326
+ * @template Path - The path template literal, inferred from `path`.
327
+ * @param path - Endpoint path, optionally with `{name}` / `{name:?}` params.
281
328
  * @returns A builder whose verb methods each return a typed `Endpoint`.
282
329
  * @example
283
330
  * ```typescript
284
- * endpoint("/api/data/{lang?}").get(({ params }) =>
331
+ * endpoint("/api/data/{lang:?}").get(({ params }) =>
285
332
  * Response.json({ lang: params.lang ?? "en" })
286
333
  * );
287
334
  * ```
288
335
  */
289
- declare const endpoint: (path: string) => EndpointBuilder;
336
+ declare const endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
290
337
  declare namespace types_d_exports$1 {
291
338
  export { Api$2 as Api, Config$3 as Config, D1Ctx, DeployManifest$2 as DeployManifest };
292
339
  }
@@ -442,8 +489,19 @@ type Api$1 = {
442
489
  */
443
490
  deployManifest(): DeployManifest$1;
444
491
  };
445
- /** Internal context type — own config first, no state, no DO events. */
446
- type Ctx$1 = PluginCtx<Config$2, Record<string, never>, WorkerEvents>;
492
+ /**
493
+ * Internal context type own config first, no state, no DO events.
494
+ * Intersected with a narrow `require` typed to the one dependency durableObjects resolves.
495
+ */
496
+ type Ctx$1 = PluginCtx<Config$2, Record<string, never>, WorkerEvents> & {
497
+ /**
498
+ * Resolve a dependency plugin's api. durableObjects only ever resolves `bindingsPlugin`.
499
+ *
500
+ * @param plugin - The bindingsPlugin instance.
501
+ * @returns The resolved bindings api.
502
+ */
503
+ require(plugin: typeof bindingsPlugin): BindingsApi;
504
+ };
447
505
  //#endregion
448
506
  //#region src/plugins/durable-objects/helpers.d.ts
449
507
  /**
@@ -691,6 +749,8 @@ type Ctx = PluginCtx<Config$1, Record<string, never>, WorkerEvents & QueueEvents
691
749
  * `events` is declared first and via `register.map<QueueEvents>` so the plugin's own events infer
692
750
  * into the factory context; the api wiring is therefore arrow-wrapped (contextually typed).
693
751
  *
752
+ * Emits the plugin-local `queue:message` event after each consumed message.
753
+ *
694
754
  * @see README.md
695
755
  */
696
756
  declare const queuesPlugin: import("@moku-labs/core").PluginInstance<"queues", Config$1, Record<string, never>, {
@@ -722,7 +782,7 @@ declare const serverPlugin: import("@moku-labs/core").PluginInstance<"server", S
722
782
  method: string;
723
783
  };
724
784
  }> & {
725
- endpoint: (path: string) => EndpointBuilder;
785
+ endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
726
786
  };
727
787
  //#endregion
728
788
  //#region src/plugins/storage/providers/types.d.ts
@@ -940,7 +1000,7 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
940
1000
  method: string;
941
1001
  };
942
1002
  }> & {
943
- endpoint: (path: string) => EndpointBuilder;
1003
+ endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
944
1004
  }) | ExtraPlugins[number], [...ExtraPlugins], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common").LogConfig, import("@moku-labs/common").LogState, import("@moku-labs/common").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common").EnvConfig, import("@moku-labs/common").EnvState, import("@moku-labs/common").EnvApi>, import("@moku-labs/core").CorePluginInstance<"stage", {
945
1005
  stage: "production" | "development" | "test";
946
1006
  }, Record<string, never>, {
@@ -953,7 +1013,7 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
953
1013
  method: string;
954
1014
  };
955
1015
  }> & {
956
- endpoint: (path: string) => EndpointBuilder;
1016
+ endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
957
1017
  }) | ExtraPlugins[number], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common").LogConfig, import("@moku-labs/common").LogState, import("@moku-labs/common").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common").EnvConfig, import("@moku-labs/common").EnvState, import("@moku-labs/common").EnvApi>, import("@moku-labs/core").CorePluginInstance<"stage", {
958
1018
  stage: "production" | "development" | "test";
959
1019
  }, Record<string, never>, {
@@ -968,4 +1028,4 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
968
1028
  current: () => "production" | "development" | "test";
969
1029
  }>]>>;
970
1030
  //#endregion
971
- export { types_d_exports as Bindings, types_d_exports$1 as D1, types_d_exports$2 as DurableObjects, types_d_exports$3 as Queues, types_d_exports$4 as Server, type StageApi, types_d_exports$5 as Storage, type WorkerConfig, type WorkerEnv, type WorkerEvents, bindingsPlugin, createApp, createPlugin, d1Plugin, defineDurableObject, durableObjectsPlugin, endpoint, envPlugin, kvPlugin, logPlugin, queuesPlugin, serverPlugin, stagePlugin, storagePlugin };
1031
+ export { type types_d_exports as Bindings, type types_d_exports$1 as D1, type types_d_exports$2 as DurableObjects, type types_d_exports$3 as Queues, type types_d_exports$4 as Server, type StageApi, type types_d_exports$5 as Storage, type WorkerConfig, type WorkerEnv, type WorkerEvents, bindingsPlugin, createApp, createPlugin, d1Plugin, defineDurableObject, durableObjectsPlugin, endpoint, envPlugin, kvPlugin, logPlugin, queuesPlugin, serverPlugin, stagePlugin, storagePlugin };
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import { n as WorkerEnv, r as WorkerEvents, t as WorkerConfig } from "./config-A
2
2
  import { envPlugin, logPlugin } from "@moku-labs/common";
3
3
  import { PluginCtx, PluginInstance } from "@moku-labs/core";
4
4
 
5
- //#region src/plugins/bindings/types.d.ts
5
+ //#region \0rolldown/runtime.js
6
6
  declare namespace types_d_exports {
7
7
  export { BindingsApi, Config$4 as Config, Context };
8
8
  }
@@ -64,19 +64,19 @@ type Context = PluginCtx<Config$4, Record<string, never>, WorkerEvents>;
64
64
  */
65
65
  declare const bindingsPlugin: import("@moku-labs/core").PluginInstance<"bindings", Config$4, Record<string, never>, BindingsApi, {}> & Record<never, never>;
66
66
  declare namespace types_d_exports$4 {
67
- export { Api$3 as Api, CompiledEndpoint, Endpoint, EndpointHandler, MatchResult, Method, PathSegment, RequestContext, RequireFn, ServerConfig, ServerCtx, ServerEvents, ServerState };
67
+ export { Api$3 as Api, CompiledEndpoint, Endpoint, EndpointHandler, MatchResult, Method, PathParams, PathSegment, RequestContext, RequireFn, ServerConfig, ServerCtx, ServerEvents, ServerState };
68
68
  }
69
69
  /** HTTP method an endpoint matches; "ALL" matches any verb. */
70
70
  type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "ALL";
71
- /** One parsed path segment: a literal, a required `{name}`, or an optional `{name?}`. */
71
+ /** One parsed path segment: a literal, a required `{name}`, or an optional `{name:?}`. */
72
72
  type PathSegment = {
73
- /** The literal text, or the param name when a param. */readonly value: string; /** Whether this segment is a `{name}` / `{name?}` parameter. */
74
- readonly param: boolean; /** Whether the param is optional (`{name?}`). */
73
+ /** The literal text, or the param name when a param. */readonly value: string; /** Whether this segment is a `{name}` / `{name:?}` parameter. */
74
+ readonly param: boolean; /** Whether the param is optional (`{name:?}`). */
75
75
  readonly optional: boolean;
76
76
  };
77
77
  /** A declarative endpoint produced by the pure endpoint() builder. */
78
78
  type Endpoint = {
79
- /** Endpoint path, optionally with `{name}` / `{name?}` params. */readonly path: string; /** HTTP method or "ALL". */
79
+ /** Endpoint path, optionally with `{name}` / `{name:?}` params. */readonly path: string; /** HTTP method or "ALL". */
80
80
  readonly method: Method; /** The handler invoked on a match. */
81
81
  readonly handler: EndpointHandler;
82
82
  };
@@ -125,14 +125,55 @@ type ApiOf<P> = P extends {
125
125
  } ? A : never;
126
126
  /** Cross-plugin reach used inside handlers: require(plugin) returns that plugin's API. Mirrors ctx.require. */
127
127
  type RequireFn = <P extends AnyPlugin>(plugin: P) => ApiOf<P>;
128
- /** A request handler: receives the per-request context, returns a Response. */
129
- type EndpointHandler = (ctx: RequestContext) => Response | Promise<Response>;
130
- /** Fresh per-request object threaded to each EndpointHandler. */
131
- type RequestContext = {
128
+ /**
129
+ * Prettify an intersection into a single flat object type for readable hovers.
130
+ * Homomorphic mapped type preserves each property's `?` optionality modifier.
131
+ */
132
+ type Prettify<T> = { [K in keyof T]: T[K] } & {};
133
+ /**
134
+ * Required param names in a path template, as a string union (`never` if none).
135
+ * Walks each `{...}` segment: collects a bare `{name}`, skips an optional `{name:?}`.
136
+ */
137
+ type RequiredParamNames<Path extends string> = Path extends `${string}{${infer Rest}` ? Rest extends `${infer Body}}${infer Tail}` ? Body extends `${string}:?` ? RequiredParamNames<Tail> : Body | RequiredParamNames<Tail> : never : never;
138
+ /**
139
+ * Optional param names in a path template with the `:?` suffix stripped, as a
140
+ * string union (`never` if none). Collects `{name:?}`, skips a bare `{name}`.
141
+ */
142
+ type OptionalParamNames<Path extends string> = Path extends `${string}{${infer Rest}` ? Rest extends `${infer Body}}${infer Tail}` ? Body extends `${infer Name}:?` ? Name | OptionalParamNames<Tail> : OptionalParamNames<Tail> : never : never;
143
+ /**
144
+ * Map a path template to its typed `params` object: a required `{name}` becomes
145
+ * `name: string`; an optional `{name:?}` becomes `name?: string`. A non-literal
146
+ * `string` path (e.g. one assembled at runtime) widens to the permissive
147
+ * `Record<string, string | undefined>`.
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * type P = PathParams<"/boards/{id}/data/{lang:?}">;
152
+ * // { id: string; lang?: string }
153
+ * ```
154
+ */
155
+ type PathParams<Path extends string> = string extends Path ? Record<string, string | undefined> : Prettify<{ [K in RequiredParamNames<Path>]: string } & { [K in OptionalParamNames<Path>]?: string }>;
156
+ /**
157
+ * A request handler: receives the per-request context, returns a Response.
158
+ *
159
+ * @template Params - Path-params shape, inferred by the `endpoint()` builder
160
+ * from the path template ({@link PathParams}) — a required `{name}` is
161
+ * `string`, an optional `{name:?}` is `string | undefined`. Defaults to the
162
+ * permissive `Record<string, string | undefined>` for hand-written handler types.
163
+ */
164
+ type EndpointHandler<Params = Record<string, string | undefined>> = (ctx: RequestContext<Params>) => Response | Promise<Response>;
165
+ /**
166
+ * Fresh per-request object threaded to each EndpointHandler.
167
+ *
168
+ * @template Params - Path-params shape for `params`, inferred from the path
169
+ * template by the `endpoint()` builder ({@link PathParams}). Defaults to the
170
+ * permissive `Record<string, string | undefined>`.
171
+ */
172
+ type RequestContext<Params = Record<string, string | undefined>> = {
132
173
  /** The incoming request. */readonly request: Request; /** Per-request Cloudflare bindings — threaded on the stack, NEVER stored in state. */
133
174
  readonly env: WorkerEnv; /** waitUntil / passThroughOnException. */
134
- readonly exec: ExecutionContext; /** Path params extracted from the matched endpoint. */
135
- readonly params: Record<string, string | undefined>; /** Parsed request URL. */
175
+ readonly exec: ExecutionContext; /** Path params extracted from the matched endpoint, typed from the path template. */
176
+ readonly params: Params; /** Parsed request URL. */
136
177
  readonly url: URL; /** Cross-plugin reach for handlers (e.g. require(bindingsPlugin)). */
137
178
  readonly require: RequireFn; /** Presence check for an optional plugin. */
138
179
  readonly has: (name: string) => boolean;
@@ -175,12 +216,14 @@ type Api$3 = {
175
216
  /**
176
217
  * Fluent builder whose verb methods each return a typed `Endpoint`.
177
218
  *
219
+ * @template Path - The path template literal, used to infer each handler's
220
+ * typed `ctx.params` ({@link PathParams}).
178
221
  * @example
179
222
  * ```typescript
180
- * const e = endpoint("/api/{id}").get(handler);
223
+ * const e = endpoint("/api/{id}").get(({ params }) => Response.json({ id: params.id }));
181
224
  * ```
182
225
  */
183
- type EndpointBuilder = {
226
+ type EndpointBuilder<Path extends string> = {
184
227
  /**
185
228
  * Build a GET endpoint bound to this path.
186
229
  *
@@ -191,7 +234,7 @@ type EndpointBuilder = {
191
234
  * endpoint("/health").get(() => new Response("ok"));
192
235
  * ```
193
236
  */
194
- get(handler: EndpointHandler): Endpoint;
237
+ get(handler: EndpointHandler<PathParams<Path>>): Endpoint;
195
238
  /**
196
239
  * Build a POST endpoint bound to this path.
197
240
  *
@@ -202,7 +245,7 @@ type EndpointBuilder = {
202
245
  * endpoint("/users").post(({ request }) => Response.json({ created: true }, { status: 201 }));
203
246
  * ```
204
247
  */
205
- post(handler: EndpointHandler): Endpoint;
248
+ post(handler: EndpointHandler<PathParams<Path>>): Endpoint;
206
249
  /**
207
250
  * Build a PUT endpoint bound to this path.
208
251
  *
@@ -213,7 +256,7 @@ type EndpointBuilder = {
213
256
  * endpoint("/users/{id}").put(({ params }) => Response.json({ updated: params.id }));
214
257
  * ```
215
258
  */
216
- put(handler: EndpointHandler): Endpoint;
259
+ put(handler: EndpointHandler<PathParams<Path>>): Endpoint;
217
260
  /**
218
261
  * Build a PATCH endpoint bound to this path.
219
262
  *
@@ -224,7 +267,7 @@ type EndpointBuilder = {
224
267
  * endpoint("/users/{id}").patch(({ params }) => Response.json({ patched: params.id }));
225
268
  * ```
226
269
  */
227
- patch(handler: EndpointHandler): Endpoint;
270
+ patch(handler: EndpointHandler<PathParams<Path>>): Endpoint;
228
271
  /**
229
272
  * Build a DELETE endpoint bound to this path.
230
273
  *
@@ -235,7 +278,7 @@ type EndpointBuilder = {
235
278
  * endpoint("/users/{id}").delete(() => new Response(null, { status: 204 }));
236
279
  * ```
237
280
  */
238
- delete(handler: EndpointHandler): Endpoint;
281
+ delete(handler: EndpointHandler<PathParams<Path>>): Endpoint;
239
282
  /**
240
283
  * Build a HEAD endpoint bound to this path.
241
284
  *
@@ -246,7 +289,7 @@ type EndpointBuilder = {
246
289
  * endpoint("/health").head(() => new Response(null, { status: 200 }));
247
290
  * ```
248
291
  */
249
- head(handler: EndpointHandler): Endpoint;
292
+ head(handler: EndpointHandler<PathParams<Path>>): Endpoint;
250
293
  /**
251
294
  * Build an OPTIONS endpoint bound to this path.
252
295
  *
@@ -257,7 +300,7 @@ type EndpointBuilder = {
257
300
  * endpoint("/api").options(() => new Response(null, { headers: { Allow: "GET, POST" } }));
258
301
  * ```
259
302
  */
260
- options(handler: EndpointHandler): Endpoint;
303
+ options(handler: EndpointHandler<PathParams<Path>>): Endpoint;
261
304
  /**
262
305
  * Build an ALL-method endpoint bound to this path (`method: "ALL"` — matches any verb).
263
306
  *
@@ -268,25 +311,29 @@ type EndpointBuilder = {
268
311
  * endpoint("0 * * * *").all(async () => new Response("cron done"));
269
312
  * ```
270
313
  */
271
- all(handler: EndpointHandler): Endpoint;
314
+ all(handler: EndpointHandler<PathParams<Path>>): Endpoint;
272
315
  };
273
316
  /**
274
- * Build a typed `Endpoint`. `{name}` → required param; `{name?}` → optional param.
317
+ * Build a typed `Endpoint`. `{name}` → required param (`string`); `{name:?}` →
318
+ * optional param (`string | undefined`). The path template flows into each
319
+ * handler's `ctx.params` ({@link PathParams}), so a required `{id}` is typed
320
+ * `string` — no `?? ""` fallback needed.
275
321
  *
276
322
  * PURE factory (spec/03 §1): no ctx, no lifecycle, no side effects; safe to run
277
323
  * before `createApp`. Each verb method (`get`, `post`, …, `all`) returns the
278
324
  * truthful Endpoint value — `method: "ALL"` is never used as a `"get"` sentinel.
279
325
  *
280
- * @param path - Endpoint path, optionally with `{name}` / `{name?}` params.
326
+ * @template Path - The path template literal, inferred from `path`.
327
+ * @param path - Endpoint path, optionally with `{name}` / `{name:?}` params.
281
328
  * @returns A builder whose verb methods each return a typed `Endpoint`.
282
329
  * @example
283
330
  * ```typescript
284
- * endpoint("/api/data/{lang?}").get(({ params }) =>
331
+ * endpoint("/api/data/{lang:?}").get(({ params }) =>
285
332
  * Response.json({ lang: params.lang ?? "en" })
286
333
  * );
287
334
  * ```
288
335
  */
289
- declare const endpoint: (path: string) => EndpointBuilder;
336
+ declare const endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
290
337
  declare namespace types_d_exports$1 {
291
338
  export { Api$2 as Api, Config$3 as Config, D1Ctx, DeployManifest$2 as DeployManifest };
292
339
  }
@@ -442,8 +489,19 @@ type Api$1 = {
442
489
  */
443
490
  deployManifest(): DeployManifest$1;
444
491
  };
445
- /** Internal context type — own config first, no state, no DO events. */
446
- type Ctx$1 = PluginCtx<Config$2, Record<string, never>, WorkerEvents>;
492
+ /**
493
+ * Internal context type own config first, no state, no DO events.
494
+ * Intersected with a narrow `require` typed to the one dependency durableObjects resolves.
495
+ */
496
+ type Ctx$1 = PluginCtx<Config$2, Record<string, never>, WorkerEvents> & {
497
+ /**
498
+ * Resolve a dependency plugin's api. durableObjects only ever resolves `bindingsPlugin`.
499
+ *
500
+ * @param plugin - The bindingsPlugin instance.
501
+ * @returns The resolved bindings api.
502
+ */
503
+ require(plugin: typeof bindingsPlugin): BindingsApi;
504
+ };
447
505
  //#endregion
448
506
  //#region src/plugins/durable-objects/helpers.d.ts
449
507
  /**
@@ -691,6 +749,8 @@ type Ctx = PluginCtx<Config$1, Record<string, never>, WorkerEvents & QueueEvents
691
749
  * `events` is declared first and via `register.map<QueueEvents>` so the plugin's own events infer
692
750
  * into the factory context; the api wiring is therefore arrow-wrapped (contextually typed).
693
751
  *
752
+ * Emits the plugin-local `queue:message` event after each consumed message.
753
+ *
694
754
  * @see README.md
695
755
  */
696
756
  declare const queuesPlugin: import("@moku-labs/core").PluginInstance<"queues", Config$1, Record<string, never>, {
@@ -722,7 +782,7 @@ declare const serverPlugin: import("@moku-labs/core").PluginInstance<"server", S
722
782
  method: string;
723
783
  };
724
784
  }> & {
725
- endpoint: (path: string) => EndpointBuilder;
785
+ endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
726
786
  };
727
787
  //#endregion
728
788
  //#region src/plugins/storage/providers/types.d.ts
@@ -940,7 +1000,7 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
940
1000
  method: string;
941
1001
  };
942
1002
  }> & {
943
- endpoint: (path: string) => EndpointBuilder;
1003
+ endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
944
1004
  }) | ExtraPlugins[number], [...ExtraPlugins], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common").LogConfig, import("@moku-labs/common").LogState, import("@moku-labs/common").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common").EnvConfig, import("@moku-labs/common").EnvState, import("@moku-labs/common").EnvApi>, import("@moku-labs/core").CorePluginInstance<"stage", {
945
1005
  stage: "production" | "development" | "test";
946
1006
  }, Record<string, never>, {
@@ -953,7 +1013,7 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
953
1013
  method: string;
954
1014
  };
955
1015
  }> & {
956
- endpoint: (path: string) => EndpointBuilder;
1016
+ endpoint: <Path extends string>(path: Path) => EndpointBuilder<Path>;
957
1017
  }) | ExtraPlugins[number], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common").LogConfig, import("@moku-labs/common").LogState, import("@moku-labs/common").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common").EnvConfig, import("@moku-labs/common").EnvState, import("@moku-labs/common").EnvApi>, import("@moku-labs/core").CorePluginInstance<"stage", {
958
1018
  stage: "production" | "development" | "test";
959
1019
  }, Record<string, never>, {
@@ -968,4 +1028,4 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
968
1028
  current: () => "production" | "development" | "test";
969
1029
  }>]>>;
970
1030
  //#endregion
971
- export { types_d_exports as Bindings, types_d_exports$1 as D1, types_d_exports$2 as DurableObjects, types_d_exports$3 as Queues, types_d_exports$4 as Server, type StageApi, types_d_exports$5 as Storage, type WorkerConfig, type WorkerEnv, type WorkerEvents, bindingsPlugin, createApp, createPlugin, d1Plugin, defineDurableObject, durableObjectsPlugin, endpoint, envPlugin, kvPlugin, logPlugin, queuesPlugin, serverPlugin, stagePlugin, storagePlugin };
1031
+ export { type types_d_exports as Bindings, type types_d_exports$1 as D1, type types_d_exports$2 as DurableObjects, type types_d_exports$3 as Queues, type types_d_exports$4 as Server, type StageApi, type types_d_exports$5 as Storage, type WorkerConfig, type WorkerEnv, type WorkerEvents, bindingsPlugin, createApp, createPlugin, d1Plugin, defineDurableObject, durableObjectsPlugin, endpoint, envPlugin, kvPlugin, logPlugin, queuesPlugin, serverPlugin, stagePlugin, storagePlugin };
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
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-DCOQ4EAg.mjs";
1
+ 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-COo-F38H.mjs";
3
2
  import { envPlugin, logPlugin } from "@moku-labs/common";
4
3
  //#region src/plugins/server/api.ts
5
4
  /**
@@ -121,17 +120,21 @@ const makeEndpoint = (path, method, handler) => ({
121
120
  handler
122
121
  });
123
122
  /**
124
- * Build a typed `Endpoint`. `{name}` → required param; `{name?}` → optional param.
123
+ * Build a typed `Endpoint`. `{name}` → required param (`string`); `{name:?}` →
124
+ * optional param (`string | undefined`). The path template flows into each
125
+ * handler's `ctx.params` ({@link PathParams}), so a required `{id}` is typed
126
+ * `string` — no `?? ""` fallback needed.
125
127
  *
126
128
  * PURE factory (spec/03 §1): no ctx, no lifecycle, no side effects; safe to run
127
129
  * before `createApp`. Each verb method (`get`, `post`, …, `all`) returns the
128
130
  * truthful Endpoint value — `method: "ALL"` is never used as a `"get"` sentinel.
129
131
  *
130
- * @param path - Endpoint path, optionally with `{name}` / `{name?}` params.
132
+ * @template Path - The path template literal, inferred from `path`.
133
+ * @param path - Endpoint path, optionally with `{name}` / `{name:?}` params.
131
134
  * @returns A builder whose verb methods each return a typed `Endpoint`.
132
135
  * @example
133
136
  * ```typescript
134
- * endpoint("/api/data/{lang?}").get(({ params }) =>
137
+ * endpoint("/api/data/{lang:?}").get(({ params }) =>
135
138
  * Response.json({ lang: params.lang ?? "en" })
136
139
  * );
137
140
  * ```
@@ -232,27 +235,28 @@ const endpoint = (path) => ({
232
235
  const LITERAL_WEIGHT = 2;
233
236
  /** Specificity weight for a required param segment `{name}`. */
234
237
  const REQUIRED_PARAM_WEIGHT = 1;
235
- /** Specificity weight for an optional param segment `{name?}`. */
238
+ /** Specificity weight for an optional param segment `{name:?}`. */
236
239
  const OPTIONAL_PARAM_WEIGHT = 0;
237
240
  /**
238
241
  * Parse one path segment string into a typed `PathSegment`.
239
242
  *
240
- * `{name}` → required param; `{name?}` → optional param; anything else → literal.
243
+ * `{name}` → required param; `{name:?}` → optional param; anything else → literal.
244
+ * The `:?` optional suffix matches the `@moku-labs/web` router pattern.
241
245
  *
242
246
  * @param raw - A single path segment token (no leading slash).
243
247
  * @returns The parsed `PathSegment`.
244
248
  * @example
245
249
  * ```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 }
250
+ * parseSegment("{id}") // → { value: "id", param: true, optional: false }
251
+ * parseSegment("{id:?}") // → { value: "id", param: true, optional: true }
252
+ * parseSegment("api") // → { value: "api", param: false, optional: false }
249
253
  * ```
250
254
  */
251
255
  const parseSegment = (raw) => {
252
256
  if (raw.startsWith("{") && raw.endsWith("}")) {
253
257
  const inner = raw.slice(1, -1);
254
- if (inner.endsWith("?")) return {
255
- value: inner.slice(0, -1),
258
+ if (inner.endsWith(":?")) return {
259
+ value: inner.slice(0, -2),
256
260
  param: true,
257
261
  optional: true
258
262
  };
@@ -347,7 +351,7 @@ const tryMatchEndpoint = (compiled, method, tokens) => {
347
351
  * @example
348
352
  * ```typescript
349
353
  * const a = compileEndpoint(endpoint("/api/{id}").get(handler)); // specificity 3
350
- * const b = compileEndpoint(endpoint("/api/{id?}").get(handler)); // specificity 2
354
+ * const b = compileEndpoint(endpoint("/api/{id:?}").get(handler)); // specificity 2
351
355
  * [b, a].sort(bySpecificityDesc); // → [a, b] — higher specificity first
352
356
  * ```
353
357
  */
@@ -390,10 +394,12 @@ const findBestMatch = (table, method, tokens) => {
390
394
  *
391
395
  * Called by `onInit` — the one-time per-isolate setup. Sorts `state.table` by
392
396
  * specificity (descending), validates that no endpoint path contains duplicate
393
- * `{param}` names, and sets `state.compiled = true` to guard re-entry.
397
+ * `{param}` names or the retired `{name?}` optional syntax, and sets
398
+ * `state.compiled = true` to guard re-entry.
394
399
  *
395
400
  * @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.
401
+ * @throws {Error} With `[moku-worker]` prefix when a path has duplicate param
402
+ * names, or uses the old `{name?}` optional syntax (now `{name:?}`).
397
403
  * @example
398
404
  * ```typescript
399
405
  * // Called inside serverPlugin.onInit:
@@ -407,6 +413,10 @@ const compileServerState = (state) => {
407
413
  const seen = /* @__PURE__ */ new Set();
408
414
  for (const segment of compiled.segments) {
409
415
  if (!segment.param) continue;
416
+ if (segment.value.endsWith("?")) {
417
+ const name = segment.value.slice(0, -1);
418
+ throw new Error(`[moku-worker] endpoint path "${compiled.endpoint.path}" uses the old optional-param syntax "{${segment.value}}".\n Optional params now use the colon form (matching @moku-labs/web): write "{${name}:?}" instead of "{${name}?}".`);
419
+ }
410
420
  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
421
  seen.add(segment.value);
412
422
  }
@@ -471,24 +481,6 @@ const serverPlugin = createPlugin$1("server", {
471
481
  },
472
482
  helpers: { endpoint }
473
483
  });
474
- //#endregion
475
- //#region src/plugins/bindings/types.ts
476
- var types_exports = /* @__PURE__ */ __exportAll({});
477
- //#endregion
478
- //#region src/plugins/d1/types.ts
479
- var types_exports$1 = /* @__PURE__ */ __exportAll({});
480
- //#endregion
481
- //#region src/plugins/durable-objects/types.ts
482
- var types_exports$2 = /* @__PURE__ */ __exportAll({});
483
- //#endregion
484
- //#region src/plugins/queues/types.ts
485
- var types_exports$3 = /* @__PURE__ */ __exportAll({});
486
- //#endregion
487
- //#region src/plugins/server/types.ts
488
- var types_exports$4 = /* @__PURE__ */ __exportAll({});
489
- //#endregion
490
- //#region src/plugins/storage/types.ts
491
- var types_exports$5 = /* @__PURE__ */ __exportAll({});
492
484
  const { createApp, createPlugin } = createCore(coreConfig, { plugins: [bindingsPlugin, serverPlugin] });
493
485
  //#endregion
494
- export { types_exports as Bindings, types_exports$1 as D1, types_exports$2 as DurableObjects, types_exports$3 as Queues, types_exports$4 as Server, types_exports$5 as Storage, bindingsPlugin, createApp, createPlugin, d1Plugin, defineDurableObject, durableObjectsPlugin, endpoint, envPlugin, kvPlugin, logPlugin, queuesPlugin, serverPlugin, stagePlugin, storagePlugin };
486
+ export { bindingsPlugin, createApp, createPlugin, d1Plugin, defineDurableObject, durableObjectsPlugin, endpoint, envPlugin, kvPlugin, logPlugin, queuesPlugin, serverPlugin, stagePlugin, storagePlugin };
@@ -661,6 +661,8 @@ const createQueuesApi = (ctx) => {
661
661
  * `events` is declared first and via `register.map<QueueEvents>` so the plugin's own events infer
662
662
  * into the factory context; the api wiring is therefore arrow-wrapped (contextually typed).
663
663
  *
664
+ * Emits the plugin-local `queue:message` event after each consumed message.
665
+ *
664
666
  * @see README.md
665
667
  */
666
668
  const queuesPlugin = createPlugin("queues", {
@@ -1,34 +1,3 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __exportAll = (all, no_symbols) => {
9
- let target = {};
10
- for (var name in all) __defProp(target, name, {
11
- get: all[name],
12
- enumerable: true
13
- });
14
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
15
- return target;
16
- };
17
- var __copyProps = (to, from, except, desc) => {
18
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
19
- key = keys[i];
20
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
21
- get: ((k) => from[k]).bind(null, key),
22
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
23
- });
24
- }
25
- return to;
26
- };
27
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
- value: mod,
29
- enumerable: true
30
- }) : target, mod));
31
- //#endregion
32
1
  let _moku_labs_common = require("@moku-labs/common");
33
2
  let _moku_labs_core = require("@moku-labs/core");
34
3
  /**
@@ -692,6 +661,8 @@ const createQueuesApi = (ctx) => {
692
661
  * `events` is declared first and via `register.map<QueueEvents>` so the plugin's own events infer
693
662
  * into the factory context; the api wiring is therefore arrow-wrapped (contextually typed).
694
663
  *
664
+ * Emits the plugin-local `queue:message` event after each consumed message.
665
+ *
695
666
  * @see README.md
696
667
  */
697
668
  const queuesPlugin = createPlugin("queues", {
@@ -910,18 +881,6 @@ const storagePlugin = createPlugin("storage", {
910
881
  api: createStorageApi
911
882
  });
912
883
  //#endregion
913
- Object.defineProperty(exports, "__exportAll", {
914
- enumerable: true,
915
- get: function() {
916
- return __exportAll;
917
- }
918
- });
919
- Object.defineProperty(exports, "__toESM", {
920
- enumerable: true,
921
- get: function() {
922
- return __toESM;
923
- }
924
- });
925
884
  Object.defineProperty(exports, "bindingsPlugin", {
926
885
  enumerable: true,
927
886
  get: function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/worker",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Cloudflare Worker framework for Moku — Durable Objects, Queues, R2, D1, and KV plugins that compose with Moku Web.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,13 +0,0 @@
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 };