1+ import fs from "node:fs/promises" ;
2+ import SCRIPT_IMAGES_SERVICE from "worker:images/images" ;
3+ import SCRIPT_KV_NAMESPACE_OBJECT from "worker:kv/namespace" ;
14import { z } from "zod" ;
2- import { CoreBindings , CoreHeaders } from "../../workers" ;
5+ import { Service } from "../../runtime" ;
6+ import { SharedBindings } from "../../workers" ;
7+ import { KV_NAMESPACE_OBJECT_CLASS_NAME } from "../kv" ;
38import {
9+ getMiniflareObjectBindings ,
10+ getPersistPath ,
411 getUserBindingServiceName ,
12+ objectEntryWorker ,
13+ PersistenceSchema ,
514 Plugin ,
615 ProxyNodeBinding ,
716 remoteProxyClientWorker ,
817 RemoteProxyConnectionString ,
18+ SERVICE_LOOPBACK ,
919 WORKER_BINDING_SERVICE_LOOPBACK ,
1020} from "../shared" ;
1121
12- const IMAGES_LOCAL_FETCHER = /* javascript */ `
13- export default {
14- fetch(req, env) {
15- const request = new Request(req);
16- request.headers.set("${ CoreHeaders . CUSTOM_FETCH_SERVICE } ", "${ CoreBindings . IMAGES_SERVICE } ");
17- request.headers.set("${ CoreHeaders . ORIGINAL_URL } ", request.url);
18- return env.${ CoreBindings . SERVICE_LOOPBACK } .fetch(request)
19- }
20- }
21- ` ;
22-
2322const ImagesSchema = z . object ( {
2423 binding : z . string ( ) ,
2524 remoteProxyConnectionString : z
@@ -31,10 +30,18 @@ export const ImagesOptionsSchema = z.object({
3130 images : ImagesSchema . optional ( ) ,
3231} ) ;
3332
33+ export const ImagesSharedOptionsSchema = z . object ( {
34+ imagesPersist : PersistenceSchema ,
35+ } ) ;
36+
3437export const IMAGES_PLUGIN_NAME = "images" ;
3538
36- export const IMAGES_PLUGIN : Plugin < typeof ImagesOptionsSchema > = {
39+ export const IMAGES_PLUGIN : Plugin <
40+ typeof ImagesOptionsSchema ,
41+ typeof ImagesSharedOptionsSchema
42+ > = {
3743 options : ImagesOptionsSchema ,
44+ sharedOptions : ImagesSharedOptionsSchema ,
3845 async getBindings ( options ) {
3946 if ( ! options . images ) {
4047 return [ ] ;
@@ -69,34 +76,120 @@ export const IMAGES_PLUGIN: Plugin<typeof ImagesOptionsSchema> = {
6976 [ options . images . binding ] : new ProxyNodeBinding ( ) ,
7077 } ;
7178 } ,
72- async getServices ( { options } ) {
79+ async getServices ( {
80+ options,
81+ sharedOptions,
82+ tmpPath,
83+ defaultPersistRoot,
84+ unsafeStickyBlobs,
85+ } ) {
7386 if ( ! options . images ) {
7487 return [ ] ;
7588 }
7689
77- return [
78- {
79- name : getUserBindingServiceName (
80- IMAGES_PLUGIN_NAME ,
81- options . images . binding ,
82- options . images . remoteProxyConnectionString
83- ) ,
84- worker : options . images . remoteProxyConnectionString
85- ? remoteProxyClientWorker (
86- options . images . remoteProxyConnectionString ,
87- options . images . binding
88- )
89- : {
90- modules : [
91- {
92- name : "index.worker.js" ,
93- esModule : IMAGES_LOCAL_FETCHER ,
94- } ,
95- ] ,
96- compatibilityDate : "2025-04-01" ,
97- bindings : [ WORKER_BINDING_SERVICE_LOOPBACK ] ,
98- } ,
90+ const serviceName = getUserBindingServiceName (
91+ IMAGES_PLUGIN_NAME ,
92+ options . images . binding ,
93+ options . images . remoteProxyConnectionString
94+ ) ;
95+
96+ if ( options . images . remoteProxyConnectionString ) {
97+ return [
98+ {
99+ name : serviceName ,
100+ worker : remoteProxyClientWorker (
101+ options . images . remoteProxyConnectionString ,
102+ options . images . binding
103+ ) ,
104+ } ,
105+ ] ;
106+ }
107+
108+ const persistPath = getPersistPath (
109+ IMAGES_PLUGIN_NAME ,
110+ tmpPath ,
111+ defaultPersistRoot ,
112+ sharedOptions . imagesPersist
113+ ) ;
114+
115+ await fs . mkdir ( persistPath , { recursive : true } ) ;
116+
117+ const storageService = {
118+ name : `${ IMAGES_PLUGIN_NAME } :storage` ,
119+ disk : { path : persistPath , writable : true } ,
120+ } satisfies Service ;
121+
122+ const objectService = {
123+ name : `${ IMAGES_PLUGIN_NAME } :ns` ,
124+ worker : {
125+ compatibilityDate : "2023-07-24" ,
126+ compatibilityFlags : [ "nodejs_compat" , "experimental" ] ,
127+ modules : [
128+ {
129+ name : "namespace.worker.js" ,
130+ esModule : SCRIPT_KV_NAMESPACE_OBJECT ( ) ,
131+ } ,
132+ ] ,
133+ durableObjectNamespaces : [
134+ {
135+ className : KV_NAMESPACE_OBJECT_CLASS_NAME ,
136+ uniqueKey : `miniflare-images-${ KV_NAMESPACE_OBJECT_CLASS_NAME } ` ,
137+ } ,
138+ ] ,
139+ durableObjectStorage : { localDisk : storageService . name } ,
140+ bindings : [
141+ {
142+ name : SharedBindings . MAYBE_SERVICE_BLOBS ,
143+ service : { name : storageService . name } ,
144+ } ,
145+ {
146+ name : SharedBindings . MAYBE_SERVICE_LOOPBACK ,
147+ service : { name : SERVICE_LOOPBACK } ,
148+ } ,
149+ ...getMiniflareObjectBindings ( unsafeStickyBlobs ) ,
150+ ] ,
99151 } ,
100- ] ;
152+ } satisfies Service ;
153+
154+ const kvNamespaceService = {
155+ name : `${ IMAGES_PLUGIN_NAME } :ns:data` ,
156+ worker : objectEntryWorker (
157+ {
158+ serviceName : objectService . name ,
159+ className : KV_NAMESPACE_OBJECT_CLASS_NAME ,
160+ } ,
161+ "images-data"
162+ ) ,
163+ } satisfies Service ;
164+
165+ const imagesService = {
166+ name : serviceName ,
167+ worker : {
168+ compatibilityDate : "2025-04-01" ,
169+ modules : [
170+ {
171+ name : "images.worker.js" ,
172+ esModule : SCRIPT_IMAGES_SERVICE ( ) ,
173+ } ,
174+ ] ,
175+ bindings : [
176+ {
177+ name : "IMAGES_STORE" ,
178+ kvNamespace : { name : kvNamespaceService . name } ,
179+ } ,
180+ WORKER_BINDING_SERVICE_LOOPBACK ,
181+ ] ,
182+ } ,
183+ } satisfies Service ;
184+
185+ return [ storageService , objectService , kvNamespaceService , imagesService ] ;
186+ } ,
187+ getPersistPath ( { imagesPersist } , tmpPath ) {
188+ return getPersistPath (
189+ IMAGES_PLUGIN_NAME ,
190+ tmpPath ,
191+ undefined ,
192+ imagesPersist
193+ ) ;
101194 } ,
102195} ;
0 commit comments