Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
11 changes: 11 additions & 0 deletions .changeset/astro-scheduled-cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@emdash-cms/cloudflare": minor

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The frontmatter only covers @emdash-cms/cloudflare, but this PR also changes packages/core/src/astro/integration/vite-config.ts — code shipped through the emdash package's ./astro export. The new optimizer entries, shim aliases, and Astro 7.3 logger handling are observable to emdash consumers and need their own release note. Because the packages are in the same fixed group in .changeset/config.json, the cloudflare entry will bump emdash without describing the core integration changes. Add a separate changeset for emdash describing the Astro 7.3 dev/build compatibility fixes, or restructure the prose so both packages are accurately represented.

For example, add a second file:

Suggested change
"@emdash-cms/cloudflare": minor
---
"emdash": patch
---
Updates the EmDash Astro integration's Vite optimizer configuration for Astro 7.3 compatibility, including pre-bundled entries for new Astro internal modules and the `use-sync-external-store` shim aliases.

---

Fixes scheduled publishing cache invalidation so newly published content is served after the scheduled task completes.

This release requires stable Astro 6.0.0 or later. Astro 6 prereleases no longer satisfy the package's peer dependency range.

#### What should I do?

Upgrade Astro to version 6.0.0 or later before updating `@emdash-cms/cloudflare` if the site still uses an Astro 6 prerelease.
5 changes: 5 additions & 0 deletions .changeset/cold-cloudflare-dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"emdash": patch
---

Fixes Cloudflare-backed Astro development servers hanging during initial setup or the first admin request.
1 change: 1 addition & 0 deletions apps/release-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"devDependencies": {
"@cloudflare/vite-plugin": "catalog:",
"@cloudflare/vitest-plugin": "catalog:",
"@cloudflare/workers-types": "catalog:",
"@playwright/test": "^1.61.1",
"@tailwindcss/vite": "^4.3.3",
"@testing-library/react": "^16.3.0",
Expand Down
218 changes: 209 additions & 9 deletions apps/release-service/worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable */
// Generated by Wrangler by running `wrangler types` (hash: f28c32364b602e6eee79e70db345bfa6)
// Runtime types generated with workerd@1.20260815.1 2026-05-14 nodejs_compat
// Runtime types generated with workerd@1.20260820.1 2026-05-14 nodejs_compat
interface __BaseEnv_Env {
PUBLICATION_STAGING: R2Bucket;
PROVENANCE_STORE: R2Bucket;
Expand Down Expand Up @@ -3465,14 +3465,20 @@ interface ContainerSnapshotRestoreParams {
interface ContainerSnapshotOptions {
name?: string;
}
interface ContainerStartupOptions {
type ContainerStartupOptions = {
entrypoint?: string[];
enableInternet: boolean;
env?: Record<string, string>;
instance?: "lite" | "standard-1" | "standard-2" | "standard-3" | "standard-4" | ContainerStartResources;
labels?: Record<string, string>;
directorySnapshots?: ContainerDirectorySnapshotRestoreParams[];
} & ({
image: string;
containerSnapshot?: never;
} | {
image?: never;
containerSnapshot?: ContainerSnapshotRestoreParams;
}
});
interface ContainerStartResources {
vcpu: number;
memoryMib: number;
Expand Down Expand Up @@ -11009,10 +11015,29 @@ type BrowserRunLinksOptions = BrowserRunCommonOptions & {
/** When true, exclude links pointing to external domains. @default false */
excludeExternalLinks?: boolean;
};
type BrowserRunSnapshotFormat = 'content' | 'screenshot' | 'markdown' | 'accessibilityTree';
type BrowserRunSnapshotOptions = BrowserRunCommonOptions & {
/** Which representations of the page to return. At least two distinct formats
* are required; request a single format from its dedicated action instead.
* @default ["content","screenshot"]
*/
formats?: BrowserRunSnapshotFormat[];
/** @see https://pptr.dev/api/puppeteer.screenshotoptions */
screenshotOptions?: Omit<BrowserRunPuppeteerScreenshotOptions, 'encoding'>;
};
/** Options for the `accessibilityTree` quick action. */
type BrowserRunAccessibilityTreeOptions = BrowserRunCommonOptions & {
/** When true, prune nodes that carry no semantic meaning, such as generic
* containers. Defaults to true, or to false when `root` is set so that the
* requested subtree is returned as-is.
*/
interestingOnly?: boolean;
/** CSS selector limiting the tree to the matching element's subtree.
* A selector that matches nothing yields `accessibilityTree: null` with
* HTTP 200; a malformed selector is an error.
*/
root?: string;
};
interface BrowserRunJsonBaseOptions {
/** Custom AI models to try in order. Max 3. Falls back to next on error. */
custom_ai?: Array<{
Expand Down Expand Up @@ -11045,6 +11070,58 @@ type BrowserRunResponseMeta = {
/** Page title */
title: string;
};
/**
* A node in the page's accessibility tree, as exposed to assistive technology.
* `role` is the only field always present; the rest are populated when the
* underlying element defines them.
* @see https://pptr.dev/api/puppeteer.serializedaxnode
*/
interface BrowserRunSerializedAXNode {
/** The ARIA role, e.g. `"button"`, `"heading"`, `"RootWebArea"`. */
role: string;
/** The `aria-autocomplete` value. */
autocomplete?: string;
/** Checked state of a checkbox, radio, or menu item. */
checked?: boolean | 'mixed';
/** Accessible description, typically from `aria-describedby` or `title`. */
description?: string;
disabled?: boolean;
expanded?: boolean;
/** Whether the element currently holds keyboard focus. */
focused?: boolean;
/** The kind of popup the element triggers, e.g. `"menu"`, `"dialog"`. */
haspopup?: string;
/** The `aria-invalid` value. */
invalid?: string;
/** Keyboard shortcuts bound to the element, from `aria-keyshortcuts`. */
keyshortcuts?: string;
/** Hierarchical level, e.g. the heading level of an `<h2>`. */
level?: number;
/** Whether the element is a modal dialog. */
modal?: boolean;
/** Whether a text input accepts multiple lines. */
multiline?: boolean;
/** Whether more than one option can be selected. */
multiselectable?: boolean;
/** Accessible name, e.g. a button's label or an image's alt text. */
name?: string;
orientation?: string;
/** Pressed state of a toggle button. */
pressed?: boolean | 'mixed';
readonly?: boolean;
required?: boolean;
/** Author-supplied role description, from `aria-roledescription`. */
roledescription?: string;
selected?: boolean;
/** Current value of an input or range element. */
value?: string | number;
valuemax?: number;
valuemin?: number;
/** Human-readable form of `value`, from `aria-valuetext`. */
valuetext?: string;
/** Child nodes. Absent for leaf nodes. */
children?: BrowserRunSerializedAXNode[];
}
/** Success response for `content` action. */
type BrowserRunContentSuccessResponse = {
success: true;
Expand Down Expand Up @@ -11088,14 +11165,31 @@ type BrowserRunScrapeSuccessResponse = {
}>;
}>;
};
/** Success response for `snapshot` action. */
/** Success response for `snapshot` action. Each field is present only when the
* corresponding entry was requested in `formats`.
*/
type BrowserRunSnapshotSuccessResponse = {
success: true;
result: {
/** HTML content of the page. */
content: string;
content?: string;
/** Base64-encoded screenshot image. */
screenshot: string;
screenshot?: string;
/** Markdown content. Prefixed with YAML frontmatter (e.g. `title`) when the
* page provides that metadata.
*/
markdown?: string;
/** Root of the page's accessibility tree. */
accessibilityTree?: BrowserRunSerializedAXNode;
};
meta: BrowserRunResponseMeta;
};
/** Success response for `accessibilityTree` action. */
type BrowserRunAccessibilityTreeSuccessResponse = {
success: true;
result: {
/** Root of the accessibility tree, or `null` when `root` matched no element. */
accessibilityTree: BrowserRunSerializedAXNode | null;
};
meta: BrowserRunResponseMeta;
};
Expand Down Expand Up @@ -11219,9 +11313,10 @@ declare abstract class BrowserRun {
*/
quickAction(action: 'links', options: BrowserRunLinksOptions): Promise<Response>;
/**
* Get both the HTML content and a base64-encoded screenshot of a web page.
* Get several representations of a web page in one request.
* @param action - Must be `'snapshot'`.
* @param options - Snapshot options including screenshot settings (encoding is always base64).
* @param options - Snapshot options including the `formats` to return and
* screenshot settings (encoding is always base64).
* @returns A `Response` containing one of:
*
* **Success (HTTP 200):**
Expand Down Expand Up @@ -11269,6 +11364,26 @@ declare abstract class BrowserRun {
* - `X-Browser-Ms-Used`: Browser time consumed in milliseconds (set when status < 500)
*/
quickAction(action: 'markdown', options: BrowserRunMarkdownOptions): Promise<Response>;
/**
* Get the accessibility tree of a web page.
* @param action - Must be `'accessibilityTree'`.
* @param options - Options to scope the tree to a subtree and to control
* whether semantically uninteresting nodes are pruned.
* @returns A `Response` containing one of:
*
* **Success (HTTP 200):**
* - `BrowserRunAccessibilityTreeSuccessResponse` JSON with `Content-Type: application/json`
* - `result.accessibilityTree` is `null` when `root` matched no element
*
* **Error:**
* - `BrowserRunErrorResponse` JSON with appropriate HTTP status code (400, 422, 429, 500, 503)
* - HTTP 422 for a malformed `root` selector
* - HTTP 500 with code `2017` or `2018` when the tree could not be built
*
* **Headers:**
* - `X-Browser-Ms-Used`: Browser time consumed in milliseconds (set when status < 500)
*/
quickAction(action: 'accessibilityTree', options: BrowserRunAccessibilityTreeOptions): Promise<Response>;
}
/**
* In addition to the properties you can set in the RequestInit dict
Expand Down Expand Up @@ -12600,6 +12715,27 @@ type ImageInfoResponse = {
width: number;
height: number;
};
/**
* Parameters for rasterizing text into an image.
*/
type TextRasterize = {
/** The text content to render */
content: string;
/** rasterization options for the text **/
options: TextOptions;
};
type TextOptions = {
/** Font configuration */
font: {
/** URL to a font file in TrueType (.ttf), OpenType (.otf), WOFF (.woff), or WOFF2 (.woff2) format */
url: string;
};
/** Font size in points (pt) */
size?: number;
/** Text color in CSS format: hex (#RRGGBB or #RRGGBBAA), rgb(r,g,b), rgba(r,g,b,a), or named colors */
color?: string;
};
type ImageSource = ReadableStream<Uint8Array> | TextRasterize;
type ImageTransform = {
width?: number;
height?: number;
Expand Down Expand Up @@ -12689,18 +12825,50 @@ interface ImageUploadOptions {
requireSignedURLs?: boolean;
metadata?: Record<string, unknown>;
creator?: string;
/**
* If 'base64', the input data will be decoded from base64 before processing
*/
encoding?: 'base64';
}
interface ImageUpdateOptions {
requireSignedURLs?: boolean;
metadata?: Record<string, unknown>;
creator?: string;
}
type ImageMetadataFilterOperators = {
eq?: string | number | boolean;
in?: string[] | number[];
gt?: number;
gte?: number;
lt?: number;
lte?: number;
};
type ImageMetadataFilterValue = string | number | boolean | ImageMetadataFilterOperators;
interface ImageListFilter {
metadata?: Record<string, ImageMetadataFilterValue>;
}
interface ImageListOptions {
limit?: number;
cursor?: string;
sortOrder?: 'asc' | 'desc';
creator?: string;
filter?: ImageListFilter;
}
interface ImageSignedUrlOptions {
variant: string;
expiresIn?: number;
keyName?: string;
}
interface ImageDirectUploadOptions {
id?: string;
requireSignedURLs?: boolean;
metadata?: Record<string, unknown>;
creator?: string;
expiresIn?: number;
}
interface ImageDirectUploadResult {
id: string;
uploadURL: string;
}
interface ImageList {
images: ImageMetadata[];
Expand All @@ -12718,6 +12886,13 @@ interface ImageHandle {
* @returns ReadableStream of image bytes, or null if not found
*/
bytes(): Promise<ReadableStream<Uint8Array> | null>;
/**
* Generate a signed delivery URL for this hosted image.
* @param options Signing configuration
* @returns A signed image delivery URL
* @throws {@link ImagesError} if signing fails
*/
signedUrl(options: ImageSignedUrlOptions): Promise<string>;
/**
* Update hosted image metadata
* @param options Properties to update
Expand Down Expand Up @@ -12753,6 +12928,14 @@ interface HostedImagesBinding {
* @throws {@link ImagesError} if list fails
*/
list(options?: ImageListOptions): Promise<ImageList>;
/**
* Create a Direct Creator Upload link, letting an end user upload an
* image straight to Cloudflare without exposing an API token
* @param options Upload link configuration
* @returns The new image ID and the upload URL to hand to the end user
* @throws {@link ImagesError} if creation fails
*/
createDirectUpload(options?: ImageDirectUploadOptions): Promise<ImageDirectUploadResult>;
}
interface ImagesBinding {
/**
Expand All @@ -12767,6 +12950,13 @@ interface ImagesBinding {
* @returns A transform handle
*/
input(stream: ReadableStream<Uint8Array>, options?: ImageInputOptions): ImageTransformer;
/**
* Begin applying a series of transformations to text
* @param content string to be rendered
* @param options font, optional color and size to use in rendering text
* @returns A transform handle
*/
text(content: string, options: TextOptions): ImageTransformer;
/**
* Access hosted images CRUD operations
*/
Expand Down Expand Up @@ -12796,11 +12986,15 @@ interface ImageTransformer {
type ImageTransformationOutputOptions = {
encoding?: 'base64';
};
type ImageTransformationResponseOptions = {
headers?: HeadersInit;
};
interface ImageTransformationResult {
/**
* The image as a response, ready to store in cache or return to users
* @param options Options that apply to the returned response, e.g. additional headers
*/
response(): Response;
response(options?: ImageTransformationResponseOptions): Response;
/**
* The content type of the returned image
*/
Expand Down Expand Up @@ -14804,6 +14998,9 @@ type WorkflowBatchDeleteResult = {
type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number;
type WorkflowRetentionDuration = WorkflowSleepDuration;
/** Geographic regions supported when creating a Workflow instance.
* Location hints are best-effort placement preferences. */
type WorkflowInstanceLocationHint = 'wnam' | 'enam' | 'sam' | 'weur' | 'eeur' | 'apac' | 'apac-ne' | 'apac-se' | 'oc' | 'afr' | 'me';
interface WorkflowInstanceCreateOptions<PARAMS = unknown> {
/**
* An id for your Workflow instance. Must be unique within the Workflow.
Expand All @@ -14821,6 +15018,9 @@ interface WorkflowInstanceCreateOptions<PARAMS = unknown> {
successRetention?: WorkflowRetentionDuration;
errorRetention?: WorkflowRetentionDuration;
};
/** A best-effort geographic placement preference for the Workflow instance.
* See `WorkflowInstanceLocationHint` for supported regions. */
locationHint?: WorkflowInstanceLocationHint;
}
type InstanceStatus = {
status: 'queued' // means that instance is waiting to be started (see concurrency limits)
Expand Down
1 change: 1 addition & 0 deletions apps/release-verifier/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"devDependencies": {
"@cloudflare/vite-plugin": "catalog:",
"@cloudflare/vitest-plugin": "catalog:",
"@cloudflare/workers-types": "catalog:",
"@types/node": "catalog:",
"modern-tar": "^0.7.6",
"typescript": "catalog:",
Expand Down
Loading
Loading