-
Notifications
You must be signed in to change notification settings - Fork 968
proposal: add entrypoint-based custom export api #114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
49d2428
add entrypoint-based custom export api
maxwellpeterson 447dbb0
comment refinements based on bonk feedback
maxwellpeterson 050976b
switch to "export target" terminology
maxwellpeterson 371ff37
fix confusing target wording
maxwellpeterson 83beace
fix missing format => target rename
maxwellpeterson e74e7a8
simplify API interface types
maxwellpeterson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import type { DurableObject, WorkerEntrypoint } from "cloudflare:workers"; | ||
|
|
||
| // Gadget-facing export API: | ||
|
|
||
| /** Name under which a Gadget may export its optional export handler entrypoint. */ | ||
| export const GADGET_EXPORT_ENTRYPOINT = "ExportHandler"; | ||
|
|
||
| /** | ||
| * Describes an export format that a Gadget supports. Export formats are split | ||
| * into two modes: | ||
| * | ||
| * Browser mode: The Gadget UI is loaded in a remote browser and the rendered | ||
| * output is captured as HTML, PDF, PNG, or JPEG using the Browser Run binding. | ||
| * | ||
| * Server mode: The server-side export handler entrypoint returns the file | ||
| * content directly. File formats are unrestricted, and a Gadget may reimplement | ||
| * a browser-mode format server-side if it wants total control over the exported | ||
| * file content. | ||
| */ | ||
| export type GadgetExportFormat = { | ||
| /** | ||
| * Unique, non-empty identifier for this format. During browser rendering, the | ||
| * Workshop injects this value as `gadgetExportFormatId` before the Gadget UI | ||
| * module evaluates, allowing multiple variants of the same media type. | ||
| */ | ||
| id: string; | ||
|
|
||
| /** User-facing label for the format. */ | ||
| label: string; | ||
|
|
||
| /** Whether the Workshop captures a browser or invokes the server-side handler. */ | ||
| mode: "browser" | "server"; | ||
|
|
||
| /** | ||
| * Media type of the exported file. Browser mode supports `text/html`, | ||
| * `application/pdf`, `image/png`, and `image/jpeg`; server mode is unrestricted. | ||
| */ | ||
| contentType: string; | ||
|
|
||
| /** File extension, including the leading dot. */ | ||
| fileExtension: string; | ||
| }; | ||
|
|
||
| /** | ||
| * Optional Worker entrypoint exported by a Gadget as `ExportHandler` to | ||
| * customize file export behavior. | ||
| * | ||
| * @typeParam Gadget The Gadget Durable Object type accepted by the handler. | ||
| */ | ||
| export interface GadgetExportEntrypoint<Gadget extends DurableObject = DurableObject> | ||
| extends WorkerEntrypoint { | ||
| /** | ||
| * Lists the export formats supported by this Gadget. When this entrypoint is | ||
| * absent, the Workshop defaults to: | ||
| * - { id: "html", label: "HTML", mode: "browser", contentType: "text/html", | ||
| * fileExtension: ".html" } | ||
| * - { id: "pdf", label: "PDF", mode: "browser", contentType: "application/pdf", | ||
| * fileExtension: ".pdf" } | ||
| * | ||
| * Every `ExportHandler` entrypoint must implement this method. The Workshop | ||
| * rejects a missing `getExportFormats` method instead of applying defaults. | ||
| * Returning an empty list disables file exports. | ||
| * | ||
| * When implementing this method, the Gadget should return these browser-mode | ||
| * export formats in addition to any server-mode formats, unless it specifically | ||
| * does not want to support browser-mode HTML or PDF exports. | ||
| */ | ||
| getExportFormats(gadget: Fetcher<Gadget>): Promise<GadgetExportFormat[]>; | ||
|
|
||
| /** | ||
| * Produces the server-side format identified by an id returned from | ||
| * `getExportFormats()`. Only called for server-mode export formats. | ||
| * Browser-mode export formats are handled by the Workshop backend. | ||
| * | ||
| * The Workshop enforces 30s export duration and 100MB file size limits while | ||
| * consuming the returned stream. It derives the filename from the Gadget | ||
| * title and the format's `fileExtension`; the handler does not control the | ||
| * base filename. | ||
| */ | ||
| export(gadget: Fetcher<Gadget>, id: string): Promise<ReadableStream<Uint8Array>>; | ||
| } | ||
|
|
||
| // Export API exposed to the Workshop UI: | ||
|
|
||
| /** | ||
| * Export methods that will be added directly to `GadgetClient`, rather than | ||
| * exposed as a separate RPC capability. These methods replace the existing | ||
| * `GadgetClient.exportPdf()` method when implemented. | ||
| */ | ||
| export interface GadgetClientExportMethods { | ||
| /** | ||
| * Lists supported export formats, including default formats if the Gadget | ||
| * does not implement ExportHandler. | ||
| */ | ||
| getExportFormats(chatId?: number): Promise<GadgetExportFormat[]>; | ||
|
|
||
| /** Exports the format with the given ID. */ | ||
| export(id: string, chatId?: number): Promise<ReadableStream<Uint8Array>>; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you writing this here for now just for illustration, but planning to move these methods to
GadgetClientlater, or is the idea thatGadgetClientwill inherit this?I would prefer to keep it simple and declare them directly on
GadgetClient.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just for illustration, these methods will be declared directly on
GadgetClient