Skip to content

Commit 5f0ef18

Browse files
authored
Revert "feat: cache purge API (object cache + native Workers Caching)" (#2281)
Reverts e886554 (#2275), which was merged before maintainer approval.
1 parent 668184f commit 5f0ef18

37 files changed

Lines changed: 15 additions & 1437 deletions

.changeset/object-cache-purge-api.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

packages/admin/src/lib/api/marketplace.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ export const CAPABILITY_LABELS: Record<string, MessageDescriptor> = {
282282
"media:read": msg`Access your media library`,
283283
"media:write": msg`Upload and manage media`,
284284
"users:read": msg`Read user accounts`,
285-
"cache:purge": msg`Clear the CMS object cache and Workers Cache`,
286285
"network:request": msg`Make network requests`,
287286
"network:request:unrestricted": msg`Make network requests to any host (unrestricted)`,
288287
// Legacy aliases (still emitted by older installed manifests)

packages/admin/tests/lib/marketplace.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,6 @@ describe("CAPABILITY_LABELS", () => {
312312
"media:read",
313313
"media:write",
314314
"users:read",
315-
"cache:purge",
316315
"network:request",
317316
"network:request:unrestricted",
318317
// Legacy aliases

packages/blocks/src/builders.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,6 @@ function button(
246246
style?: "primary" | "danger" | "secondary";
247247
value?: unknown;
248248
confirm?: ConfirmDialog;
249-
disabled?: boolean;
250-
title?: string;
251249
},
252250
): ButtonElement {
253251
return {
@@ -257,8 +255,6 @@ function button(
257255
...(opts?.style !== undefined && { style: opts.style }),
258256
...(opts?.value !== undefined && { value: opts.value }),
259257
...(opts?.confirm !== undefined && { confirm: opts.confirm }),
260-
...(opts?.disabled !== undefined && { disabled: opts.disabled }),
261-
...(opts?.title !== undefined && { title: opts.title }),
262258
};
263259
}
264260

packages/blocks/src/elements/button.tsx

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Button, Dialog, DialogRoot, Tooltip, TooltipProvider } from "@cloudflare/kumo";
1+
import { Button, Dialog, DialogRoot } from "@cloudflare/kumo";
22
import { useCallback, useState } from "react";
33

44
import type { BlockInteraction, ButtonElement } from "../types.js";
@@ -11,26 +11,22 @@ export function ButtonElementComponent({
1111
onAction: (interaction: BlockInteraction) => void;
1212
}) {
1313
const [confirmOpen, setConfirmOpen] = useState(false);
14-
const isDisabled = element.disabled === true;
15-
const hasTitle = element.title !== undefined && element.title.length > 0;
1614

1715
const fireAction = useCallback(() => {
18-
if (isDisabled) return;
1916
onAction({
2017
type: "block_action",
2118
action_id: element.action_id,
2219
value: element.value,
2320
});
24-
}, [onAction, isDisabled, element.action_id, element.value]);
21+
}, [onAction, element.action_id, element.value]);
2522

2623
const handleClick = useCallback(() => {
27-
if (isDisabled) return;
2824
if (element.confirm) {
2925
setConfirmOpen(true);
3026
} else {
3127
fireAction();
3228
}
33-
}, [isDisabled, element.confirm, fireAction]);
29+
}, [element.confirm, fireAction]);
3430

3531
const handleConfirm = useCallback(() => {
3632
setConfirmOpen(false);
@@ -44,35 +40,12 @@ export function ButtonElementComponent({
4440
? ("destructive" as const)
4541
: ("secondary" as const);
4642

47-
// Don't pass `title` into Kumo Button when disabled — that attaches the
48-
// tooltip trigger to the disabled <button>, which never receives hover.
49-
// Instead wrap a span (always hoverable) as the Tooltip trigger.
50-
const button = (
51-
<Button variant={variant} onClick={handleClick} disabled={isDisabled}>
52-
{element.label}
53-
</Button>
54-
);
55-
56-
const withTooltip = hasTitle ? (
57-
<TooltipProvider>
58-
<Tooltip
59-
content={element.title}
60-
delay={200}
61-
closeDelay={0}
62-
// Span keeps pointer events when the inner button is disabled.
63-
render={<span className="inline-flex max-w-max" />}
64-
>
65-
{button}
66-
</Tooltip>
67-
</TooltipProvider>
68-
) : (
69-
button
70-
);
71-
7243
return (
7344
<>
74-
{withTooltip}
75-
{element.confirm && !isDisabled && (
45+
<Button variant={variant} onClick={handleClick}>
46+
{element.label}
47+
</Button>
48+
{element.confirm && (
7649
<DialogRoot open={confirmOpen} onOpenChange={setConfirmOpen}>
7750
<Dialog>
7851
<h3 className="text-lg font-semibold text-kumo-default">{element.confirm.title}</h3>

packages/blocks/src/types.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ export interface ButtonElement {
1717
style?: "primary" | "danger" | "secondary";
1818
value?: unknown;
1919
confirm?: ConfirmDialog;
20-
/** When true, the button does not fire actions. */
21-
disabled?: boolean;
22-
/** Native tooltip shown on hover (e.g. why the button is disabled). */
23-
title?: string;
2420
}
2521

2622
export interface TextInputElement {

packages/blocks/src/validation.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,18 +176,6 @@ function validateElement(value: unknown, path: string, errors: ValidationError[]
176176
message: `Field 'style' must be one of: ${[...BUTTON_STYLES].join(", ")}`,
177177
});
178178
}
179-
if (value.disabled !== undefined && typeof value.disabled !== "boolean") {
180-
errors.push({
181-
path: `${path}.disabled`,
182-
message: "Field 'disabled' must be a boolean",
183-
});
184-
}
185-
if (value.title !== undefined && typeof value.title !== "string") {
186-
errors.push({
187-
path: `${path}.title`,
188-
message: "Field 'title' must be a string",
189-
});
190-
}
191179
if (value.confirm !== undefined) {
192180
validateConfirmDialog(value.confirm, `${path}.confirm`, errors);
193181
}

packages/blocks/tests/renderer.test.tsx

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,11 @@ const CollapsibleContext = React.createContext<{
1515
}>({});
1616

1717
vi.mock("@cloudflare/kumo", () => ({
18-
Button: ({ children, onClick, variant, type, disabled, title }: any) => (
19-
<button
20-
onClick={onClick}
21-
data-variant={variant}
22-
type={type || "button"}
23-
disabled={disabled}
24-
title={typeof title === "string" ? title : undefined}
25-
>
18+
Button: ({ children, onClick, variant, type }: any) => (
19+
<button onClick={onClick} data-variant={variant} type={type || "button"}>
2620
{children}
2721
</button>
2822
),
29-
TooltipProvider: ({ children }: any) => <>{children}</>,
30-
Tooltip: ({ content, children, render: triggerRender }: any) => {
31-
const trigger = triggerRender ?? <span />;
32-
return (
33-
<div data-testid="tooltip" data-content={content}>
34-
{React.isValidElement(trigger)
35-
? React.cloneElement(trigger as React.ReactElement<any>, {}, children)
36-
: children}
37-
</div>
38-
);
39-
},
4023
Badge: ({ children }: any) => <span data-testid="badge">{children}</span>,
4124
Input: ({ label, value, defaultValue, onChange, onBlur, placeholder, type, min, max }: any) => (
4225
<div>
@@ -410,34 +393,6 @@ describe("BlockRenderer", () => {
410393
expect(screen.getByText("Cancel")).toBeTruthy();
411394
});
412395

413-
it("disabled button with title wraps a tooltip and does not fire actions", () => {
414-
const onAction = vi.fn();
415-
renderBlocks(
416-
[
417-
{
418-
type: "actions",
419-
elements: [
420-
{
421-
type: "button",
422-
action_id: "clear",
423-
label: "Clear object cache",
424-
disabled: true,
425-
title: "Object Cache Not Configured",
426-
},
427-
],
428-
},
429-
],
430-
onAction,
431-
);
432-
const btn = screen.getByText("Clear object cache") as HTMLButtonElement;
433-
expect(btn.disabled).toBe(true);
434-
expect(screen.getByTestId("tooltip").getAttribute("data-content")).toBe(
435-
"Object Cache Not Configured",
436-
);
437-
fireEvent.click(btn);
438-
expect(onAction).not.toHaveBeenCalled();
439-
});
440-
441396
it("stats block renders stat cards with values", () => {
442397
renderBlocks([
443398
{

packages/cloudflare/src/sandbox/bridge.ts

Lines changed: 1 addition & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,7 @@
1010
import type { D1Database } from "@cloudflare/workers-types";
1111
import { WorkerEntrypoint } from "cloudflare:workers";
1212
import type { SandboxEmailSendCallback } from "emdash";
13-
import {
14-
handleObjectCachePurge,
15-
handleObjectCacheStatus,
16-
handleWorkersCachePurge,
17-
handleWorkersCacheStatus,
18-
ulid,
19-
PluginStorageRepository,
20-
} from "emdash";
13+
import { ulid, PluginStorageRepository } from "emdash";
2114
import { Kysely } from "kysely";
2215
import { D1Dialect } from "kysely-d1";
2316

@@ -1180,70 +1173,6 @@ export class PluginBridge extends WorkerEntrypoint<PluginBridgeEnv, PluginBridge
11801173
await emailSendCallback(message, pluginId);
11811174
}
11821175

1183-
// =========================================================================
1184-
// Object cache — capability-gated (cache:purge)
1185-
// =========================================================================
1186-
1187-
async getObjectCacheStatus(): Promise<{ configured: boolean }> {
1188-
const { capabilities } = this.ctx.props;
1189-
if (!capabilities.includes("cache:purge")) {
1190-
throw new Error("Missing capability: cache:purge");
1191-
}
1192-
const result = await handleObjectCacheStatus();
1193-
if (!result.success) {
1194-
throw new Error(result.error.message);
1195-
}
1196-
return result.data;
1197-
}
1198-
1199-
async purgeObjectCache(options?: {
1200-
namespaces?: string[];
1201-
}): Promise<{ configured: boolean; active: boolean; purged: string[] }> {
1202-
const { capabilities } = this.ctx.props;
1203-
if (!capabilities.includes("cache:purge")) {
1204-
throw new Error("Missing capability: cache:purge");
1205-
}
1206-
const db = new Kysely<unknown>({
1207-
dialect: new D1Dialect({ database: this.env.DB }),
1208-
});
1209-
// eslint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- D1 dialect matches core handler db shape
1210-
const result = await handleObjectCachePurge(db as never, {
1211-
namespaces: options?.namespaces,
1212-
});
1213-
if (!result.success) {
1214-
throw new Error(result.error.message);
1215-
}
1216-
return result.data;
1217-
}
1218-
1219-
async getWorkersCacheStatus(): Promise<{ configured: boolean }> {
1220-
const { capabilities } = this.ctx.props;
1221-
if (!capabilities.includes("cache:purge")) {
1222-
throw new Error("Missing capability: cache:purge");
1223-
}
1224-
const result = await handleWorkersCacheStatus();
1225-
if (!result.success) {
1226-
throw new Error(result.error.message);
1227-
}
1228-
return result.data;
1229-
}
1230-
1231-
async purgeWorkersCache(options?: {
1232-
pathPrefixes?: string[];
1233-
}): Promise<{ configured: boolean; purged: boolean; pathPrefixes?: string[] }> {
1234-
const { capabilities } = this.ctx.props;
1235-
if (!capabilities.includes("cache:purge")) {
1236-
throw new Error("Missing capability: cache:purge");
1237-
}
1238-
const result = await handleWorkersCachePurge({
1239-
pathPrefixes: options?.pathPrefixes,
1240-
});
1241-
if (!result.success) {
1242-
throw new Error(result.error.message);
1243-
}
1244-
return result.data;
1245-
}
1246-
12471176
// =========================================================================
12481177
// Logging
12491178
// =========================================================================

packages/cloudflare/src/sandbox/types.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,15 +212,6 @@ export interface PluginBridgeBinding {
212212
): Promise<{ status: number; headers: Record<string, string>; text: string }>;
213213
// Email
214214
emailSend(message: { to: string; subject: string; text: string; html?: string }): Promise<void>;
215-
// Cache purge (gated on cache:purge)
216-
getObjectCacheStatus(): Promise<{ configured: boolean }>;
217-
purgeObjectCache(options?: {
218-
namespaces?: string[];
219-
}): Promise<{ configured: boolean; active: boolean; purged: string[] }>;
220-
getWorkersCacheStatus(): Promise<{ configured: boolean }>;
221-
purgeWorkersCache(options?: {
222-
pathPrefixes?: string[];
223-
}): Promise<{ configured: boolean; purged: boolean; pathPrefixes?: string[] }>;
224215
// Logging
225216
log(level: "debug" | "info" | "warn" | "error", msg: string, data?: unknown): void;
226217
}

0 commit comments

Comments
 (0)