devflare 1.0.0-next.46 → 1.0.0-next.48

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
@@ -188,6 +188,7 @@ for examples with file paths.
188
188
  | `devflare productions` | inspect or manage production Worker versions |
189
189
  | `devflare remote` | manage remote test mode |
190
190
  | `devflare secrets` | manage local Secrets Store values |
191
+ | `devflare tail` | stream live logs from a deployed Worker |
191
192
  | `devflare tokens` | create and manage Devflare-scoped API tokens |
192
193
  | `devflare types` | generate `env.d.ts` |
193
194
  | `devflare version` | print the installed version |
@@ -20,6 +20,7 @@ const COMMANDS = [
20
20
  "worker",
21
21
  "tokens",
22
22
  "secrets",
23
+ "tail",
23
24
  "ai",
24
25
  "remote",
25
26
  "help",
@@ -260,6 +261,7 @@ const CORE_HELP_PAGES = [
260
261
  entry("worker", "Rename and manage Worker control-plane operations"),
261
262
  entry("tokens", "Manage Devflare-managed Cloudflare API tokens"),
262
263
  entry("secrets", "Manage local Secrets Store values"),
264
+ entry("tail", "Stream live logs from a deployed Worker"),
263
265
  entry("ai", "View Workers AI pricing information"),
264
266
  entry("remote", "Manage remote test mode for paid Cloudflare features"),
265
267
  entry("help", "Show command overview or a command-specific help page"),
@@ -346,6 +348,7 @@ const CORE_HELP_PAGES = [
346
348
  "devflare deploy --production [--config <path>] [--build <path>] [--message <text>] [--tag <text>] [--debug]",
347
349
  "devflare deploy --preview <name> [--config <path>] [--build <path>] [--message <text>] [--tag <text>]",
348
350
  "devflare deploy --preview [--config <path>] [--build <path>] [--branch-name <branch>] [--message <text>] [--tag <text>]",
351
+ "devflare deploy --prod --percentage <n> [--version <current-version-id>] [--config <path>] [--message <text>]",
349
352
  "devflare deploy --prod --dry-run [--config <path>]",
350
353
  "devflare deploy --preview <name> --dry-run [--config <path>]",
351
354
  "devflare deploy --preview --dry-run [--config <path>]"
@@ -367,6 +370,8 @@ const CORE_HELP_PAGES = [
367
370
  entry("--branch-name <branch>", "Provide explicit branch metadata for preview-aware naming when your workflow needs it"),
368
371
  entry("--message <text>", "Attach an explicit Wrangler deployment/version message"),
369
372
  entry("--tag <text>", "Attach an explicit Wrangler version tag"),
373
+ entry("--percentage <n>", "Production gradual rollout: upload a new version and route n% (0-100) of traffic to it via `wrangler versions deploy`"),
374
+ entry("--version <id>", "With --percentage, the currently-live version id that keeps the remaining traffic (otherwise Wrangler distributes it)"),
370
375
  entry("--debug", "Print stack traces when deployment orchestration fails")
371
376
  ],
372
377
  examples: [
@@ -376,10 +381,12 @@ const CORE_HELP_PAGES = [
376
381
  entry("devflare deploy --preview next", "Deploy the named `next` preview scope and provision preview-scoped resources automatically"),
377
382
  entry("devflare deploy --preview pr-1", "Deploy the named `pr-1` preview scope directly"),
378
383
  entry("devflare deploy --preview --branch-name feature-branch", "Upload a same-worker preview version with explicit branch metadata"),
379
- entry("devflare deploy --preview next --dry-run", "Inspect the generated named-preview Wrangler config without deploying")
384
+ entry("devflare deploy --preview next --dry-run", "Inspect the generated named-preview Wrangler config without deploying"),
385
+ entry("devflare deploy --prod --percentage 10", "Upload a new version and route 10% of production traffic to it (canary)")
380
386
  ],
381
387
  notes: [
382
388
  "`devflare deploy` without an explicit target is rejected from the CLI so production and preview destinations stay unmistakable.",
389
+ "`--percentage <n>` performs a Cloudflare gradual deployment: Devflare runs `wrangler versions upload` (no traffic shift) and then `wrangler versions deploy <new-version-id>@<n> --yes`, routing n% of production traffic to the new version. Pass `--version <id>` to keep the remaining traffic on a specific currently-live version; otherwise Wrangler distributes the rest. To advance an existing rollout (e.g. 10% → 50% → 100%) without re-uploading, drive `wrangler versions deploy` directly. `--percentage` is production-only and rejected with `--preview`.",
383
390
  "`--prod` / `--production` clear preview-scope environment overrides such as `DEVFLARE_PREVIEW_BRANCH` so production deploys stay pointed at stable Worker names.",
384
391
  "Named preview deploys automatically provision preview-scoped resources before building and deploying.",
385
392
  "When a build artifact still contains name-based bindings, deploy resolves or provisions the concrete Cloudflare resources and rewrites the generated Wrangler config with the IDs Wrangler requires.",
@@ -563,6 +570,25 @@ const MISC_HELP_PAGES = [
563
570
  ],
564
571
  notes: ["Created tokens include the selected account resource and all zones in that account so deploys can manage Worker routes and custom-domain route state.", "Cloudflare only returns token secrets once for create and roll operations, so store them immediately."]
565
572
  },
573
+ {
574
+ path: ["tail"],
575
+ summary: "Stream live logs from a deployed Worker",
576
+ usage: ["devflare tail [worker] [--worker <name>] [--format <pretty|json>] [--account <id>] [--config <path>]"],
577
+ description: ["Streams live request logs and exceptions from a Worker that is already deployed to Cloudflare, using Cloudflare’s Workers Trace (tail) API.", "This command is inherently remote: it opens a real tail session against your live Worker. There is no local emulation of deployed traffic — use `devflare dev` and `cf.tail.trigger()` for local/offline tail-handler testing."],
578
+ arguments: [entry("[worker]", "Worker name to tail (defaults to --worker, then the config `name`)")],
579
+ options: [
580
+ entry("--worker <name>", "Worker name to tail instead of the configured `name`"),
581
+ entry("--format <pretty|json>", "Output format: human-readable lines (default) or raw JSON events"),
582
+ entry("--account <id>", "Use a specific Cloudflare account instead of the resolved default"),
583
+ entry("--config <path>", "Use a specific devflare config file to resolve the Worker name/account")
584
+ ],
585
+ examples: [
586
+ entry("devflare tail", "Stream logs from the Worker named in the current config"),
587
+ entry("devflare tail my-worker", "Stream logs from an explicitly named Worker"),
588
+ entry("devflare tail --format json", "Stream raw JSON tail events for piping into other tools")
589
+ ],
590
+ notes: ["Requires Cloudflare authentication (`devflare login` or CLOUDFLARE_API_TOKEN) and a resolvable account id.", "Press Ctrl-C to stop; Devflare deletes the tail session and closes the WebSocket on exit. Cloudflare also expires idle tail sessions on its own."]
591
+ },
566
592
  {
567
593
  path: ["ai"],
568
594
  summary: "Show Workers AI pricing information",
@@ -1050,6 +1076,7 @@ async function runCli(argv, options = {}) {
1050
1076
  case "worker": return runWorker(parsed, logger, options);
1051
1077
  case "tokens": return runToken(parsed, logger, options);
1052
1078
  case "secrets": return runSecrets(parsed, logger, options);
1079
+ case "tail": return runTail(parsed, logger, options);
1053
1080
  case "ai": return runAI();
1054
1081
  case "remote": return runRemote(parsed, logger, options);
1055
1082
  default:
@@ -1070,14 +1097,14 @@ async function runBuild(parsed, logger, options) {
1070
1097
  return runBuildCommand(parsed, logger, options);
1071
1098
  }
1072
1099
  async function runDeploy(parsed, logger, options) {
1073
- const { runDeployCommand } = await import("./deploy-0w_38g8h.js");
1100
+ const { runDeployCommand } = await import("./deploy-B0l0VWNl.js");
1074
1101
  return runDeployCommand(parsed, logger, {
1075
1102
  ...options,
1076
1103
  requireExplicitDeployTarget: true
1077
1104
  });
1078
1105
  }
1079
1106
  async function runTypes(parsed, logger, options) {
1080
- const { runTypesCommand } = await import("./types-Zn9vyUGA.js");
1107
+ const { runTypesCommand } = await import("./types-BWLYVp3B.js");
1081
1108
  return runTypesCommand(parsed, logger, options);
1082
1109
  }
1083
1110
  async function runDoctor(parsed, logger, options) {
@@ -1105,17 +1132,21 @@ async function runProductions(parsed, logger, options) {
1105
1132
  return runProductionsCommand(parsed, logger, options);
1106
1133
  }
1107
1134
  async function runWorker(parsed, logger, options) {
1108
- const { runWorkerCommand } = await import("./worker-rL5udcl9.js");
1135
+ const { runWorkerCommand } = await import("./worker-BUGz2bVG.js");
1109
1136
  return runWorkerCommand(parsed, logger, options);
1110
1137
  }
1111
1138
  async function runToken(parsed, logger, options) {
1112
- const { runTokenCommand } = await import("./token-kguIDmej.js");
1139
+ const { runTokenCommand } = await import("./token-Dmi0KXyX.js");
1113
1140
  return runTokenCommand(parsed, logger, options);
1114
1141
  }
1115
1142
  async function runSecrets(parsed, logger, options) {
1116
1143
  const { runSecretsCommand } = await import("./secrets-DIpJGe6C.js");
1117
1144
  return runSecretsCommand(parsed, logger, options);
1118
1145
  }
1146
+ async function runTail(parsed, logger, options) {
1147
+ const { runTailCommand } = await import("./tail-BKLtjVi4.js");
1148
+ return runTailCommand(parsed, logger, options);
1149
+ }
1119
1150
  async function runAI() {
1120
1151
  const { runAICommand } = await import("./ai-CBEuLdoP.js");
1121
1152
  return runAICommand();
@@ -147,6 +147,68 @@ async function withTemporaryEnvironment(overrides, operation) {
147
147
  }
148
148
  }
149
149
  //#endregion
150
+ //#region src/cli/gradual-deploy.ts
151
+ /**
152
+ * Parse and validate the `--percentage` CLI option.
153
+ *
154
+ * Returns `undefined` when the flag is absent (a normal full deploy). Throws a
155
+ * clear error when the value is present but not an integer in `[0, 100]` — the
156
+ * same bounds Wrangler enforces — so a typo never silently becomes a full
157
+ * deploy.
158
+ */
159
+ function parseDeployPercentage(value) {
160
+ if (value === void 0) return;
161
+ if (value === true || value === false) throw new Error("--percentage requires a value between 0 and 100, e.g. `--percentage 10`.");
162
+ const trimmed = value.trim();
163
+ if (!/^\d+$/.test(trimmed)) throw new Error(`--percentage must be a whole number between 0 and 100, received "${value}".`);
164
+ const parsed = Number.parseInt(trimmed, 10);
165
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 100) throw new Error(`--percentage must be a whole number between 0 and 100, received "${value}".`);
166
+ return parsed;
167
+ }
168
+ /**
169
+ * Build the positional `<version-id>@<percentage>` version specs for a rollout.
170
+ *
171
+ * Wrangler's `versions deploy` consumes traffic splits as positional specs (see
172
+ * `wrangler versions deploy --help`: "Shorthand notation to deploy Worker
173
+ * Version(s) [<version-id>@<percentage>..]"). When a distinct previous version
174
+ * id is provided, the remaining percentage is routed to it so the split is
175
+ * fully specified and Wrangler never has to prompt; otherwise only the new
176
+ * version is named and Wrangler distributes the rest.
177
+ */
178
+ function buildVersionSpecs(spec) {
179
+ const specs = [`${spec.versionId}@${spec.percentage}`];
180
+ const remaining = 100 - spec.percentage;
181
+ if (spec.previousVersionId && spec.previousVersionId !== spec.versionId && remaining > 0) specs.push(`${spec.previousVersionId}@${remaining}`);
182
+ return specs;
183
+ }
184
+ /**
185
+ * Build the full `wrangler versions deploy` invocation for a gradual rollout.
186
+ *
187
+ * `localWranglerExecutable` mirrors the deploy command's resolution: when a
188
+ * local `wrangler/bin/wrangler.js` exists it is run under `node`; otherwise the
189
+ * invocation falls back to `bunx wrangler …`.
190
+ */
191
+ function buildGradualDeployInvocation(spec, localWranglerExecutable) {
192
+ const command = localWranglerExecutable ? "node" : "bunx";
193
+ const args = localWranglerExecutable ? [
194
+ localWranglerExecutable,
195
+ "versions",
196
+ "deploy"
197
+ ] : [
198
+ "wrangler",
199
+ "versions",
200
+ "deploy"
201
+ ];
202
+ args.push(...buildVersionSpecs(spec));
203
+ args.push("--name", spec.workerName);
204
+ if (spec.message?.trim()) args.push("--message", spec.message.trim());
205
+ args.push("--yes");
206
+ return {
207
+ command,
208
+ args
209
+ };
210
+ }
211
+ //#endregion
150
212
  //#region src/cli/preview.ts
151
213
  function matchNamedValue(output, patterns) {
152
214
  for (const pattern of patterns) {
@@ -555,6 +617,8 @@ async function runDeployCommand(parsed, logger, options) {
555
617
  let deployMessage;
556
618
  let deployTag;
557
619
  let previewScopeName;
620
+ let rolloutPercentage;
621
+ let rolloutPreviousVersionId;
558
622
  let requireFreshProductionDeployment = false;
559
623
  let resolvedPreviewScopeName = process.env.DEVFLARE_PREVIEW_BRANCH?.trim() || void 0;
560
624
  const theme = createCliTheme(parsed.options);
@@ -570,6 +634,9 @@ async function runDeployCommand(parsed, logger, options) {
570
634
  branchName = resolvedParsed.options["branch-name"];
571
635
  deployMessage = resolvedParsed.options.message;
572
636
  deployTag = resolvedParsed.options.tag;
637
+ rolloutPercentage = parseDeployPercentage(resolvedParsed.options.percentage);
638
+ rolloutPreviousVersionId = typeof resolvedParsed.options.version === "string" ? resolvedParsed.options.version.trim() || void 0 : void 0;
639
+ if (rolloutPercentage !== void 0 && deployTarget.mode !== "production") throw new Error("--percentage is a production gradual rollout and is only valid with an explicit production target. Run `devflare deploy --prod --percentage <n>` (it cannot be combined with --preview or a named/branch preview deploy).");
573
640
  previewScopeName = branchName?.trim() || deployTarget.previewScopeRaw || void 0;
574
641
  resolvedPreviewScopeName = previewScopeName || process.env.DEVFLARE_PREVIEW_BRANCH?.trim() || void 0;
575
642
  requireFreshProductionDeployment = !preview && shouldRequireFreshProductionDeployment();
@@ -591,6 +658,7 @@ async function runDeployCommand(parsed, logger, options) {
591
658
  });
592
659
  const wranglerConfig = compileBuildConfig(deploymentStrategy.config, void 0, { alreadyResolved: true });
593
660
  logLine(logger, `${yellow("dry run", theme)} ${dim("Skipping actual deployment", theme)}`);
661
+ if (rolloutPercentage !== void 0) logLine(logger, dim(`Would upload a new Worker version and route ${rolloutPercentage}% of production traffic to it via \`wrangler versions deploy\`${rolloutPreviousVersionId ? `, keeping ${100 - rolloutPercentage}% on version ${rolloutPreviousVersionId}` : ""}.`, theme));
594
662
  const deploymentStrategyMessage = describeDeploymentStrategy(deploymentStrategy);
595
663
  if (deploymentStrategyMessage) logLine(logger, dim(deploymentStrategyMessage, theme));
596
664
  logLine(logger, dim("Would deploy with wrangler config:", theme));
@@ -647,13 +715,15 @@ async function runDeployCommand(parsed, logger, options) {
647
715
  if (prepared.config.bindings?.durableObjects && Object.keys(prepared.config.bindings.durableObjects).length > 0) logger.warn("Cloudflare does not currently generate preview URLs for Workers that implement Durable Objects.");
648
716
  if (prepared.config.migrations && prepared.config.migrations.length > 0) logger.warn("Cloudflare versions upload does not currently support Durable Object migrations.");
649
717
  }
650
- logLine(logger, dim(preview ? "Uploading preview version with Wrangler…" : "Deploying with Wrangler…", theme));
718
+ const usePercentageRollout = !preview && !isBranchScopedPreviewDeployment && rolloutPercentage !== void 0;
719
+ const uploadVersionOnly = preview || usePercentageRollout;
720
+ logLine(logger, dim(preview ? "Uploading preview version with Wrangler…" : usePercentageRollout ? "Uploading new Worker version with Wrangler…" : "Deploying with Wrangler…", theme));
651
721
  const deployStartedAt = /* @__PURE__ */ new Date();
652
722
  const wranglerOutputDirectory = join(cwd, ".devflare");
653
723
  const wranglerOutputFilePath = join(wranglerOutputDirectory, `wrangler-output-${Date.now()}-${process.pid}.ndjson`);
654
724
  await deps.fs.mkdir(wranglerOutputDirectory, { recursive: true });
655
725
  const wranglerCommand = localWranglerExecutable ? "node" : "bunx";
656
- const wranglerArgs = preview ? localWranglerExecutable ? [
726
+ const wranglerArgs = uploadVersionOnly ? localWranglerExecutable ? [
657
727
  localWranglerExecutable,
658
728
  "versions",
659
729
  "upload"
@@ -766,7 +836,7 @@ async function runDeployCommand(parsed, logger, options) {
766
836
  const message = error instanceof Error ? error.message : String(error);
767
837
  versionRecoveryDiagnostics.push(`deployment lookup: ${message}`);
768
838
  }
769
- if (!preview && !isBranchScopedPreviewDeployment && !resolvedVersionId && resolvedAccountId) try {
839
+ if (!preview && !isBranchScopedPreviewDeployment && !usePercentageRollout && !resolvedVersionId && resolvedAccountId) try {
770
840
  const fallbackDeployment = await resolveVersionIdFromLatestProductionDeployment({
771
841
  accountId: resolvedAccountId,
772
842
  workerName: prepared.config.name,
@@ -780,7 +850,7 @@ async function runDeployCommand(parsed, logger, options) {
780
850
  const message = error instanceof Error ? error.message : String(error);
781
851
  versionRecoveryDiagnostics.push(`deployment lookup: ${message}`);
782
852
  }
783
- if (!preview && !isBranchScopedPreviewDeployment && !resolvedVersionId && resolvedAccountId) try {
853
+ if (!preview && !isBranchScopedPreviewDeployment && !usePercentageRollout && !resolvedVersionId && resolvedAccountId) try {
784
854
  const currentDeployment = await resolveVersionIdFromCurrentProductionDeployment({
785
855
  accountId: resolvedAccountId,
786
856
  workerName: prepared.config.name
@@ -845,7 +915,7 @@ async function runDeployCommand(parsed, logger, options) {
845
915
  accountId: resolvedAccountId,
846
916
  workerName: prepared.config.name,
847
917
  versionId: resolvedVersionId,
848
- preview,
918
+ preview: preview || usePercentageRollout,
849
919
  logger,
850
920
  theme
851
921
  });
@@ -863,6 +933,52 @@ async function runDeployCommand(parsed, logger, options) {
863
933
  };
864
934
  }
865
935
  }
936
+ if (usePercentageRollout) {
937
+ if (!resolvedVersionId) {
938
+ const recoveryDetails = versionRecoveryDiagnostics.length > 0 ? ` Cloudflare fallback checks also failed: ${versionRecoveryDiagnostics.join(" | ")}` : "";
939
+ const rolloutError = `A new Worker version was uploaded, but Devflare could not resolve its version id, so it could not start the ${rolloutPercentage}% gradual rollout. Re-run \`devflare deploy --prod --percentage ${rolloutPercentage}\`, or split traffic manually with \`wrangler versions deploy\`.${recoveryDetails}`;
940
+ await persistDeployMetadata({
941
+ status: "failure",
942
+ exitCode: 1,
943
+ error: rolloutError
944
+ });
945
+ logger.error(rolloutError);
946
+ return {
947
+ exitCode: 1,
948
+ output: structuredOutput
949
+ };
950
+ }
951
+ const rolloutInvocation = buildGradualDeployInvocation({
952
+ versionId: resolvedVersionId,
953
+ percentage: rolloutPercentage,
954
+ previousVersionId: rolloutPreviousVersionId,
955
+ workerName: prepared.config.name,
956
+ message: deployMessage?.trim() || void 0
957
+ }, localWranglerExecutable);
958
+ logLine(logger, dim(`Routing ${rolloutPercentage}% of production traffic to the new version with Wrangler…`, theme));
959
+ const rolloutProc = await deps.exec.exec(rolloutInvocation.command, rolloutInvocation.args, {
960
+ cwd,
961
+ stdio: "inherit",
962
+ env: {
963
+ ...process.env,
964
+ FORCE_COLOR: process.env.FORCE_COLOR ?? "0"
965
+ }
966
+ });
967
+ if (rolloutProc.exitCode !== 0) {
968
+ const rolloutError = `The new Worker version ${resolvedVersionId} was uploaded, but \`wrangler versions deploy\` failed to route ${rolloutPercentage}% of production traffic to it. Re-run the rollout, or split traffic manually with \`wrangler versions deploy\`.`;
969
+ await persistDeployMetadata({
970
+ status: "failure",
971
+ exitCode: 1,
972
+ error: rolloutProc.stderr || rolloutProc.stdout || rolloutError
973
+ });
974
+ logger.error(rolloutError);
975
+ return {
976
+ exitCode: 1,
977
+ output: structuredOutput
978
+ };
979
+ }
980
+ logger.success(`Routed ${rolloutPercentage}% of production traffic to version ${resolvedVersionId}`);
981
+ }
866
982
  if (resolvedAccountId) {
867
983
  const previewRegistryScope = isBranchScopedPreviewDeployment ? deployTarget.previewScope : void 0;
868
984
  const previewRegistryUrl = preview || isBranchScopedPreviewDeployment ? resolvedPreviewUrl : void 0;
@@ -0,0 +1,239 @@
1
+ import { r as loadConfig } from "./loader-RZhqzsRJ.js";
2
+ import { p as getApiToken } from "./api-TzdliH-6.js";
3
+ import { d as green, f as logLine, g as yellow, h as whiteDim, m as red, o as dim, r as createCliTheme } from "./ui-BUoZApvE.js";
4
+ import { r as resolveCloudflareAccountId, t as asOptionalString } from "./command-utils-C48hJMln.js";
5
+ //#region src/cli/commands/tail.ts
6
+ const CLOUDFLARE_API_BASE = "https://api.cloudflare.com/client/v4";
7
+ const TAIL_SUBPROTOCOL = "trace-v1";
8
+ function parseTailFormat(value) {
9
+ const normalized = asOptionalString(value)?.toLowerCase();
10
+ if (normalized === void 0 || normalized === "pretty") return "pretty";
11
+ if (normalized === "json") return "json";
12
+ throw new Error(`--format must be "pretty" or "json", received "${String(value)}".`);
13
+ }
14
+ function formatTimestamp(timestamp) {
15
+ if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) return (/* @__PURE__ */ new Date()).toISOString();
16
+ return new Date(timestamp).toISOString();
17
+ }
18
+ function describeTriggerEvent(event) {
19
+ if (!event) return "event";
20
+ if (event.request?.url) return `${event.request.method ?? "GET"} ${event.request.url}`;
21
+ if (event.cron) return `cron ${event.cron}`;
22
+ if (event.queue) return `queue ${event.queue}`;
23
+ if (event.mailFrom) return `email from ${event.mailFrom}`;
24
+ if (event.rpcMethod) return `rpc ${event.rpcMethod}`;
25
+ return "event";
26
+ }
27
+ function stringifyLogMessage(message) {
28
+ if (typeof message === "string") return message;
29
+ if (Array.isArray(message)) return message.map((part) => stringifyLogMessage(part)).join(" ");
30
+ if (message === void 0) return "";
31
+ try {
32
+ return JSON.stringify(message);
33
+ } catch {
34
+ return String(message);
35
+ }
36
+ }
37
+ /**
38
+ * Render one trace event as the lines `devflare tail` prints in pretty mode.
39
+ * Returns an ordered list of `{ level, text }` so the caller can route each
40
+ * line to the matching logger method (and tests can assert content without
41
+ * ANSI noise).
42
+ */
43
+ function formatTailEventLines(event) {
44
+ const lines = [];
45
+ const timestamp = formatTimestamp(event.eventTimestamp);
46
+ const outcome = event.outcome ?? "unknown";
47
+ const trigger = describeTriggerEvent(event.event);
48
+ const scriptName = event.scriptName ? ` ${event.scriptName}` : "";
49
+ lines.push({
50
+ level: outcome === "ok" ? "info" : "error",
51
+ text: `[${timestamp}]${scriptName} ${trigger} — ${outcome}`
52
+ });
53
+ for (const log of event.logs ?? []) {
54
+ const level = log.level ?? "log";
55
+ const text = stringifyLogMessage(log.message);
56
+ lines.push({
57
+ level: level === "error" || level === "warn" ? level : "info",
58
+ text: ` ${level}: ${text}`
59
+ });
60
+ }
61
+ for (const exception of event.exceptions ?? []) {
62
+ const name = exception.name ?? "Error";
63
+ const message = exception.message ?? "";
64
+ lines.push({
65
+ level: "error",
66
+ text: ` exception ${name}: ${message}`
67
+ });
68
+ }
69
+ return lines;
70
+ }
71
+ async function readEnvelope(response, endpoint) {
72
+ const text = await response.text();
73
+ let parsed;
74
+ try {
75
+ parsed = JSON.parse(text);
76
+ } catch {
77
+ throw new Error(`Cloudflare ${endpoint} returned an invalid response (status ${response.status}).`);
78
+ }
79
+ if (!parsed.success) {
80
+ const first = parsed.errors?.[0];
81
+ throw new Error(first ? `Cloudflare ${endpoint} failed (${first.code}): ${first.message}` : `Cloudflare ${endpoint} failed (status ${response.status}).`);
82
+ }
83
+ return parsed.result;
84
+ }
85
+ async function createTail(accountId, workerName, token) {
86
+ const result = await readEnvelope(await fetch(`${CLOUDFLARE_API_BASE}/accounts/${accountId}/workers/scripts/${encodeURIComponent(workerName)}/tails`, {
87
+ method: "POST",
88
+ headers: {
89
+ Authorization: `Bearer ${token}`,
90
+ "Content-Type": "application/json"
91
+ }
92
+ }), `create tail for ${workerName}`);
93
+ return {
94
+ id: result.id,
95
+ url: result.url,
96
+ expiresAt: result.expires_at
97
+ };
98
+ }
99
+ async function deleteTail(accountId, workerName, tailId, token) {
100
+ const response = await fetch(`${CLOUDFLARE_API_BASE}/accounts/${accountId}/workers/scripts/${encodeURIComponent(workerName)}/tails/${encodeURIComponent(tailId)}`, {
101
+ method: "DELETE",
102
+ headers: { Authorization: `Bearer ${token}` }
103
+ });
104
+ if (!response.ok) await readEnvelope(response, `delete tail ${tailId}`);
105
+ }
106
+ function parseTailMessage(raw) {
107
+ const parsed = JSON.parse(raw);
108
+ if (Array.isArray(parsed)) return parsed;
109
+ return [parsed];
110
+ }
111
+ async function resolveWorkerName(parsed, cwd, configFile, fallbackArg) {
112
+ const explicit = asOptionalString(parsed.options.worker) ?? fallbackArg;
113
+ if (explicit) return explicit;
114
+ try {
115
+ return (await loadConfig({
116
+ cwd,
117
+ configFile
118
+ })).name;
119
+ } catch {
120
+ return;
121
+ }
122
+ }
123
+ async function runTailCommand(parsed, logger, options) {
124
+ const theme = createCliTheme(parsed.options);
125
+ const cwd = options.cwd ?? process.cwd();
126
+ const configFile = asOptionalString(parsed.options.config);
127
+ let format;
128
+ try {
129
+ format = parseTailFormat(parsed.options.format);
130
+ } catch (error) {
131
+ logger.error(error instanceof Error ? error.message : String(error));
132
+ return { exitCode: 1 };
133
+ }
134
+ const fallbackArg = parsed.args[0];
135
+ const workerName = await resolveWorkerName(parsed, cwd, configFile, fallbackArg);
136
+ if (!workerName) {
137
+ logger.error("No Worker name could be resolved.");
138
+ logLine(logger, dim("Pass a Worker name (`devflare tail <worker>`), --worker <name>, or run inside a configured package.", theme));
139
+ return { exitCode: 1 };
140
+ }
141
+ const token = await getApiToken();
142
+ if (!token) {
143
+ logger.error("Not authenticated with Cloudflare.");
144
+ logLine(logger, dim("Run `devflare login` first, or set CLOUDFLARE_API_TOKEN.", theme));
145
+ return { exitCode: 1 };
146
+ }
147
+ let configuredAccountId;
148
+ try {
149
+ configuredAccountId = (await loadConfig({
150
+ cwd,
151
+ configFile
152
+ })).accountId;
153
+ } catch {
154
+ configuredAccountId = void 0;
155
+ }
156
+ const accountId = await resolveCloudflareAccountId({
157
+ explicitAccountId: asOptionalString(parsed.options.account),
158
+ configuredAccountId
159
+ });
160
+ if (!accountId) {
161
+ logger.error("No Cloudflare account could be resolved.");
162
+ logLine(logger, dim("Use --account <id> or set accountId in devflare.config.* / CLOUDFLARE_ACCOUNT_ID.", theme));
163
+ return { exitCode: 1 };
164
+ }
165
+ logLine(logger);
166
+ logLine(logger, `${yellow("tail", theme)} ${dim("Streaming live logs from Cloudflare", theme)}`);
167
+ logLine(logger, `${dim("worker", theme)} ${green(workerName, theme)}`);
168
+ logLine(logger, `${dim("account", theme)} ${whiteDim(accountId, theme)}`);
169
+ let createdTail;
170
+ try {
171
+ createdTail = await createTail(accountId, workerName, token);
172
+ } catch (error) {
173
+ logger.error(error instanceof Error ? error.message : String(error));
174
+ return { exitCode: 1 };
175
+ }
176
+ logLine(logger, dim(`Connected to tail ${createdTail.id}${createdTail.expiresAt ? ` (expires ${createdTail.expiresAt})` : ""}. Press Ctrl-C to stop.`, theme));
177
+ const { WebSocket: WsWebSocket } = await import("ws");
178
+ return new Promise((resolve) => {
179
+ let settled = false;
180
+ const socket = new WsWebSocket(createdTail.url, TAIL_SUBPROTOCOL, { headers: {
181
+ Authorization: `Bearer ${token}`,
182
+ "User-Agent": "devflare"
183
+ } });
184
+ const cleanup = async (exitCode) => {
185
+ if (settled) return;
186
+ settled = true;
187
+ try {
188
+ socket.close();
189
+ } catch {}
190
+ process.off("SIGINT", onSigint);
191
+ try {
192
+ await deleteTail(accountId, workerName, createdTail.id, token);
193
+ } catch (error) {
194
+ logger.warn(`Could not delete the tail session ${createdTail.id}; it will expire on its own. ${error instanceof Error ? error.message : String(error)}`);
195
+ }
196
+ resolve({ exitCode });
197
+ };
198
+ const onSigint = () => {
199
+ logLine(logger);
200
+ logLine(logger, dim("Stopping tail…", theme));
201
+ cleanup(0);
202
+ };
203
+ process.on("SIGINT", onSigint);
204
+ socket.on("open", () => {
205
+ logger.success("Tail stream open");
206
+ });
207
+ socket.on("message", (data) => {
208
+ const raw = typeof data === "string" ? data : String(data);
209
+ let events;
210
+ try {
211
+ events = parseTailMessage(raw);
212
+ } catch {
213
+ logger.warn(`Received a non-JSON tail message: ${raw.slice(0, 200)}`);
214
+ return;
215
+ }
216
+ for (const event of events) {
217
+ if (format === "json") {
218
+ logger.log(JSON.stringify(event));
219
+ continue;
220
+ }
221
+ for (const line of formatTailEventLines(event)) if (line.level === "error") logLine(logger, red(line.text, theme));
222
+ else if (line.level === "warn") logLine(logger, yellow(line.text, theme));
223
+ else logLine(logger, line.text);
224
+ }
225
+ });
226
+ socket.on("error", (error) => {
227
+ logger.error(`Tail stream error: ${error.message}`);
228
+ cleanup(1);
229
+ });
230
+ socket.on("close", () => {
231
+ if (!settled) {
232
+ logLine(logger, dim("Tail stream closed.", theme));
233
+ cleanup(0);
234
+ }
235
+ });
236
+ });
237
+ }
238
+ //#endregion
239
+ export { runTailCommand };
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAoB9C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AA2BjE,wBAAsB,gBAAgB,CACrC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC,CA8hBpB"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqB9C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AA2BjE,wBAAsB,gBAAgB,CACrC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC,CA0pBpB"}
@@ -0,0 +1,44 @@
1
+ import type { ConsolaInstance } from 'consola';
2
+ import type { CliOptions, CliResult, ParsedArgs } from '../index.js';
3
+ export type TailFormat = 'pretty' | 'json';
4
+ export interface TailTraceLog {
5
+ level?: string;
6
+ message?: unknown;
7
+ timestamp?: number;
8
+ }
9
+ export interface TailTraceException {
10
+ name?: string;
11
+ message?: string;
12
+ timestamp?: number;
13
+ }
14
+ export interface TailTraceEvent {
15
+ scriptName?: string | null;
16
+ outcome?: string;
17
+ eventTimestamp?: number | null;
18
+ event?: {
19
+ request?: {
20
+ method?: string;
21
+ url?: string;
22
+ };
23
+ cron?: string;
24
+ scheduledTime?: number;
25
+ queue?: string;
26
+ mailFrom?: string;
27
+ rpcMethod?: string;
28
+ } | null;
29
+ logs?: TailTraceLog[];
30
+ exceptions?: TailTraceException[];
31
+ }
32
+ export declare function parseTailFormat(value: string | boolean | undefined): TailFormat;
33
+ /**
34
+ * Render one trace event as the lines `devflare tail` prints in pretty mode.
35
+ * Returns an ordered list of `{ level, text }` so the caller can route each
36
+ * line to the matching logger method (and tests can assert content without
37
+ * ANSI noise).
38
+ */
39
+ export declare function formatTailEventLines(event: TailTraceEvent): Array<{
40
+ level: string;
41
+ text: string;
42
+ }>;
43
+ export declare function runTailCommand(parsed: ParsedArgs, logger: ConsolaInstance, options: CliOptions): Promise<CliResult>;
44
+ //# sourceMappingURL=tail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tail.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/tail.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAI9C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAMjE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAA;AAkB1C,MAAM,WAAW,YAAY;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,KAAK,CAAC,EAAE;QACP,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,SAAS,CAAC,EAAE,MAAM,CAAA;KAClB,GAAG,IAAI,CAAA;IACR,IAAI,CAAC,EAAE,YAAY,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,kBAAkB,EAAE,CAAA;CACjC;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAW/E;AA0DD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,cAAc,GACnB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA+BxC;AAwHD,wBAAsB,cAAc,CACnC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC,CAiLpB"}
@@ -0,0 +1,49 @@
1
+ export interface GradualDeploySpec {
2
+ /** Worker version id receiving the rollout percentage. */
3
+ versionId: string;
4
+ /** Percentage of production traffic to route to `versionId` (0–100). */
5
+ percentage: number;
6
+ /**
7
+ * The currently-live version id that receives the remaining percentage. When
8
+ * omitted (or equal to `versionId`) the rollout deploys a single version and
9
+ * Wrangler distributes the remaining traffic itself.
10
+ */
11
+ previousVersionId?: string;
12
+ /** Worker name (`--name`). */
13
+ workerName: string;
14
+ /** Optional deployment message (`--message`). */
15
+ message?: string;
16
+ }
17
+ export interface ResolvedWranglerInvocation {
18
+ command: string;
19
+ args: string[];
20
+ }
21
+ /**
22
+ * Parse and validate the `--percentage` CLI option.
23
+ *
24
+ * Returns `undefined` when the flag is absent (a normal full deploy). Throws a
25
+ * clear error when the value is present but not an integer in `[0, 100]` — the
26
+ * same bounds Wrangler enforces — so a typo never silently becomes a full
27
+ * deploy.
28
+ */
29
+ export declare function parseDeployPercentage(value: string | boolean | undefined): number | undefined;
30
+ /**
31
+ * Build the positional `<version-id>@<percentage>` version specs for a rollout.
32
+ *
33
+ * Wrangler's `versions deploy` consumes traffic splits as positional specs (see
34
+ * `wrangler versions deploy --help`: "Shorthand notation to deploy Worker
35
+ * Version(s) [<version-id>@<percentage>..]"). When a distinct previous version
36
+ * id is provided, the remaining percentage is routed to it so the split is
37
+ * fully specified and Wrangler never has to prompt; otherwise only the new
38
+ * version is named and Wrangler distributes the rest.
39
+ */
40
+ export declare function buildVersionSpecs(spec: GradualDeploySpec): string[];
41
+ /**
42
+ * Build the full `wrangler versions deploy` invocation for a gradual rollout.
43
+ *
44
+ * `localWranglerExecutable` mirrors the deploy command's resolution: when a
45
+ * local `wrangler/bin/wrangler.js` exists it is run under `node`; otherwise the
46
+ * invocation falls back to `bunx wrangler …`.
47
+ */
48
+ export declare function buildGradualDeployInvocation(spec: GradualDeploySpec, localWranglerExecutable: string | null): ResolvedWranglerInvocation;
49
+ //# sourceMappingURL=gradual-deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gradual-deploy.d.ts","sourceRoot":"","sources":["../../src/cli/gradual-deploy.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,iBAAiB;IACjC,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,0BAA0B;IAC1C,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAoB7F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,EAAE,CASnE;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,iBAAiB,EACvB,uBAAuB,EAAE,MAAM,GAAG,IAAI,GACpC,0BAA0B,CAmB5B"}
@@ -1,4 +1,4 @@
1
1
  import type { HelpPage } from '../types.js';
2
2
  export declare const CORE_HELP_PAGES: HelpPage[];
3
- export declare const CORE_COMMANDS: readonly ["init", "dev", "build", "deploy", "types", "doctor", "config", "account", "login", "previews", "productions", "worker", "tokens", "secrets", "ai", "remote", "help", "version"];
3
+ export declare const CORE_COMMANDS: readonly ["init", "dev", "build", "deploy", "types", "doctor", "config", "account", "login", "previews", "productions", "worker", "tokens", "secrets", "tail", "ai", "remote", "help", "version"];
4
4
  //# sourceMappingURL=core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/cli/help-pages/pages/core.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,eAAO,MAAM,eAAe,EAAE,QAAQ,EAgbrC,CAAA;AAED,eAAO,MAAM,aAAa,2LAAW,CAAA"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/cli/help-pages/pages/core.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,eAAO,MAAM,eAAe,EAAE,QAAQ,EA+brC,CAAA;AAED,eAAO,MAAM,aAAa,mMAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../../src/cli/help-pages/pages/misc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,eAAO,MAAM,eAAe,EAAE,QAAQ,EA4KrC,CAAA"}
1
+ {"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../../src/cli/help-pages/pages/misc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,eAAO,MAAM,eAAe,EAAE,QAAQ,EAkNrC,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import type { HelpEntry, HelpPage } from './types.js';
2
- export declare const COMMANDS: readonly ['init', 'dev', 'build', 'deploy', 'types', 'doctor', 'config', 'account', 'login', 'previews', 'productions', 'worker', 'tokens', 'secrets', 'ai', 'remote', 'help', 'version'];
2
+ export declare const COMMANDS: readonly ['init', 'dev', 'build', 'deploy', 'types', 'doctor', 'config', 'account', 'login', 'previews', 'productions', 'worker', 'tokens', 'secrets', 'tail', 'ai', 'remote', 'help', 'version'];
3
3
  export type Command = (typeof COMMANDS)[number];
4
4
  export declare const COMMAND_ALIASES: Record<string, string>;
5
5
  export declare const COMMON_OPTIONS: HelpEntry[];
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cli/help-pages/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAElD,eAAO,MAAM,QAAQ,YACpB,MAAM,EACN,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,SAAS,CACA,CAAA;AACV,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,CAAA;AAEzD,eAAO,MAAM,cAAc,EAAE,SAAS,EAgBrC,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,SAG5B,CAAA;AAED,eAAO,MAAM,uBAAuB,EAAE,SAAS,EAM9C,CAAA;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAErE;AAuBD,KAAK,iCAAiC,GAAG,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,OAAO,EAAE,SAAS,EAAE,EACpB,QAAQ,EAAE,SAAS,EAAE,EACrB,KAAK,CAAC,EAAE,MAAM,EAAE,KACZ,QAAQ,CAAA;AAmBb,wBAAgB,0BAA0B,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,GACxB,QAAQ,CAaV;AAED,wBAAgB,0BAA0B,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,QAAQ,EAAE,SAAS,EAAE,EACrB,KAAK,EAAE,MAAM,EAAE,GACb,QAAQ,CAUV;AAED,eAAO,MAAM,2BAA2B,mCAAsD,CAAA;AAE9F,eAAO,MAAM,+BAA+B,mCACW,CAAA"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cli/help-pages/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAElD,eAAO,MAAM,QAAQ,YACpB,MAAM,EACN,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,SAAS,CACA,CAAA;AACV,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,CAAA;AAEzD,eAAO,MAAM,cAAc,EAAE,SAAS,EAgBrC,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,SAG5B,CAAA;AAED,eAAO,MAAM,uBAAuB,EAAE,SAAS,EAM9C,CAAA;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAErE;AAuBD,KAAK,iCAAiC,GAAG,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,OAAO,EAAE,SAAS,EAAE,EACpB,QAAQ,EAAE,SAAS,EAAE,EACrB,KAAK,CAAC,EAAE,MAAM,EAAE,KACZ,QAAQ,CAAA;AAmBb,wBAAgB,0BAA0B,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,GACxB,QAAQ,CAaV;AAED,wBAAgB,0BAA0B,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,QAAQ,EAAE,SAAS,EAAE,EACrB,KAAK,EAAE,MAAM,EAAE,GACb,QAAQ,CAUV;AAED,eAAO,MAAM,2BAA2B,mCAAsD,CAAA;AAE9F,eAAO,MAAM,+BAA+B,mCACW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAA;IACzC,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACrC;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAUD;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAgEpD;AAMD;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAyGzF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAA;IACzC,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACrC;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAUD;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAgEpD;AAMD;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CA4GzF"}
package/dist/cli/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as runCli, t as parseArgs } from "../_chunks/cli-Ba6JBLOz.js";
1
+ import { n as runCli, t as parseArgs } from "../_chunks/cli-Btoe8xx4.js";
2
2
  export { parseArgs, runCli };
package/dist/index.js CHANGED
@@ -4,6 +4,6 @@ import { a as ConfigResourceResolutionError, d as configSchema, n as ConfigValid
4
4
  import { n as compileConfig, s as stringifyConfig } from "./_chunks/compiler-Dc97FkBg.js";
5
5
  import { t as workerName } from "./_chunks/workerName-CFJsLZA-.js";
6
6
  import { n as getDurableObjectOptions, t as durableObject } from "./_chunks/decorators-QmV57ixr.js";
7
- import { n as runCli, t as parseArgs } from "./_chunks/cli-Ba6JBLOz.js";
7
+ import { n as runCli, t as parseArgs } from "./_chunks/cli-Btoe8xx4.js";
8
8
  import { i as vars, r as env } from "./_chunks/env-S0_nMVz1.js";
9
9
  export { ConfigNotFoundError, ConfigResourceResolutionError, ConfigValidationError, compileConfig, configSchema, defineConfig as default, defineConfig, durableObject, env, getDurableObjectOptions, loadConfig, loadResolvedConfig, parseArgs, preview, ref, runCli, stringifyConfig, vars, workerName };
@@ -4511,6 +4511,18 @@ function createMockAISearchNamespace(options = {}) {
4511
4511
  //#endregion
4512
4512
  //#region src/test/offline-bindings.ts
4513
4513
  const SUPPORT_MATRIX = {
4514
+ durableObjects: {
4515
+ service: "durableObjects",
4516
+ tier: "offline-native",
4517
+ reason: "Miniflare executes Durable Object classes locally, so createTestContext() runs them fully offline. There is no pure in-memory createMockEnv() mock for a DO (a real instance needs the Miniflare runtime).",
4518
+ recommendation: "Use createTestContext() (Miniflare-backed) to run Durable Objects locally; there is no standalone createMockEnv() DO fixture."
4519
+ },
4520
+ services: {
4521
+ service: "services",
4522
+ tier: "offline-native",
4523
+ reason: "Miniflare resolves service bindings worker-to-worker locally, so createTestContext() runs them fully offline. There is no pure in-memory createMockEnv() mock (a real service binding needs the Miniflare runtime to wire the target Worker).",
4524
+ recommendation: "Use createTestContext() (Miniflare-backed) for worker-to-worker service bindings; there is no standalone createMockEnv() service fixture."
4525
+ },
4514
4526
  rateLimits: {
4515
4527
  service: "rateLimits",
4516
4528
  tier: "offline-native",
@@ -1 +1 @@
1
- {"version":3,"file":"offline-bindings.d.ts","sourceRoot":"","sources":["../../src/test/offline-bindings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"offline-bindings.d.ts","sourceRoot":"","sources":["../../src/test/offline-bindings.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devflare",
3
- "version": "1.0.0-next.46",
3
+ "version": "1.0.0-next.48",
4
4
  "description": "Devflare is a developer-first toolkit for Cloudflare Workers that sits on top of Miniflare and Wrangler-compatible config",
5
5
  "repository": {
6
6
  "type": "git",