@@ -3,14 +3,14 @@ import {
33 DeferredPromise ,
44 DELETE ,
55 GET ,
6- POST ,
76 HttpError ,
7+ KeyValueEntry ,
88 KeyValueStorage ,
99 maybeApply ,
1010 MiniflareDurableObject ,
11+ POST ,
1112 PUT ,
1213 RouteHandler ,
13- KeyValueEntry ,
1414} from "miniflare:shared" ;
1515import { KVHeaders , KVLimits , KVParams } from "./constants" ;
1616import {
@@ -22,6 +22,7 @@ import {
2222 validatePutOptions ,
2323} from "./validator.worker" ;
2424
25+ const MAX_BULK_LENGTH = 100 * 1024 * 1024 ;
2526interface KVParams {
2627 key : string ;
2728}
@@ -75,10 +76,14 @@ function secondsToMillis(seconds: number): number {
7576 return seconds * 1000 ;
7677}
7778
78- async function processKeyValue ( obj : KeyValueEntry < unknown > | null , type : string = "text" , withMetadata : boolean = false ) {
79+ async function processKeyValue (
80+ obj : KeyValueEntry < unknown > | null ,
81+ type : string = "text" ,
82+ withMetadata : boolean = false
83+ ) {
7984 const decoder = new TextDecoder ( ) ;
8085 let r = "" ;
81- if ( obj ?. value ) {
86+ if ( obj ?. value ) {
8287 for await ( const chunk of obj ?. value ) {
8388 r += decoder . decode ( chunk , { stream : true } ) ;
8489 }
@@ -89,16 +94,22 @@ async function processKeyValue(obj: KeyValueEntry<unknown> | null, type: string
8994 try {
9095 val = obj ?. value == null ? null : type === "json" ? JSON . parse ( r ) : r ;
9196 } catch ( err : any ) {
92- throw new HttpError ( 400 , "At least of of the requested keys corresponds to a non-JSON value" ) ;
97+ throw new HttpError (
98+ 400 ,
99+ "At least of of the requested keys corresponds to a non-JSON value"
100+ ) ;
93101 }
94102 if ( val == null ) {
95103 return null ;
96104 }
97105 if ( withMetadata ) {
98- return { value : val , metadata : obj ?. metadata ? JSON . stringify ( obj ?. metadata ) : null } ;
106+ return {
107+ value : val ,
108+ metadata : obj ?. metadata ? JSON . stringify ( obj ?. metadata ) : null ,
109+ } ;
99110 }
100111 return val ;
101- }
112+ }
102113
103114export class KVNamespaceObject extends MiniflareDurableObject {
104115 #storage?: KeyValueStorage ;
@@ -110,13 +121,7 @@ export class KVNamespaceObject extends MiniflareDurableObject {
110121 @GET ( "/:key" )
111122 @POST ( "/bulk/get" )
112123 get : RouteHandler < KVParams > = async ( req , params , url ) => {
113- // Decode URL parameters
114- const key = decodeKey ( params , url . searchParams ) ;
115- const cacheTtlParam = url . searchParams . get ( KVParams . CACHE_TTL ) ;
116- const cacheTtl =
117- cacheTtlParam === null ? undefined : parseInt ( cacheTtlParam ) ;
118- if ( req . body != null ) { // get bulk
119- // get bulk
124+ if ( req . method === "POST" && req . body != null ) {
120125 let r = "" ;
121126 const decoder = new TextDecoder ( ) ;
122127 for await ( const chunk of req . body ) {
@@ -125,15 +130,33 @@ export class KVNamespaceObject extends MiniflareDurableObject {
125130 r += decoder . decode ( ) ;
126131 const parsedBody = JSON . parse ( r ) ;
127132 const keys : string [ ] = parsedBody . keys ;
128- const obj : { [ key : string ] : any } = { } ;
129- for ( const key of keys ) {
133+ const type = parsedBody ?. type ;
134+ if ( type && type !== "text" && type !== "json" ) {
135+ return new Response ( "" , { status : 400 } ) ;
136+ }
137+ const obj : { [ key : string ] : any } = { } ;
138+ if ( keys . length > 100 ) {
139+ return new Response ( "" , { status : 400 } ) ;
140+ }
141+ for ( const key of keys ) {
130142 validateGetOptions ( key , { cacheTtl : parsedBody ?. cacheTtl } ) ;
131143 const entry = await this . storage . get ( key ) ;
132- obj [ key ] = await processKeyValue ( entry , parsedBody ?. type , parsedBody ?. withMetadata ) ;
144+ const value = await processKeyValue (
145+ entry ,
146+ parsedBody ?. type ,
147+ parsedBody ?. withMetadata
148+ ) ;
149+ obj [ key ] = value ;
133150 }
151+
134152 return new Response ( JSON . stringify ( obj ) ) ;
135153 }
136154
155+ // Decode URL parameters
156+ const key = decodeKey ( params , url . searchParams ) ;
157+ const cacheTtlParam = url . searchParams . get ( KVParams . CACHE_TTL ) ;
158+ const cacheTtl =
159+ cacheTtlParam === null ? undefined : parseInt ( cacheTtlParam ) ;
137160 // Get value from storage
138161 validateGetOptions ( key , { cacheTtl } ) ;
139162 const entry = await this . storage . get ( key ) ;
@@ -160,7 +183,6 @@ export class KVNamespaceObject extends MiniflareDurableObject {
160183 const rawExpiration = url . searchParams . get ( KVParams . EXPIRATION ) ;
161184 const rawExpirationTtl = url . searchParams . get ( KVParams . EXPIRATION_TTL ) ;
162185 const rawMetadata = req . headers . get ( KVHeaders . METADATA ) ;
163-
164186 // Validate key, expiration and metadata
165187 const now = millisToSeconds ( this . timers . now ( ) ) ;
166188 const { expiration, metadata } = validatePutOptions ( key , {
0 commit comments