Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/dependabot-update-14147.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"miniflare": patch
"wrangler": patch
---

Update dependencies of "miniflare", "wrangler"

The following dependency versions have been updated:

| Dependency | From | To |
| ---------- | ------------ | ------------ |
| workerd | 1.20260529.1 | 1.20260601.1 |
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

Fix `wrangler deploy --upload-source-maps` silently skipping source maps when the entry file ends with magic comments after `//# sourceMappingURL=`

Wrangler previously assumed the `//# sourceMappingURL=` comment was the last non-empty line of a module. Tools like `sentry-cli sourcemaps inject` append a `//# debugId=` comment after it, which silently caused source maps to be omitted from the upload form, most commonly when deploying with `--no-bundle --upload-source-maps`. Wrangler now scans trailing magic comments (lines starting with `//#` or `//@`) and detects the `//# sourceMappingURL=` comment regardless of which other magic comments follow it.
7 changes: 7 additions & 0 deletions .changeset/workflows-restart-from-step-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": minor
---

Add restart-from-step options to `wrangler workflows instances restart`

You can now restart a Workflow instance from a specific step using `--from-step-name`, with optional `--from-step-count` and `--from-step-type` disambiguation. These options work for both remote Workflow instances and local `wrangler dev --local` sessions.
2 changes: 1 addition & 1 deletion packages/miniflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"@cspotcode/source-map-support": "0.8.1",
"sharp": "0.34.5",
"undici": "catalog:default",
"workerd": "1.20260529.1",
"workerd": "1.20260601.1",
"ws": "catalog:default",
"youch": "4.1.0-beta.10"
},
Expand Down
23 changes: 23 additions & 0 deletions packages/miniflare/scripts/openapi-filter-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,29 @@ const config = {
description:
"The action to perform on the workflow instance.",
},
from: {
type: "object",
description:
"The step to restart the instance from. Only valid when action is restart.",
required: ["name"],
properties: {
name: {
type: "string",
description: "The name of the step.",
},
count: {
type: "integer",
minimum: 1,
description:
"The 1-based index of the step when multiple steps share the same name and type. Defaults to 1.",
},
type: {
type: "string",
enum: ["do", "sleep", "waitForEvent"],
description: "The step type. Defaults to do.",
},
},
},
},
},
},
Expand Down
17 changes: 11 additions & 6 deletions packages/miniflare/src/workers/local-explorer/explorer.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
zWorkersKvNamespaceGetMultipleKeyValuePairsData,
zWorkersKvNamespaceListANamespaceSKeysData,
zWorkersKvNamespaceListNamespacesData,
zWorkflowsChangeInstanceStatusData,
zWorkflowsListInstancesData,
} from "./generated/zod.gen";
import openApiSpec from "./openapi.local.json";
Expand Down Expand Up @@ -317,12 +318,16 @@ app.get("/api/workflows/:workflow_name/instances/:instance_id", (c) =>
)
);

app.patch("/api/workflows/:workflow_name/instances/:instance_id/status", (c) =>
changeWorkflowInstanceStatus(
c,
c.req.param("workflow_name"),
c.req.param("instance_id")
)
app.patch(
"/api/workflows/:workflow_name/instances/:instance_id/status",
validateRequestBody(zWorkflowsChangeInstanceStatusData.shape.body),
(c) =>
changeWorkflowInstanceStatus(
c,
c.req.param("workflow_name"),
c.req.param("instance_id"),
c.req.valid("json")
)
);

app.post(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1730,6 +1730,23 @@ export type WorkflowsChangeInstanceStatusData = {
* The action to perform on the workflow instance.
*/
action: "pause" | "resume" | "restart" | "terminate";
/**
* The step to restart the instance from. Only valid when action is restart.
*/
from?: {
/**
* The name of the step.
*/
name: string;
/**
* The 1-based index of the step when multiple steps share the same name and type. Defaults to 1.
*/
count?: number;
/**
* The step type. Defaults to do.
*/
type?: "do" | "sleep" | "waitForEvent";
};
};
path: {
workflow_name: WorkflowsWorkflowName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,13 @@ export const zWorkflowsGetInstanceDetailsResponse =
export const zWorkflowsChangeInstanceStatusData = z.object({
body: z.object({
action: z.enum(["pause", "resume", "restart", "terminate"]),
from: z
.object({
name: z.string(),
count: z.number().int().gte(1).optional(),
type: z.enum(["do", "sleep", "waitForEvent"]).optional(),
})
.optional(),
}),
path: z.object({
workflow_name: zWorkflowsWorkflowName,
Expand Down
21 changes: 21 additions & 0 deletions packages/miniflare/src/workers/local-explorer/openapi.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -1789,6 +1789,27 @@
"type": "string",
"enum": ["pause", "resume", "restart", "terminate"],
"description": "The action to perform on the workflow instance."
},
"from": {
"type": "object",
"description": "The step to restart the instance from. Only valid when action is restart.",
"required": ["name"],
"properties": {
"name": {
"type": "string",
"description": "The name of the step."
},
"count": {
"type": "integer",
"minimum": 1,
"description": "The 1-based index of the step when multiple steps share the same name and type. Defaults to 1."
},
"type": {
"type": "string",
"enum": ["do", "sleep", "waitForEvent"],
"description": "The step type. Defaults to do."
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
import { errorResponse, wrapResponse } from "../common";
import type { AppContext } from "../common";
import type { Env } from "../explorer.worker";
import type { WorkflowsWorkflow } from "../generated";
import type {
WorkflowsChangeInstanceStatusData,
WorkflowsWorkflow,
} from "../generated";
import type { zWorkflowsListInstancesData } from "../generated/zod.gen";
import type { RestartFromStep } from "@cloudflare/workflows-shared/src/binding";
import type { z } from "zod";
Expand Down Expand Up @@ -843,7 +846,8 @@ export async function createWorkflowInstance(
export async function changeWorkflowInstanceStatus(
c: AppContext,
workflowName: string,
instanceId: string
instanceId: string,
body: WorkflowsChangeInstanceStatusData["body"]
): Promise<Response> {
const workflow = getWorkflowBinding(c.env, workflowName);

Expand All @@ -856,7 +860,7 @@ export async function changeWorkflowInstanceStatus(
{
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: await c.req.text(),
body: JSON.stringify(body),
}
);
if (response) {
Expand All @@ -872,10 +876,6 @@ export async function changeWorkflowInstanceStatus(
}

try {
const body = (await c.req.json()) as {
action: string;
from?: RestartFromStep;
};
const { action } = body;

if (!["pause", "resume", "restart", "terminate"].includes(action)) {
Expand Down
4 changes: 2 additions & 2 deletions packages/workflows-shared/src/lib/restart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function resolveGroupKeysToWipe(
sleep: InstanceEvent.SLEEP_START,
waitForEvent: InstanceEvent.WAIT_START,
};
const targetEvent = param.type ? stepTypeToEvent[param.type] : null;
const targetEvent = stepTypeToEvent[param.type ?? "do"];
const targetCount = param.count ?? 1;

const cursor = sql.exec<RawInstanceLog>(
Expand Down Expand Up @@ -57,7 +57,7 @@ export function resolveGroupKeysToWipe(
if (rawStepName !== param.name) {
continue;
}
if (targetEvent && row.event !== targetEvent) {
if (row.event !== targetEvent) {
continue;
}

Expand Down
66 changes: 66 additions & 0 deletions packages/workflows-shared/tests/engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,72 @@ describe("Engine", () => {
).toBe(true);
});

it("should default restart from step type to step.do", async ({
expect,
}) => {
const instanceId = "RESTART-FROM-STEP-DEFAULT-DO";
const engineId = env.ENGINE.idFromName(instanceId);

const engineStub = await runWorkflowAndAwait(
instanceId,
async (_event: unknown, step: WorkflowStep) => {
const setup = await step.do("setup", async () => crypto.randomUUID());
await step.sleep("checkpoint", 1);
const between = await step.do("between", async () =>
crypto.randomUUID()
);
const checkpoint = await step.do("checkpoint", async () =>
crypto.randomUUID()
);
const after = await step.do("after", async () => crypto.randomUUID());
return { setup, between, checkpoint, after };
}
);

const logsBefore = (await engineStub.readLogs()) as EngineLogs;
const stepResultsBefore = logsBefore.logs
.filter((log) => log.event === InstanceEvent.STEP_SUCCESS)
.map((log) => log.metadata.result);

try {
await runInDurableObject(engineStub, async (engine) => {
await engine.changeInstanceStatus("restart", {
name: "checkpoint",
});
});
} catch (e) {
if (!isAbortError(e)) {
throw e;
}
}

const restartedStub = env.ENGINE.get(engineId);

await runInDurableObject(restartedStub, async (engine) => {
await engine.attemptRestart();
});

await vi.waitUntil(
async () => {
const status = await runInDurableObject(restartedStub, (engine) =>
engine.getStatus()
);
return status === InstanceStatus.Complete;
},
{ timeout: 5000 }
);

const logsAfter = (await restartedStub.readLogs()) as EngineLogs;
const stepResultsAfter = logsAfter.logs
.filter((log) => log.event === InstanceEvent.STEP_SUCCESS)
.map((log) => log.metadata.result);

expect(stepResultsAfter[0]).toEqual(stepResultsBefore[0]);
expect(stepResultsAfter[1]).toEqual(stepResultsBefore[1]);
expect(stepResultsAfter[2]).not.toEqual(stepResultsBefore[2]);
expect(stepResultsAfter[3]).not.toEqual(stepResultsBefore[3]);
});

it("should throw when restarting from a non-existing step", async ({
expect,
}) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/wrangler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"miniflare": "workspace:*",
"path-to-regexp": "6.3.0",
"unenv": "2.0.0-rc.24",
"workerd": "1.20260529.1"
"workerd": "1.20260601.1"
},
"devDependencies": {
"@aws-sdk/client-s3": "^3.721.0",
Expand Down
43 changes: 43 additions & 0 deletions packages/wrangler/src/__tests__/deploy/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,49 @@ export default { fetch() { return new Response(mod); } };`;
await runWrangler("deploy");
});

it("should include source maps when a //# debugId= comment follows the //# sourceMappingURL= comment", async ({
expect,
}) => {
writeWranglerConfig({
no_bundle: true,
main: "index.js",
upload_source_maps: true,
build: {
command: `echo "custom build script"`,
},
});
// Mirrors the output of `sentry-cli sourcemaps inject`, which appends
// a `//# debugId=` comment on a new line after `//# sourceMappingURL=`.
fs.writeFileSync(
"index.js",
`export default { fetch() { return new Response("Hello World"); } }\n` +
"//# sourceMappingURL=index.js.map\n" +
"\n" +
"//# debugId=7f1ca8ac-1725-5ca5-b961-279f0ab7279a\n"
);
fs.writeFileSync(
"index.js.map",
JSON.stringify({
version: 3,
sources: ["index.ts"],
sourceRoot: "",
file: "index.js",
})
);

mockSubDomainRequest();
mockUploadWorkerRequest({
expectedMainModule: "index.js",
expectedModules: {
"index.js.map": expect.stringMatching(
/"sources":\["index.ts"\],"sourceRoot":"".*"file":"index.js"/
),
},
});

await runWrangler("deploy");
});

it("should not include source maps emitted by custom build when upload_source_maps = false", async () => {
writeWranglerConfig({
no_bundle: true,
Expand Down
Loading
Loading