Skip to content

IMAGES-1177: Implement wrapped Images binding#2188

Merged
irvinebroque merged 1 commit intocloudflare:mainfrom
ns476:wrapped-images-binding
Aug 7, 2024
Merged

IMAGES-1177: Implement wrapped Images binding#2188
irvinebroque merged 1 commit intocloudflare:mainfrom
ns476:wrapped-images-binding

Conversation

@ns476
Copy link
Copy Markdown
Contributor

@ns476 ns476 commented May 29, 2024

This creates a new TS binding that allows image transformations and makes it available as an internal Cloudflare binding.

@ns476 ns476 requested review from a team as code owners May 29, 2024 16:48
@ns476 ns476 force-pushed the wrapped-images-binding branch from 06058b5 to bde005a Compare June 17, 2024 14:44
@xjewer
Copy link
Copy Markdown

xjewer commented Jun 19, 2024

lgtm

Copy link
Copy Markdown
Contributor

@IgorMinar IgorMinar left a comment

Choose a reason for hiding this comment

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

I'm excited about this addition! I have some API design feedback, please take a look...

}

interface TransformationResult {
response(): Response;
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.

Could you avoid making the result of type Response as that leaks the underlying transport layer. You already expose image as a readable stream which is perfect. Do we need to expose more?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is just a convenience that does:

new Response(this.image(), {
      headers: {
        "content-type": this.contentType(),
      },
    });

so I don't think it leaks anything, but happy to remove it if you don't think it's useful

}

interface ImagesError {
code: number;
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.

I wonder if there is a precedence for error handling in bindings. It seems that R2 and D1 as preexisting APIs take vastly different approach to this.

Could we think about which way we want to go in this area, and start consolidating the error APIs to feel consistent?

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.

at minimum this property should be readonly. For reference this is what R2Error looks like:


declare interface R2Error extends Error {
  readonly name: string;
  readonly code: number;
  readonly message: string;
  readonly action: string;
  readonly stack: any;
}

source: https://unpkg.com/browse/@cloudflare/workers-types@4.20240614.0/experimental/index.d.ts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Made it this for now:

interface ImagesError extends Error {
  readonly code: number;
  readonly message: string;
  readonly stack?: string;
}

It's fairly similar to the Vectorize error (which I cribbed from shamelessly)


interface ImagesBinding {
info(blob: Blob): Promise<InfoResponse>;
input(blob: Blob): ImageTransformer;
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.

could the input be a stream instead? It's easy to turn a blob to a stream, the opposite is problematic.

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.

my main goal here is to enable streaming transformations/use-cases by default, because enabling that ad-hoc will be hard/awkward.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've made the interfaces take streams and then buffer internally for now as it's a bit tricky to stream properly with multipart, which is what the backend uses

@IgorMinar
Copy link
Copy Markdown
Contributor

one more thing... this feature is really cool, but it will not be very useful if we don't come up with a local development story for it. Have you thought about how can one develop a worker locally with wrangler and miniflare and still use this new binding?

@ns476 ns476 force-pushed the wrapped-images-binding branch from bde005a to acca5dd Compare June 20, 2024 19:43
@ns476
Copy link
Copy Markdown
Contributor Author

ns476 commented Jun 20, 2024

Thanks for the review!

one more thing... this feature is really cool, but it will not be very useful if we don't come up with a local development story for it. Have you thought about how can one develop a worker locally with wrangler and miniflare and still use this new binding?

I think the approach taken with the AI binding will work nicely here: provide an endpoint on api.cloudflare.com with OAuth that proxies to a worker that invokes the binding.

@GregBrimble
Copy link
Copy Markdown
Contributor

Like Igor, very excited for this! I've personally needed it many many times :)

Comment on lines +47 to +61
const chunks: Uint8Array[] = [];
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();

if (value) {
chunks.push(value);
}

if (done) {
break;
}
}

return new Blob(chunks);
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.

Worth measuring but in theory it would be more efficient to avoid bouncing back and forth between C++ and JS:

Suggested change
const chunks: Uint8Array[] = [];
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (value) {
chunks.push(value);
}
if (done) {
break;
}
}
return new Blob(chunks);
return new Response(stream).blob();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Made this change, also much more obviously correct which is a plus for me!

@ns476 ns476 force-pushed the wrapped-images-binding branch from acca5dd to c84d67c Compare July 22, 2024 11:49
@irvinebroque irvinebroque requested review from anonrig and jasnell August 1, 2024 20:02
@ns476 ns476 force-pushed the wrapped-images-binding branch from c84d67c to 5d24095 Compare August 2, 2024 10:12
@ns476 ns476 requested a review from a team August 2, 2024 10:12
This creates a new TS binding that allows image transformations and
makes it available as an internal Cloudflare binding.
@ns476 ns476 force-pushed the wrapped-images-binding branch from 5d24095 to e7b29af Compare August 2, 2024 11:13
@irvinebroque irvinebroque merged commit 4bae509 into cloudflare:main Aug 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants