@moku-labs/web 1.0.0 → 1.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/dist/index.d.cts CHANGED
@@ -1748,12 +1748,21 @@ declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Con
1748
1748
  durationMs: number;
1749
1749
  };
1750
1750
  }> & Record<never, never>;
1751
- declare namespace types_d_exports$4 {
1752
- export { Api$2 as Api, Config$2 as Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WranglerErrorKind };
1753
- }
1751
+ //#endregion
1752
+ //#region src/plugins/deploy/generators/github-workflow.d.ts
1754
1753
  /**
1755
- * @file deploy plugin type definitions.
1754
+ * What triggers the generated deploy workflow:
1755
+ * - `"auto"` — every push to `main` (plus the manual "Run workflow" button).
1756
+ * - `"versioned-tag"` — pushing a `v*` semver tag (plus the manual button).
1757
+ * - `"dispatch"` — only the manual "Run workflow" button (no automation).
1758
+ *
1759
+ * @example
1760
+ * const trigger: WorkflowTrigger = "versioned-tag";
1756
1761
  */
1762
+ type WorkflowTrigger = "auto" | "versioned-tag" | "dispatch";
1763
+ declare namespace types_d_exports$4 {
1764
+ export { Api$2 as Api, Config$2 as Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WorkflowTrigger, WranglerErrorKind };
1765
+ }
1757
1766
  /**
1758
1767
  * Options passed to the injected spawner — the subset of Bun.spawn's options the
1759
1768
  * plugin uses (piped stdout/stderr plus an env carrying the API token).
@@ -1867,6 +1876,11 @@ type DeployRunOptions = {
1867
1876
  type DeployInitOptions = {
1868
1877
  /** Also generate the GitHub Actions workflow. Defaults to config.ci. */ci?: boolean; /** Drift-only mode: report differences without writing any files. Defaults to `false`. */
1869
1878
  check?: boolean;
1879
+ /**
1880
+ * What triggers the generated workflow (see {@link WorkflowTrigger}): `"auto"` (push to
1881
+ * main), `"versioned-tag"` (push a `v*` tag), or `"dispatch"` (manual only). Default `"auto"`.
1882
+ */
1883
+ workflowTrigger?: WorkflowTrigger;
1870
1884
  };
1871
1885
  /**
1872
1886
  * Result of an init/scaffold operation.
@@ -2013,8 +2027,23 @@ type CliRenderer = {
2013
2027
  */
2014
2028
  serverReady(info: ServerInfo): void;
2015
2029
  /**
2016
- * Render the post-rebuild line ("~ dir" + " rebuilt N pages · Xms · reloaded"),
2017
- * where the label is the watched directory whose subtree changed (see {@link ReloadInfo.file}).
2030
+ * Begin a serve() rebuild: show ONE compact, in-place "rebuilding {label}…" line
2031
+ * (a live spinner on a TTY) and suppress the verbose per-phase rows + BUILD summary
2032
+ * box until the matching {@link reload} (or {@link error}) settles it. The initial
2033
+ * build is NOT a rebuild — it still renders the full live phase list. Without this
2034
+ * gate, every keystroke reprinted the entire build log.
2035
+ *
2036
+ * @param label - The changed watch target shown in the line (e.g. "content").
2037
+ * @returns Nothing.
2038
+ * @example
2039
+ * render.rebuildStart("content");
2040
+ */
2041
+ rebuildStart(label: string): void;
2042
+ /**
2043
+ * Settle a serve() rebuild started by {@link rebuildStart}: replace the in-place
2044
+ * "rebuilding…" line with a compact "✓ rebuilt N pages · Xs · reloaded" result and
2045
+ * re-enable verbose build output. The label is the watched directory whose subtree
2046
+ * changed (see {@link ReloadInfo.file}).
2018
2047
  *
2019
2048
  * @param info - The changed watched directory plus the rebuild's page count and duration.
2020
2049
  * @returns Nothing.
@@ -2059,6 +2088,27 @@ type CliRenderer = {
2059
2088
  * render.error("build failed", err);
2060
2089
  */
2061
2090
  error(message: string, cause?: unknown): void;
2091
+ /**
2092
+ * Render a section heading for a multi-step flow (e.g. the guided deploy wizard).
2093
+ *
2094
+ * @param text - The heading label.
2095
+ * @returns Nothing.
2096
+ * @example
2097
+ * render.heading("Diagnostics");
2098
+ */
2099
+ heading(text: string): void;
2100
+ /**
2101
+ * Render one diagnostic line — a green `✓` (pass) or red `✗` (fail) before the label,
2102
+ * with optional dim, indented detail beneath it (e.g. how to fix a failing check).
2103
+ *
2104
+ * @param ok - Whether the check passed.
2105
+ * @param label - The check label.
2106
+ * @param detail - Optional multi-line guidance shown indented under the line.
2107
+ * @returns Nothing.
2108
+ * @example
2109
+ * render.check(false, "CLOUDFLARE_API_TOKEN is set", "Create one at …");
2110
+ */
2111
+ check(ok: boolean, label: string, detail?: string): void;
2062
2112
  };
2063
2113
  /**
2064
2114
  * A live directory watcher handle returned by the injectable `watch` seam. Closing
@@ -2115,6 +2165,14 @@ type ServeStaticOptions = {
2115
2165
  * fetch(req) { return new Response("ok"); }
2116
2166
  */
2117
2167
  fetch(request: Request): Response | Promise<Response>;
2168
+ /**
2169
+ * Idle timeout in SECONDS before the server severs a connection with no traffic
2170
+ * (Bun semantics; `0` disables it, max `255`). The dev server passes `0` so the
2171
+ * long-lived live-reload SSE stream is never cut — Bun's 10s default closes it,
2172
+ * which the browser surfaces as `ERR_INCOMPLETE_CHUNKED_ENCODING` and then
2173
+ * reconnects in an endless storm. Omitted by `preview()` (short static requests).
2174
+ */
2175
+ idleTimeout?: number;
2118
2176
  };
2119
2177
  /**
2120
2178
  * An injectable static-server factory (defaults to `Bun.serve`). Keeps the Bun
@@ -2169,6 +2227,18 @@ type State$1 = {
2169
2227
  * const ok = await state.confirm("Deploy dist/?");
2170
2228
  */
2171
2229
  confirm: (question: string) => Promise<boolean>;
2230
+ /**
2231
+ * Interactive single-choice prompt used by the guided deploy wizard. Presents the
2232
+ * `choices` numbered from 1 and resolves the chosen zero-based index (clamped to a
2233
+ * valid choice). Default reads stdin (TTY); tests inject a canned selection.
2234
+ *
2235
+ * @param question - The prompt to display.
2236
+ * @param choices - The selectable option labels.
2237
+ * @returns Resolves the chosen zero-based index.
2238
+ * @example
2239
+ * const i = await state.select("Workflow trigger?", ["Auto on push", "Manual only"]);
2240
+ */
2241
+ select: (question: string, choices: readonly string[]) => Promise<number>;
2172
2242
  /**
2173
2243
  * Monotonic clock for durations. Default `Date.now`; tests inject for deterministic timing.
2174
2244
  *
@@ -2182,12 +2252,14 @@ type State$1 = {
2182
2252
  * tests inject a fake emitter.
2183
2253
  *
2184
2254
  * @param dir - The directory to watch recursively.
2185
- * @param onChange - Invoked on any change beneath `dir`.
2255
+ * @param onChange - Invoked on any change beneath `dir`, with the changed path
2256
+ * relative to `dir` when the platform reports it (`undefined` otherwise). serve()
2257
+ * uses it to ignore `outDir`/noise writes and to drop duplicate events by mtime.
2186
2258
  * @returns A handle whose `close()` detaches the watcher.
2187
2259
  * @example
2188
- * const handle = state.watch("content", () => rebuild());
2260
+ * const handle = state.watch("content", file => rebuild(file));
2189
2261
  */
2190
- watch: (dir: string, onChange: () => void) => WatchHandle; /** Static-server factory used by serve()/preview(). Default `Bun.serve`; tests inject a fake. */
2262
+ watch: (dir: string, onChange: (filename?: string) => void) => WatchHandle; /** Static-server factory used by serve()/preview(). Default `Bun.serve`; tests inject a fake. */
2191
2263
  serveStatic: ServeStaticFunction; /** File-response factory mapping a resolved path + status to a `Response`. Default `Bun.file`. */
2192
2264
  fileResponse: FileResponseFunction;
2193
2265
  /**
@@ -2200,6 +2272,18 @@ type State$1 = {
2200
2272
  * const url = state.networkUrl(4173);
2201
2273
  */
2202
2274
  networkUrl: (port: number) => string | null;
2275
+ /**
2276
+ * Resolve a file's modification time in epoch milliseconds, or `null` when it does
2277
+ * not exist. serve() uses it to collapse the burst of duplicate `fs.watch` events
2278
+ * macOS emits per save (same mtime ⇒ already-built ⇒ ignored) into one rebuild.
2279
+ * Default wraps `node:fs.statSync`; tests inject deterministic values.
2280
+ *
2281
+ * @param path - The absolute path to stat.
2282
+ * @returns The mtime in milliseconds, or `null` when the file is missing.
2283
+ * @example
2284
+ * const mtime = state.fileMtime("/abs/content/a.md");
2285
+ */
2286
+ fileMtime: (path: string) => number | null;
2203
2287
  };
2204
2288
  /**
2205
2289
  * Summary returned by `cli.build()` — the awaited `build.run()` result.
@@ -2214,9 +2298,10 @@ type BuildSummary = {
2214
2298
  };
2215
2299
  /**
2216
2300
  * Outcome returned by `cli.deploy()` — either a completed deploy (with details) or a
2217
- * skipped one. A skip happens only when an interactive TTY user answers "no" at the
2218
- * confirm prompt (`reason: "declined"`). Non-interactive runs (CI / non-TTY) never
2219
- * prompt and always proceed, so they never skip the scripts are CI-safe.
2301
+ * skipped one. A skip is `"declined"` when a user answers "no" at the confirm prompt,
2302
+ * or `"blocked"` when the guided wizard found unmet prerequisites and stopped before
2303
+ * deploying. Non-interactive direct runs (CI / non-TTY) never prompt and always proceed,
2304
+ * so they never skip — the scripts are CI-safe.
2220
2305
  *
2221
2306
  * @example
2222
2307
  * const outcome: DeployOutcome = { deployed: false, reason: "declined" };
@@ -2229,7 +2314,7 @@ type DeployOutcome = {
2229
2314
  durationMs: number;
2230
2315
  } | {
2231
2316
  deployed: false;
2232
- reason: "declined";
2317
+ reason: "declined" | "blocked";
2233
2318
  };
2234
2319
  /**
2235
2320
  * Options for `cli.build()`.
@@ -2268,6 +2353,13 @@ type PreviewOptions = {
2268
2353
  type DeployOptions = {
2269
2354
  /** Branch to deploy. Defaults to the deploy plugin's production branch. */branch?: string; /** Skip the y/N confirm and deploy immediately. Defaults to `false`. */
2270
2355
  yes?: boolean;
2356
+ /**
2357
+ * Run the guided, interactive setup wizard instead of the direct `--cli` deploy:
2358
+ * diagnose prerequisites, guide the user to fix anything missing, gate the deploy on
2359
+ * everything being green, then offer to scaffold a GitHub Actions workflow. Defaults
2360
+ * to `false` (the direct, CI-safe path). Thin scripts pass `{ guided: !"--cli" }`.
2361
+ */
2362
+ guided?: boolean;
2271
2363
  };
2272
2364
  /**
2273
2365
  * Public API of the cli plugin (mounted at `app.cli`) — exactly four methods.
package/dist/index.d.mts CHANGED
@@ -1748,12 +1748,21 @@ declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Con
1748
1748
  durationMs: number;
1749
1749
  };
1750
1750
  }> & Record<never, never>;
1751
- declare namespace types_d_exports$4 {
1752
- export { Api$2 as Api, Config$2 as Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WranglerErrorKind };
1753
- }
1751
+ //#endregion
1752
+ //#region src/plugins/deploy/generators/github-workflow.d.ts
1754
1753
  /**
1755
- * @file deploy plugin type definitions.
1754
+ * What triggers the generated deploy workflow:
1755
+ * - `"auto"` — every push to `main` (plus the manual "Run workflow" button).
1756
+ * - `"versioned-tag"` — pushing a `v*` semver tag (plus the manual button).
1757
+ * - `"dispatch"` — only the manual "Run workflow" button (no automation).
1758
+ *
1759
+ * @example
1760
+ * const trigger: WorkflowTrigger = "versioned-tag";
1756
1761
  */
1762
+ type WorkflowTrigger = "auto" | "versioned-tag" | "dispatch";
1763
+ declare namespace types_d_exports$4 {
1764
+ export { Api$2 as Api, Config$2 as Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WorkflowTrigger, WranglerErrorKind };
1765
+ }
1757
1766
  /**
1758
1767
  * Options passed to the injected spawner — the subset of Bun.spawn's options the
1759
1768
  * plugin uses (piped stdout/stderr plus an env carrying the API token).
@@ -1867,6 +1876,11 @@ type DeployRunOptions = {
1867
1876
  type DeployInitOptions = {
1868
1877
  /** Also generate the GitHub Actions workflow. Defaults to config.ci. */ci?: boolean; /** Drift-only mode: report differences without writing any files. Defaults to `false`. */
1869
1878
  check?: boolean;
1879
+ /**
1880
+ * What triggers the generated workflow (see {@link WorkflowTrigger}): `"auto"` (push to
1881
+ * main), `"versioned-tag"` (push a `v*` tag), or `"dispatch"` (manual only). Default `"auto"`.
1882
+ */
1883
+ workflowTrigger?: WorkflowTrigger;
1870
1884
  };
1871
1885
  /**
1872
1886
  * Result of an init/scaffold operation.
@@ -2013,8 +2027,23 @@ type CliRenderer = {
2013
2027
  */
2014
2028
  serverReady(info: ServerInfo): void;
2015
2029
  /**
2016
- * Render the post-rebuild line ("~ dir" + " rebuilt N pages · Xms · reloaded"),
2017
- * where the label is the watched directory whose subtree changed (see {@link ReloadInfo.file}).
2030
+ * Begin a serve() rebuild: show ONE compact, in-place "rebuilding {label}…" line
2031
+ * (a live spinner on a TTY) and suppress the verbose per-phase rows + BUILD summary
2032
+ * box until the matching {@link reload} (or {@link error}) settles it. The initial
2033
+ * build is NOT a rebuild — it still renders the full live phase list. Without this
2034
+ * gate, every keystroke reprinted the entire build log.
2035
+ *
2036
+ * @param label - The changed watch target shown in the line (e.g. "content").
2037
+ * @returns Nothing.
2038
+ * @example
2039
+ * render.rebuildStart("content");
2040
+ */
2041
+ rebuildStart(label: string): void;
2042
+ /**
2043
+ * Settle a serve() rebuild started by {@link rebuildStart}: replace the in-place
2044
+ * "rebuilding…" line with a compact "✓ rebuilt N pages · Xs · reloaded" result and
2045
+ * re-enable verbose build output. The label is the watched directory whose subtree
2046
+ * changed (see {@link ReloadInfo.file}).
2018
2047
  *
2019
2048
  * @param info - The changed watched directory plus the rebuild's page count and duration.
2020
2049
  * @returns Nothing.
@@ -2059,6 +2088,27 @@ type CliRenderer = {
2059
2088
  * render.error("build failed", err);
2060
2089
  */
2061
2090
  error(message: string, cause?: unknown): void;
2091
+ /**
2092
+ * Render a section heading for a multi-step flow (e.g. the guided deploy wizard).
2093
+ *
2094
+ * @param text - The heading label.
2095
+ * @returns Nothing.
2096
+ * @example
2097
+ * render.heading("Diagnostics");
2098
+ */
2099
+ heading(text: string): void;
2100
+ /**
2101
+ * Render one diagnostic line — a green `✓` (pass) or red `✗` (fail) before the label,
2102
+ * with optional dim, indented detail beneath it (e.g. how to fix a failing check).
2103
+ *
2104
+ * @param ok - Whether the check passed.
2105
+ * @param label - The check label.
2106
+ * @param detail - Optional multi-line guidance shown indented under the line.
2107
+ * @returns Nothing.
2108
+ * @example
2109
+ * render.check(false, "CLOUDFLARE_API_TOKEN is set", "Create one at …");
2110
+ */
2111
+ check(ok: boolean, label: string, detail?: string): void;
2062
2112
  };
2063
2113
  /**
2064
2114
  * A live directory watcher handle returned by the injectable `watch` seam. Closing
@@ -2115,6 +2165,14 @@ type ServeStaticOptions = {
2115
2165
  * fetch(req) { return new Response("ok"); }
2116
2166
  */
2117
2167
  fetch(request: Request): Response | Promise<Response>;
2168
+ /**
2169
+ * Idle timeout in SECONDS before the server severs a connection with no traffic
2170
+ * (Bun semantics; `0` disables it, max `255`). The dev server passes `0` so the
2171
+ * long-lived live-reload SSE stream is never cut — Bun's 10s default closes it,
2172
+ * which the browser surfaces as `ERR_INCOMPLETE_CHUNKED_ENCODING` and then
2173
+ * reconnects in an endless storm. Omitted by `preview()` (short static requests).
2174
+ */
2175
+ idleTimeout?: number;
2118
2176
  };
2119
2177
  /**
2120
2178
  * An injectable static-server factory (defaults to `Bun.serve`). Keeps the Bun
@@ -2169,6 +2227,18 @@ type State$1 = {
2169
2227
  * const ok = await state.confirm("Deploy dist/?");
2170
2228
  */
2171
2229
  confirm: (question: string) => Promise<boolean>;
2230
+ /**
2231
+ * Interactive single-choice prompt used by the guided deploy wizard. Presents the
2232
+ * `choices` numbered from 1 and resolves the chosen zero-based index (clamped to a
2233
+ * valid choice). Default reads stdin (TTY); tests inject a canned selection.
2234
+ *
2235
+ * @param question - The prompt to display.
2236
+ * @param choices - The selectable option labels.
2237
+ * @returns Resolves the chosen zero-based index.
2238
+ * @example
2239
+ * const i = await state.select("Workflow trigger?", ["Auto on push", "Manual only"]);
2240
+ */
2241
+ select: (question: string, choices: readonly string[]) => Promise<number>;
2172
2242
  /**
2173
2243
  * Monotonic clock for durations. Default `Date.now`; tests inject for deterministic timing.
2174
2244
  *
@@ -2182,12 +2252,14 @@ type State$1 = {
2182
2252
  * tests inject a fake emitter.
2183
2253
  *
2184
2254
  * @param dir - The directory to watch recursively.
2185
- * @param onChange - Invoked on any change beneath `dir`.
2255
+ * @param onChange - Invoked on any change beneath `dir`, with the changed path
2256
+ * relative to `dir` when the platform reports it (`undefined` otherwise). serve()
2257
+ * uses it to ignore `outDir`/noise writes and to drop duplicate events by mtime.
2186
2258
  * @returns A handle whose `close()` detaches the watcher.
2187
2259
  * @example
2188
- * const handle = state.watch("content", () => rebuild());
2260
+ * const handle = state.watch("content", file => rebuild(file));
2189
2261
  */
2190
- watch: (dir: string, onChange: () => void) => WatchHandle; /** Static-server factory used by serve()/preview(). Default `Bun.serve`; tests inject a fake. */
2262
+ watch: (dir: string, onChange: (filename?: string) => void) => WatchHandle; /** Static-server factory used by serve()/preview(). Default `Bun.serve`; tests inject a fake. */
2191
2263
  serveStatic: ServeStaticFunction; /** File-response factory mapping a resolved path + status to a `Response`. Default `Bun.file`. */
2192
2264
  fileResponse: FileResponseFunction;
2193
2265
  /**
@@ -2200,6 +2272,18 @@ type State$1 = {
2200
2272
  * const url = state.networkUrl(4173);
2201
2273
  */
2202
2274
  networkUrl: (port: number) => string | null;
2275
+ /**
2276
+ * Resolve a file's modification time in epoch milliseconds, or `null` when it does
2277
+ * not exist. serve() uses it to collapse the burst of duplicate `fs.watch` events
2278
+ * macOS emits per save (same mtime ⇒ already-built ⇒ ignored) into one rebuild.
2279
+ * Default wraps `node:fs.statSync`; tests inject deterministic values.
2280
+ *
2281
+ * @param path - The absolute path to stat.
2282
+ * @returns The mtime in milliseconds, or `null` when the file is missing.
2283
+ * @example
2284
+ * const mtime = state.fileMtime("/abs/content/a.md");
2285
+ */
2286
+ fileMtime: (path: string) => number | null;
2203
2287
  };
2204
2288
  /**
2205
2289
  * Summary returned by `cli.build()` — the awaited `build.run()` result.
@@ -2214,9 +2298,10 @@ type BuildSummary = {
2214
2298
  };
2215
2299
  /**
2216
2300
  * Outcome returned by `cli.deploy()` — either a completed deploy (with details) or a
2217
- * skipped one. A skip happens only when an interactive TTY user answers "no" at the
2218
- * confirm prompt (`reason: "declined"`). Non-interactive runs (CI / non-TTY) never
2219
- * prompt and always proceed, so they never skip the scripts are CI-safe.
2301
+ * skipped one. A skip is `"declined"` when a user answers "no" at the confirm prompt,
2302
+ * or `"blocked"` when the guided wizard found unmet prerequisites and stopped before
2303
+ * deploying. Non-interactive direct runs (CI / non-TTY) never prompt and always proceed,
2304
+ * so they never skip — the scripts are CI-safe.
2220
2305
  *
2221
2306
  * @example
2222
2307
  * const outcome: DeployOutcome = { deployed: false, reason: "declined" };
@@ -2229,7 +2314,7 @@ type DeployOutcome = {
2229
2314
  durationMs: number;
2230
2315
  } | {
2231
2316
  deployed: false;
2232
- reason: "declined";
2317
+ reason: "declined" | "blocked";
2233
2318
  };
2234
2319
  /**
2235
2320
  * Options for `cli.build()`.
@@ -2268,6 +2353,13 @@ type PreviewOptions = {
2268
2353
  type DeployOptions = {
2269
2354
  /** Branch to deploy. Defaults to the deploy plugin's production branch. */branch?: string; /** Skip the y/N confirm and deploy immediately. Defaults to `false`. */
2270
2355
  yes?: boolean;
2356
+ /**
2357
+ * Run the guided, interactive setup wizard instead of the direct `--cli` deploy:
2358
+ * diagnose prerequisites, guide the user to fix anything missing, gate the deploy on
2359
+ * everything being green, then offer to scaffold a GitHub Actions workflow. Defaults
2360
+ * to `false` (the direct, CI-safe path). Thin scripts pass `{ guided: !"--cli" }`.
2361
+ */
2362
+ guided?: boolean;
2271
2363
  };
2272
2364
  /**
2273
2365
  * Public API of the cli plugin (mounted at `app.cli`) — exactly four methods.