22// Image data is stored as KV values, metadata as KV metadata
33// Transforms and info operations are handled via HTTP loopback to Node.js Sharp
44
5- import { WorkerEntrypoint } from "cloudflare:workers" ;
5+ import { RpcTarget , WorkerEntrypoint } from "cloudflare:workers" ;
66import { CoreBindings , CoreHeaders } from "../core/constants" ;
77
88interface Env {
@@ -29,23 +29,70 @@ async function base64DecodeStream(
2929 return base64DecodeArrayBuffer ( buffer ) ;
3030}
3131
32- export default class ImagesService extends WorkerEntrypoint < Env > {
33- async details ( imageId : string ) : Promise < ImageMetadata | null > {
34- const result = await this . env . IMAGES_STORE . getWithMetadata < ImageMetadata > (
35- imageId ,
32+ class ImageHandleImpl extends RpcTarget {
33+ readonly #imageId: string ;
34+ readonly #store: KVNamespace ;
35+
36+ constructor ( imageId : string , store : KVNamespace ) {
37+ super ( ) ;
38+ this . #imageId = imageId ;
39+ this . #store = store ;
40+ }
41+
42+ async details ( ) : Promise < ImageMetadata | null > {
43+ const result = await this . #store. getWithMetadata < ImageMetadata > (
44+ this . #imageId,
3645 "arrayBuffer"
3746 ) ;
3847 return result . metadata ?? null ;
3948 }
4049
41- async image ( imageId : string ) : Promise < ReadableStream < Uint8Array > | null > {
42- const data = await this . env . IMAGES_STORE . get ( imageId , "arrayBuffer" ) ;
50+ async bytes ( ) : Promise < ReadableStream < Uint8Array > | null > {
51+ const data = await this . #store . get ( this . # imageId, "arrayBuffer" ) ;
4352 if ( data === null ) {
4453 return null ;
4554 }
4655 return new Blob ( [ data ] ) . stream ( ) ;
4756 }
4857
58+ async update ( options : ImageUpdateOptions ) : Promise < ImageMetadata > {
59+ const existing = await this . #store. getWithMetadata < ImageMetadata > (
60+ this . #imageId,
61+ "arrayBuffer"
62+ ) ;
63+ if ( existing . value === null || existing . metadata === null ) {
64+ throw new Error ( `Image not found: ${ this . #imageId} ` ) ;
65+ }
66+
67+ const updatedMetadata : ImageMetadata = {
68+ ...existing . metadata ,
69+ requireSignedURLs :
70+ options . requireSignedURLs ?? existing . metadata . requireSignedURLs ,
71+ meta : options . metadata ?? existing . metadata . meta ,
72+ creator : options . creator ?? existing . metadata . creator ,
73+ } ;
74+
75+ await this . #store. put ( this . #imageId, existing . value , {
76+ metadata : updatedMetadata ,
77+ } ) ;
78+ return updatedMetadata ;
79+ }
80+
81+ async delete ( ) : Promise < boolean > {
82+ const existing = await this . #store. get ( this . #imageId, "arrayBuffer" ) ;
83+ if ( existing === null ) {
84+ return false ;
85+ }
86+ await this . #store. delete ( this . #imageId) ;
87+ return true ;
88+ }
89+ }
90+
91+ export default class ImagesService extends WorkerEntrypoint < Env > {
92+ image ( imageId : string ) : ImageHandleImpl {
93+ return new ImageHandleImpl ( imageId , this . env . IMAGES_STORE ) ;
94+ }
95+
4996 async upload (
5097 image : ReadableStream < Uint8Array > | ArrayBuffer ,
5198 options ?: ImageUploadOptions
@@ -80,41 +127,6 @@ export default class ImagesService extends WorkerEntrypoint<Env> {
80127 return metadata ;
81128 }
82129
83- async update (
84- imageId : string ,
85- options : ImageUpdateOptions
86- ) : Promise < ImageMetadata > {
87- const existing = await this . env . IMAGES_STORE . getWithMetadata < ImageMetadata > (
88- imageId ,
89- "arrayBuffer"
90- ) ;
91- if ( existing . value === null || existing . metadata === null ) {
92- throw new Error ( `Image not found: ${ imageId } ` ) ;
93- }
94-
95- const updatedMetadata : ImageMetadata = {
96- ...existing . metadata ,
97- requireSignedURLs :
98- options . requireSignedURLs ?? existing . metadata . requireSignedURLs ,
99- meta : options . metadata ?? existing . metadata . meta ,
100- creator : options . creator ?? existing . metadata . creator ,
101- } ;
102-
103- await this . env . IMAGES_STORE . put ( imageId , existing . value , {
104- metadata : updatedMetadata ,
105- } ) ;
106- return updatedMetadata ;
107- }
108-
109- async delete ( imageId : string ) : Promise < boolean > {
110- const existing = await this . env . IMAGES_STORE . get ( imageId , "arrayBuffer" ) ;
111- if ( existing === null ) {
112- return false ;
113- }
114- await this . env . IMAGES_STORE . delete ( imageId ) ;
115- return true ;
116- }
117-
118130 async list ( options ?: ImageListOptions ) : Promise < ImageList > {
119131 const limit = options ?. limit ?? 50 ;
120132
0 commit comments