@moku-labs/worker 0.7.1 → 0.7.2

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.
@@ -1984,8 +1984,9 @@ const runDev = async (ctx, opts, deps) => {
1984
1984
  //#region src/plugins/deploy/infra/plan.ts
1985
1985
  /**
1986
1986
  * Decide whether a single declared resource already exists in the account, recovering its id
1987
- * (kv/d1) when it does. Durable Objects are config-only (they ship with the script), so they are
1988
- * always treated as "missing" provisioning them is a no-op that just records the binding.
1987
+ * (kv/d1) when it does. Durable Objects are config-only they ship with the Worker (`wrangler
1988
+ * deploy` + the auto-derived DO migration create the namespace), never provisioned via the API — so
1989
+ * they are treated as already EXISTING, and the plan never re-offers to "create" them each deploy.
1989
1990
  *
1990
1991
  * @param resource - The declared resource descriptor.
1991
1992
  * @param existing - The indexed set of resources already in the account.
@@ -2013,7 +2014,7 @@ const checkExisting = (resource, existing) => {
2013
2014
  }
2014
2015
  case "r2": return { exists: existing.r2.has(resource.name) };
2015
2016
  case "queue": return { exists: existing.queue.has(resource.name) };
2016
- case "do": return { exists: false };
2017
+ case "do": return { exists: true };
2017
2018
  }
2018
2019
  };
2019
2020
  /**
@@ -3086,6 +3087,32 @@ const guidedUpload = async (ctx, manifest, deps) => {
3086
3087
  return true;
3087
3088
  };
3088
3089
  /**
3090
+ * The final deploy step: confirm the target (guided only), run `wrangler deploy` with interactive
3091
+ * retry, then emit deploy:complete. Resolves false when the target gate or a deploy retry is declined.
3092
+ *
3093
+ * @param ctx - The deploy plugin context.
3094
+ * @param manifest - The assembled (or caller-supplied) deploy manifest.
3095
+ * @param stage - The resolved deploy stage (for the confirm prompt).
3096
+ * @param deps - Interactivity + the confirm prompt.
3097
+ * @returns True once deployed; false when the user declined the gate or a retry (abort).
3098
+ * @example
3099
+ * ```ts
3100
+ * if (!(await guidedDeployStep(ctx, manifest, stage, deps))) return emitAborted(ctx);
3101
+ * ```
3102
+ */
3103
+ const guidedDeployStep = async (ctx, manifest, stage, deps) => {
3104
+ if (!await deps.confirm(`Deploy "${manifest.name}" to ${stage}?`)) return false;
3105
+ ctx.emit("deploy:phase", { phase: "deploy" });
3106
+ const url = await guidedStep(() => runWrangler([
3107
+ "deploy",
3108
+ "--config",
3109
+ ctx.config.configFile
3110
+ ]), HINTS.deploy, deps);
3111
+ if (url === ABORTED) return false;
3112
+ ctx.emit("deploy:complete", { url });
3113
+ return true;
3114
+ };
3115
+ /**
3089
3116
  * Create the deploy api. Assembles the manifest from each resource plugin's own deployManifest(),
3090
3117
  * runs an infra preflight (check-before-create + id capture), generates config, uploads, and runs
3091
3118
  * `wrangler deploy`, emitting global deploy events along the way.
@@ -3127,10 +3154,9 @@ const createDeployApi = (ctx) => ({
3127
3154
  const ci = opts?.ci ?? ctx.config.ci;
3128
3155
  const stage = opts?.stage ?? ctx.global.stage;
3129
3156
  const interactive = !ci && stdoutIsTty();
3130
- const confirm = interactive ? (0, _moku_labs_common_cli.createBrandPrompts)().confirm : async (_question) => true;
3131
3157
  const deps = {
3132
3158
  interactive,
3133
- confirm
3159
+ confirm: interactive ? (0, _moku_labs_common_cli.createBrandPrompts)().confirm : async (_question) => true
3134
3160
  };
3135
3161
  ctx.emit("deploy:phase", { phase: "auth" });
3136
3162
  if (!await guidedAuth(ctx, deps)) return emitAborted(ctx);
@@ -3143,15 +3169,7 @@ const createDeployApi = (ctx) => ({
3143
3169
  ctx.emit("deploy:phase", { phase: "wrangler-config" });
3144
3170
  await writeWranglerConfig(ctx.config.configFile, manifest, provisioned.ids, wranglerExtra(ctx.config));
3145
3171
  if (!await guidedUpload(ctx, manifest, deps)) return emitAborted(ctx);
3146
- if (!await confirm(`Deploy "${manifest.name}" to ${stage}?`)) return emitAborted(ctx);
3147
- ctx.emit("deploy:phase", { phase: "deploy" });
3148
- const url = await guidedStep(() => runWrangler([
3149
- "deploy",
3150
- "--config",
3151
- ctx.config.configFile
3152
- ]), HINTS.deploy, deps);
3153
- if (url === ABORTED) return emitAborted(ctx);
3154
- ctx.emit("deploy:complete", { url });
3172
+ if (!await guidedDeployStep(ctx, manifest, stage, deps)) return emitAborted(ctx);
3155
3173
  },
3156
3174
  /**
3157
3175
  * Start a long-lived local dev session: cold-build the Moku site, spawn `wrangler dev
@@ -3603,6 +3621,25 @@ const createCliApi = (ctx) => ({
3603
3621
  //#region src/plugins/cli/handlers.ts
3604
3622
  /** Divider drawn before the native `wrangler dev` TUI so the moku preamble reads as one section. */
3605
3623
  const WRANGLER_DIVIDER = ` ── wrangler ${"─".repeat(48)}`;
3624
+ /** Deploy phases that are a slow, opaque wait (captured output) — worth a live spinner on a TTY. */
3625
+ const SPINNER_PHASES = new Set(["upload", "deploy"]);
3626
+ /** Braille spinner glyphs; advance one per tick. */
3627
+ const SPINNER_FRAMES = [
3628
+ "⠋",
3629
+ "⠙",
3630
+ "⠹",
3631
+ "⠸",
3632
+ "⠼",
3633
+ "⠴",
3634
+ "⠦",
3635
+ "⠧",
3636
+ "⠇",
3637
+ "⠏"
3638
+ ];
3639
+ /** Spinner tick interval (ms). */
3640
+ const SPINNER_TICK_MS = 80;
3641
+ /** Carriage-return + blanks + carriage-return that wipes the transient spinner line before settling. */
3642
+ const SPINNER_CLEAR = `\r${" ".repeat(72)}\r`;
3606
3643
  /**
3607
3644
  * Builds the hook handlers that turn global deploy events into a live progress TUI.
3608
3645
  * Each logs a clean, prefix-free message via `ctx.log`; the branded log sink (installed
@@ -3622,19 +3659,48 @@ const WRANGLER_DIVIDER = ` ── wrangler ${"─".repeat(48)}`;
3622
3659
  */
3623
3660
  const createCliHooks = (ctx) => {
3624
3661
  const ui = (0, _moku_labs_common_cli.createBrandConsole)();
3662
+ const { palette } = ui;
3663
+ let spinnerTimer;
3664
+ let spinnerLabel;
3665
+ const stopSpinner = () => {
3666
+ if (spinnerTimer !== void 0) {
3667
+ clearInterval(spinnerTimer);
3668
+ spinnerTimer = void 0;
3669
+ }
3670
+ if (spinnerLabel !== void 0) {
3671
+ process.stdout.write(SPINNER_CLEAR);
3672
+ ctx.log.info(spinnerLabel);
3673
+ spinnerLabel = void 0;
3674
+ }
3675
+ };
3676
+ const startSpinner = (label) => {
3677
+ spinnerLabel = label;
3678
+ let frame = 0;
3679
+ const text = `${label} …`;
3680
+ spinnerTimer = setInterval(() => {
3681
+ const glyph = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? SPINNER_FRAMES[0];
3682
+ frame += 1;
3683
+ process.stdout.write(`\r ${palette.pink(glyph)} ${palette.dim(text)}`);
3684
+ }, SPINNER_TICK_MS);
3685
+ };
3625
3686
  return {
3626
3687
  /**
3627
- * Log one clean line per pipeline phase: "phase" or "phase · detail".
3688
+ * Render one pipeline phase. Quick phases print a clean line ("phase" / "phase · detail"); the
3689
+ * slow opaque waits (upload / deploy) animate a branded spinner on a TTY, settling to a line when
3690
+ * the next phase or completion arrives. Off a TTY every phase is a plain line (unchanged).
3628
3691
  *
3629
3692
  * @param p - The deploy:phase event payload.
3630
3693
  * @example
3631
3694
  * ```ts
3632
3695
  * handler({ phase: "detect" }); // "detect"
3633
- * handler({ phase: "upload", detail: "3 files" }); // "upload · 3 files"
3696
+ * handler({ phase: "deploy" }); // spins on a TTY, else "deploy"
3634
3697
  * ```
3635
3698
  */
3636
3699
  "deploy:phase"(p) {
3637
- ctx.log.info(p.detail ? `${p.phase} · ${p.detail}` : p.phase);
3700
+ stopSpinner();
3701
+ const label = p.detail ? `${p.phase} · ${p.detail}` : p.phase;
3702
+ if (process.stdout.isTTY === true && SPINNER_PHASES.has(p.phase)) startSpinner(label);
3703
+ else ctx.log.info(label);
3638
3704
  },
3639
3705
  /**
3640
3706
  * Log one dev-session phase: "phase" or "phase · detail".
@@ -3684,6 +3750,7 @@ const createCliHooks = (ctx) => {
3684
3750
  * ```
3685
3751
  */
3686
3752
  "deploy:complete"(p) {
3753
+ stopSpinner();
3687
3754
  ctx.log.info(`deployed → ${p.url}`);
3688
3755
  }
3689
3756
  };
@@ -1961,8 +1961,9 @@ const runDev = async (ctx, opts, deps) => {
1961
1961
  //#region src/plugins/deploy/infra/plan.ts
1962
1962
  /**
1963
1963
  * Decide whether a single declared resource already exists in the account, recovering its id
1964
- * (kv/d1) when it does. Durable Objects are config-only (they ship with the script), so they are
1965
- * always treated as "missing" provisioning them is a no-op that just records the binding.
1964
+ * (kv/d1) when it does. Durable Objects are config-only they ship with the Worker (`wrangler
1965
+ * deploy` + the auto-derived DO migration create the namespace), never provisioned via the API — so
1966
+ * they are treated as already EXISTING, and the plan never re-offers to "create" them each deploy.
1966
1967
  *
1967
1968
  * @param resource - The declared resource descriptor.
1968
1969
  * @param existing - The indexed set of resources already in the account.
@@ -1990,7 +1991,7 @@ const checkExisting = (resource, existing) => {
1990
1991
  }
1991
1992
  case "r2": return { exists: existing.r2.has(resource.name) };
1992
1993
  case "queue": return { exists: existing.queue.has(resource.name) };
1993
- case "do": return { exists: false };
1994
+ case "do": return { exists: true };
1994
1995
  }
1995
1996
  };
1996
1997
  /**
@@ -3063,6 +3064,32 @@ const guidedUpload = async (ctx, manifest, deps) => {
3063
3064
  return true;
3064
3065
  };
3065
3066
  /**
3067
+ * The final deploy step: confirm the target (guided only), run `wrangler deploy` with interactive
3068
+ * retry, then emit deploy:complete. Resolves false when the target gate or a deploy retry is declined.
3069
+ *
3070
+ * @param ctx - The deploy plugin context.
3071
+ * @param manifest - The assembled (or caller-supplied) deploy manifest.
3072
+ * @param stage - The resolved deploy stage (for the confirm prompt).
3073
+ * @param deps - Interactivity + the confirm prompt.
3074
+ * @returns True once deployed; false when the user declined the gate or a retry (abort).
3075
+ * @example
3076
+ * ```ts
3077
+ * if (!(await guidedDeployStep(ctx, manifest, stage, deps))) return emitAborted(ctx);
3078
+ * ```
3079
+ */
3080
+ const guidedDeployStep = async (ctx, manifest, stage, deps) => {
3081
+ if (!await deps.confirm(`Deploy "${manifest.name}" to ${stage}?`)) return false;
3082
+ ctx.emit("deploy:phase", { phase: "deploy" });
3083
+ const url = await guidedStep(() => runWrangler([
3084
+ "deploy",
3085
+ "--config",
3086
+ ctx.config.configFile
3087
+ ]), HINTS.deploy, deps);
3088
+ if (url === ABORTED) return false;
3089
+ ctx.emit("deploy:complete", { url });
3090
+ return true;
3091
+ };
3092
+ /**
3066
3093
  * Create the deploy api. Assembles the manifest from each resource plugin's own deployManifest(),
3067
3094
  * runs an infra preflight (check-before-create + id capture), generates config, uploads, and runs
3068
3095
  * `wrangler deploy`, emitting global deploy events along the way.
@@ -3104,10 +3131,9 @@ const createDeployApi = (ctx) => ({
3104
3131
  const ci = opts?.ci ?? ctx.config.ci;
3105
3132
  const stage = opts?.stage ?? ctx.global.stage;
3106
3133
  const interactive = !ci && stdoutIsTty();
3107
- const confirm = interactive ? createBrandPrompts().confirm : async (_question) => true;
3108
3134
  const deps = {
3109
3135
  interactive,
3110
- confirm
3136
+ confirm: interactive ? createBrandPrompts().confirm : async (_question) => true
3111
3137
  };
3112
3138
  ctx.emit("deploy:phase", { phase: "auth" });
3113
3139
  if (!await guidedAuth(ctx, deps)) return emitAborted(ctx);
@@ -3120,15 +3146,7 @@ const createDeployApi = (ctx) => ({
3120
3146
  ctx.emit("deploy:phase", { phase: "wrangler-config" });
3121
3147
  await writeWranglerConfig(ctx.config.configFile, manifest, provisioned.ids, wranglerExtra(ctx.config));
3122
3148
  if (!await guidedUpload(ctx, manifest, deps)) return emitAborted(ctx);
3123
- if (!await confirm(`Deploy "${manifest.name}" to ${stage}?`)) return emitAborted(ctx);
3124
- ctx.emit("deploy:phase", { phase: "deploy" });
3125
- const url = await guidedStep(() => runWrangler([
3126
- "deploy",
3127
- "--config",
3128
- ctx.config.configFile
3129
- ]), HINTS.deploy, deps);
3130
- if (url === ABORTED) return emitAborted(ctx);
3131
- ctx.emit("deploy:complete", { url });
3149
+ if (!await guidedDeployStep(ctx, manifest, stage, deps)) return emitAborted(ctx);
3132
3150
  },
3133
3151
  /**
3134
3152
  * Start a long-lived local dev session: cold-build the Moku site, spawn `wrangler dev
@@ -3580,6 +3598,25 @@ const createCliApi = (ctx) => ({
3580
3598
  //#region src/plugins/cli/handlers.ts
3581
3599
  /** Divider drawn before the native `wrangler dev` TUI so the moku preamble reads as one section. */
3582
3600
  const WRANGLER_DIVIDER = ` ── wrangler ${"─".repeat(48)}`;
3601
+ /** Deploy phases that are a slow, opaque wait (captured output) — worth a live spinner on a TTY. */
3602
+ const SPINNER_PHASES = new Set(["upload", "deploy"]);
3603
+ /** Braille spinner glyphs; advance one per tick. */
3604
+ const SPINNER_FRAMES = [
3605
+ "⠋",
3606
+ "⠙",
3607
+ "⠹",
3608
+ "⠸",
3609
+ "⠼",
3610
+ "⠴",
3611
+ "⠦",
3612
+ "⠧",
3613
+ "⠇",
3614
+ "⠏"
3615
+ ];
3616
+ /** Spinner tick interval (ms). */
3617
+ const SPINNER_TICK_MS = 80;
3618
+ /** Carriage-return + blanks + carriage-return that wipes the transient spinner line before settling. */
3619
+ const SPINNER_CLEAR = `\r${" ".repeat(72)}\r`;
3583
3620
  /**
3584
3621
  * Builds the hook handlers that turn global deploy events into a live progress TUI.
3585
3622
  * Each logs a clean, prefix-free message via `ctx.log`; the branded log sink (installed
@@ -3599,19 +3636,48 @@ const WRANGLER_DIVIDER = ` ── wrangler ${"─".repeat(48)}`;
3599
3636
  */
3600
3637
  const createCliHooks = (ctx) => {
3601
3638
  const ui = createBrandConsole();
3639
+ const { palette } = ui;
3640
+ let spinnerTimer;
3641
+ let spinnerLabel;
3642
+ const stopSpinner = () => {
3643
+ if (spinnerTimer !== void 0) {
3644
+ clearInterval(spinnerTimer);
3645
+ spinnerTimer = void 0;
3646
+ }
3647
+ if (spinnerLabel !== void 0) {
3648
+ process.stdout.write(SPINNER_CLEAR);
3649
+ ctx.log.info(spinnerLabel);
3650
+ spinnerLabel = void 0;
3651
+ }
3652
+ };
3653
+ const startSpinner = (label) => {
3654
+ spinnerLabel = label;
3655
+ let frame = 0;
3656
+ const text = `${label} …`;
3657
+ spinnerTimer = setInterval(() => {
3658
+ const glyph = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? SPINNER_FRAMES[0];
3659
+ frame += 1;
3660
+ process.stdout.write(`\r ${palette.pink(glyph)} ${palette.dim(text)}`);
3661
+ }, SPINNER_TICK_MS);
3662
+ };
3602
3663
  return {
3603
3664
  /**
3604
- * Log one clean line per pipeline phase: "phase" or "phase · detail".
3665
+ * Render one pipeline phase. Quick phases print a clean line ("phase" / "phase · detail"); the
3666
+ * slow opaque waits (upload / deploy) animate a branded spinner on a TTY, settling to a line when
3667
+ * the next phase or completion arrives. Off a TTY every phase is a plain line (unchanged).
3605
3668
  *
3606
3669
  * @param p - The deploy:phase event payload.
3607
3670
  * @example
3608
3671
  * ```ts
3609
3672
  * handler({ phase: "detect" }); // "detect"
3610
- * handler({ phase: "upload", detail: "3 files" }); // "upload · 3 files"
3673
+ * handler({ phase: "deploy" }); // spins on a TTY, else "deploy"
3611
3674
  * ```
3612
3675
  */
3613
3676
  "deploy:phase"(p) {
3614
- ctx.log.info(p.detail ? `${p.phase} · ${p.detail}` : p.phase);
3677
+ stopSpinner();
3678
+ const label = p.detail ? `${p.phase} · ${p.detail}` : p.phase;
3679
+ if (process.stdout.isTTY === true && SPINNER_PHASES.has(p.phase)) startSpinner(label);
3680
+ else ctx.log.info(label);
3615
3681
  },
3616
3682
  /**
3617
3683
  * Log one dev-session phase: "phase" or "phase · detail".
@@ -3661,6 +3727,7 @@ const createCliHooks = (ctx) => {
3661
3727
  * ```
3662
3728
  */
3663
3729
  "deploy:complete"(p) {
3730
+ stopSpinner();
3664
3731
  ctx.log.info(`deployed → ${p.url}`);
3665
3732
  }
3666
3733
  };
package/dist/cli.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_cli = require("./cli-BBO_YNVC.cjs");
2
+ const require_cli = require("./cli-BPnG_JGR.cjs");
3
3
  exports.cliPlugin = require_cli.cliPlugin;
4
4
  exports.deployPlugin = require_cli.deployPlugin;
package/dist/cli.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { i as ResourceManifest, n as cliPlugin, r as ExternalManifest, t as deployPlugin } from "./index-Dse6wZJH.cjs";
1
+ import { i as ResourceManifest, n as cliPlugin, r as ExternalManifest, t as deployPlugin } from "./index-DCweBI9s.cjs";
2
2
  export { type ExternalManifest, type ResourceManifest, cliPlugin, deployPlugin };
package/dist/cli.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { i as ResourceManifest, n as cliPlugin, r as ExternalManifest, t as deployPlugin } from "./index-Dse6wZJH.mjs";
1
+ import { i as ResourceManifest, n as cliPlugin, r as ExternalManifest, t as deployPlugin } from "./index-DCweBI9s.mjs";
2
2
  export { type ExternalManifest, type ResourceManifest, cliPlugin, deployPlugin };
package/dist/cli.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as deployPlugin, t as cliPlugin } from "./cli-D67ea3Lu.mjs";
1
+ import { n as deployPlugin, t as cliPlugin } from "./cli-D8UmSqh4.mjs";
2
2
  export { cliPlugin, deployPlugin };
@@ -267,17 +267,21 @@ type Api = {
267
267
  * when omitted. A failure renders a branded `✗` line and sets a non-zero exit code rather than
268
268
  * throwing a raw stack trace.
269
269
  *
270
- * @param opts - Optional port and web build hook.
270
+ * @param opts - Optional port, stage, and web build hook.
271
271
  * @param opts.port - Local dev port to bind. Defaults to 8787 when omitted.
272
+ * @param opts.stage - Stage for the generated wrangler config's resource names. Falls back to the
273
+ * `--stage` CLI flag, then the app's configured stage. Pass it explicitly from a script for a
274
+ * self-documenting `dev({ stage })` instead of relying on the hidden flag.
272
275
  * @param opts.webBuild - Rebuild the web site on change (e.g. `() => webApp.cli.build()`).
273
276
  * @returns Resolves when the dev session ends.
274
277
  * @example
275
278
  * ```ts
276
- * await app.cli.dev({ port: 7878, webBuild: () => web.cli.build() });
279
+ * await app.cli.dev({ stage: "dev", port: 7878, webBuild: () => web.cli.build() });
277
280
  * ```
278
281
  */
279
282
  dev(opts?: {
280
283
  port?: number;
284
+ stage?: string;
281
285
  webBuild?: WebBuild;
282
286
  }): Promise<void>;
283
287
  /**
@@ -285,18 +289,22 @@ type Api = {
285
289
  * `{ ci: true }` for the automated/non-interactive path (CI). A failure renders a branded `✗`
286
290
  * line and sets a non-zero exit code rather than throwing a raw stack trace.
287
291
  *
288
- * @param opts - Optional ci flag and a web build hook.
292
+ * @param opts - Optional ci flag, stage, and a web build hook.
289
293
  * @param opts.ci - Automated mode: never prompts, auto-confirms. Omit/false → guided on a TTY.
294
+ * @param opts.stage - Stage for the generated wrangler config's resource names (e.g. "production",
295
+ * "staging"). Falls back to the `--stage` CLI flag, then the app's configured stage. Pass it
296
+ * explicitly from a script for a self-documenting `deploy({ stage })` instead of the hidden flag.
290
297
  * @param opts.webBuild - Build the web site first (e.g. `() => webApp.cli.build()`), before deploy.
291
298
  * @returns Resolves once the deploy completes (or after a failure is rendered).
292
299
  * @example
293
300
  * ```ts
294
- * await app.cli.deploy({ webBuild: () => web.cli.build() }); // guided
295
- * await app.cli.deploy({ ci: true, webBuild: () => web.cli.build() }); // CI
301
+ * await app.cli.deploy({ stage: "production", webBuild: () => web.cli.build() }); // guided
302
+ * await app.cli.deploy({ ci: true, webBuild: () => web.cli.build() }); // CI
296
303
  * ```
297
304
  */
298
305
  deploy(opts?: {
299
306
  ci?: boolean;
307
+ stage?: string;
300
308
  webBuild?: WebBuild;
301
309
  }): Promise<void>;
302
310
  /**
@@ -267,17 +267,21 @@ type Api = {
267
267
  * when omitted. A failure renders a branded `✗` line and sets a non-zero exit code rather than
268
268
  * throwing a raw stack trace.
269
269
  *
270
- * @param opts - Optional port and web build hook.
270
+ * @param opts - Optional port, stage, and web build hook.
271
271
  * @param opts.port - Local dev port to bind. Defaults to 8787 when omitted.
272
+ * @param opts.stage - Stage for the generated wrangler config's resource names. Falls back to the
273
+ * `--stage` CLI flag, then the app's configured stage. Pass it explicitly from a script for a
274
+ * self-documenting `dev({ stage })` instead of relying on the hidden flag.
272
275
  * @param opts.webBuild - Rebuild the web site on change (e.g. `() => webApp.cli.build()`).
273
276
  * @returns Resolves when the dev session ends.
274
277
  * @example
275
278
  * ```ts
276
- * await app.cli.dev({ port: 7878, webBuild: () => web.cli.build() });
279
+ * await app.cli.dev({ stage: "dev", port: 7878, webBuild: () => web.cli.build() });
277
280
  * ```
278
281
  */
279
282
  dev(opts?: {
280
283
  port?: number;
284
+ stage?: string;
281
285
  webBuild?: WebBuild;
282
286
  }): Promise<void>;
283
287
  /**
@@ -285,18 +289,22 @@ type Api = {
285
289
  * `{ ci: true }` for the automated/non-interactive path (CI). A failure renders a branded `✗`
286
290
  * line and sets a non-zero exit code rather than throwing a raw stack trace.
287
291
  *
288
- * @param opts - Optional ci flag and a web build hook.
292
+ * @param opts - Optional ci flag, stage, and a web build hook.
289
293
  * @param opts.ci - Automated mode: never prompts, auto-confirms. Omit/false → guided on a TTY.
294
+ * @param opts.stage - Stage for the generated wrangler config's resource names (e.g. "production",
295
+ * "staging"). Falls back to the `--stage` CLI flag, then the app's configured stage. Pass it
296
+ * explicitly from a script for a self-documenting `deploy({ stage })` instead of the hidden flag.
290
297
  * @param opts.webBuild - Build the web site first (e.g. `() => webApp.cli.build()`), before deploy.
291
298
  * @returns Resolves once the deploy completes (or after a failure is rendered).
292
299
  * @example
293
300
  * ```ts
294
- * await app.cli.deploy({ webBuild: () => web.cli.build() }); // guided
295
- * await app.cli.deploy({ ci: true, webBuild: () => web.cli.build() }); // CI
301
+ * await app.cli.deploy({ stage: "production", webBuild: () => web.cli.build() }); // guided
302
+ * await app.cli.deploy({ ci: true, webBuild: () => web.cli.build() }); // CI
296
303
  * ```
297
304
  */
298
305
  deploy(opts?: {
299
306
  ci?: boolean;
307
+ stage?: string;
300
308
  webBuild?: WebBuild;
301
309
  }): Promise<void>;
302
310
  /**
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_cli = require("./cli-BBO_YNVC.cjs");
2
+ const require_cli = require("./cli-BPnG_JGR.cjs");
3
3
  let _moku_labs_common = require("@moku-labs/common");
4
4
  //#region src/env-provider.ts
5
5
  /**
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as WorkerConfig, c as WorkerPluginCtx, i as ResourceManifest, n as cliPlugin, o as WorkerEnv, r as ExternalManifest, s as WorkerEvents, t as deployPlugin } from "./index-Dse6wZJH.cjs";
1
+ import { a as WorkerConfig, c as WorkerPluginCtx, i as ResourceManifest, n as cliPlugin, o as WorkerEnv, r as ExternalManifest, s as WorkerEvents, t as deployPlugin } from "./index-DCweBI9s.cjs";
2
2
  import { envPlugin, logPlugin } from "@moku-labs/common";
3
3
  import { PluginCtx, PluginCtx as PluginCtx$1, PluginInstance } from "@moku-labs/core";
4
4
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as WorkerConfig, c as WorkerPluginCtx, i as ResourceManifest, n as cliPlugin, o as WorkerEnv, r as ExternalManifest, s as WorkerEvents, t as deployPlugin } from "./index-Dse6wZJH.mjs";
1
+ import { a as WorkerConfig, c as WorkerPluginCtx, i as ResourceManifest, n as cliPlugin, o as WorkerEnv, r as ExternalManifest, s as WorkerEvents, t as deployPlugin } from "./index-DCweBI9s.mjs";
2
2
  import { envPlugin, logPlugin } from "@moku-labs/common";
3
3
  import { PluginCtx, PluginCtx as PluginCtx$1, PluginInstance } from "@moku-labs/core";
4
4
 
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as kvPlugin, c as d1Plugin, d as createCore, f as createPlugin$1, i as queuesPlugin, l as bindingsPlugin, n as deployPlugin, o as durableObjectsPlugin, p as stagePlugin, r as storagePlugin, s as defineDurableObject, t as cliPlugin, u as coreConfig } from "./cli-D67ea3Lu.mjs";
1
+ import { a as kvPlugin, c as d1Plugin, d as createCore, f as createPlugin$1, i as queuesPlugin, l as bindingsPlugin, n as deployPlugin, o as durableObjectsPlugin, p as stagePlugin, r as storagePlugin, s as defineDurableObject, t as cliPlugin, u as coreConfig } from "./cli-D8UmSqh4.mjs";
2
2
  import { envPlugin, logPlugin } from "@moku-labs/common";
3
3
  //#region src/env-provider.ts
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/worker",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
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",