1+ import { z } from "zod" ;
12import { Log } from "../../shared" ;
23import { RangeStoredValueMeta , Storage } from "../../storage" ;
34import { InvalidRange , NoSuchKey } from "./errors" ;
45import {
5- R2HTTPMetadata ,
66 R2Object ,
77 R2ObjectBody ,
88 R2ObjectMetadata ,
99 createVersion ,
1010} from "./r2Object" ;
11- import { Validator } from "./validator" ;
12-
13- // For more information, refer to https://datatracker.ietf.org/doc/html/rfc7232
14- export interface R2Conditional {
15- // Performs the operation if the object’s etag matches the given string.
16- etagMatches ?: string ;
17- // Performs the operation if the object’s etag does not match the given string.
18- etagDoesNotMatch ?: string ;
19- // Performs the operation if the object was uploaded before the given date.
20- uploadedBefore ?: number ;
21- // Performs the operation if the object was uploaded after the given date.
22- uploadedAfter ?: number ;
23- }
24-
25- export interface R2Range {
26- offset ?: number ;
27- length ?: number ;
28- suffix ?: number ;
29- }
30-
31- export interface R2GetOptions {
32- // Specifies that the object should only be returned given satisfaction of
33- // certain conditions in the R2Conditional. Refer to R2Conditional above.
34- onlyIf ?: R2Conditional ;
35- // Specifies that only a specific length (from an optional offset) or suffix
36- // of bytes from the object should be returned. Refer to
37- // https://developers.cloudflare.com/r2/runtime-apis/#ranged-reads.
38- range ?: R2Range ;
39- }
40-
41- export interface R2PutOptions {
42- // Various HTTP headers associated with the object. Refer to
43- // https://developers.cloudflare.com/r2/runtime-apis/#http-metadata.
44- httpMetadata : R2HTTPMetadata ;
45- // A map of custom, user-defined metadata that will be stored with the object.
46- customMetadata : Record < string , string > ;
47- // A md5 hash to use to check the recieved object’s integrity.
48- md5 ?: string ;
49- }
50-
51- export type R2ListOptionsInclude = ( "httpMetadata" | "customMetadata" ) [ ] ;
11+ import {
12+ R2GetRequestSchema ,
13+ R2ListRequestSchema ,
14+ R2PutRequestSchema ,
15+ } from "./schemas" ;
16+ import { MAX_LIST_KEYS , Validator } from "./validator" ;
5217
53- export interface R2ListOptions {
54- // The number of results to return. Defaults to 1000, with a maximum of 1000.
55- limit ?: number ;
56- // The prefix to match keys against. Keys will only be returned if they start with given prefix.
57- prefix ?: string ;
58- // An opaque token that indicates where to continue listing objects from.
59- // A cursor can be retrieved from a previous list operation.
60- cursor ?: string ;
61- // The character to use when grouping keys.
62- delimiter ?: string ;
63- // Can include httpFields and/or customFields. If included, items returned by
64- // the list will include the specified metadata. Note that there is a limit on the
65- // total amount of data that a single list operation can return.
66- // If you request data, you may recieve fewer than limit results in your response
67- // to accomodate metadata.
68- // Use the truncated property to determine if the list request has more data to be returned.
69- include ?: R2ListOptionsInclude ;
70- }
18+ export type OmitRequest < T > = Omit < T , "method" | "object" > ;
19+ export type R2GetOptions = OmitRequest < z . infer < typeof R2GetRequestSchema > > ;
20+ export type R2PutOptions = OmitRequest < z . infer < typeof R2PutRequestSchema > > ;
21+ export type R2ListOptions = OmitRequest < z . infer < typeof R2ListRequestSchema > > ;
7122
7223export interface R2Objects {
7324 // An array of objects matching the list request.
7425 objects : R2Object [ ] ;
75- // If true, indicates there are more results to be retrieved for the current list request.
26+ // If true, indicates there are more results to be retrieved for the current
27+ // list request.
7628 truncated : boolean ;
77- // A token that can be passed to future list calls to resume listing from that point.
29+ // A token that can be passed to future list calls to resume listing from that
30+ // point.
7831 // Only present if truncated is true.
7932 cursor ?: string ;
80- // If a delimiter has been specified, contains all prefixes between the specified
81- // prefix and the next occurence of the delimiter.
82- // For example, if no prefix is provided and the delimiter is ‘/’, foo/bar/baz
83- // would return foo as a delimited prefix. If foo/ was passed as a prefix
84- // with the same structure and delimiter, foo/bar would be returned as a delimited prefix.
33+ // If a delimiter has been specified, contains all prefixes between the
34+ // specified prefix and the next occurrence of the delimiter. For example, if
35+ // no prefix is provided and the delimiter is "/", " foo/bar/baz" would return
36+ // " foo" as a delimited prefix. If " foo/" was passed as a prefix with the same
37+ // structure and delimiter, " foo/bar" would be returned as a delimited prefix.
8538 delimitedPrefixes : string [ ] ;
8639}
8740
88- const MAX_LIST_KEYS = 1_000 ;
89- // https://developers.cloudflare.com/r2/platform/limits/ (5GB - 5MB)
90-
9141const validate = new Validator ( ) ;
9242
9343export class R2Gateway {
@@ -110,10 +60,7 @@ export class R2Gateway {
11060 options : R2GetOptions = { }
11161 ) : Promise < R2ObjectBody | R2Object > {
11262 const { range = { } , onlyIf } = options ;
113- validate
114- . key ( key )
115- . getOptions ( options )
116- . condition ( await this . head ( key ) , onlyIf ) ;
63+ validate . key ( key ) . condition ( await this . head ( key ) , onlyIf ) ;
11764
11865 let stored : RangeStoredValueMeta < R2ObjectMetadata > | undefined ;
11966
@@ -140,22 +87,18 @@ export class R2Gateway {
14087 ) : Promise < R2Object > {
14188 const { customMetadata, md5, httpMetadata } = options ;
14289
143- const hash = validate
144- . key ( key )
145- . putOptions ( options )
146- . size ( value )
147- . md5 ( value , md5 ) ;
90+ const hash = validate . key ( key ) . size ( value ) . md5 ( value , md5 ) ;
14891
14992 // build metadata
15093 const metadata : R2ObjectMetadata = {
15194 key,
15295 size : value . byteLength ,
153- etag : hash ,
96+ etag : hash . toString ( "hex" ) ,
15497 version : createVersion ( ) ,
15598 httpEtag : `"${ hash } "` ,
15699 uploaded : Date . now ( ) ,
157- httpMetadata,
158- customMetadata,
100+ httpMetadata : httpMetadata ?? { } ,
101+ customMetadata : customMetadata ?? { } ,
159102 } ;
160103
161104 // Store value with expiration and metadata
@@ -173,9 +116,7 @@ export class R2Gateway {
173116 }
174117
175118 async list ( listOptions : R2ListOptions = { } ) : Promise < R2Objects > {
176- const delimitedPrefixes = new Set < string > ( ) ;
177-
178- validate . listOptions ( listOptions ) ;
119+ validate . limit ( listOptions . limit ) ;
179120
180121 const { prefix = "" , include = [ ] , cursor = "" } = listOptions ;
181122 let { delimiter, limit = MAX_LIST_KEYS } = listOptions ;
@@ -190,8 +131,7 @@ export class R2Gateway {
190131 cursor,
191132 delimiter,
192133 } ) ;
193- // add delimited prefixes should they exist
194- for ( const dP of res . delimitedPrefixes ?? [ ] ) delimitedPrefixes . add ( dP ) ;
134+ const delimitedPrefixes = new Set ( res . delimitedPrefixes ?? [ ] ) ;
195135
196136 const objects = res . keys
197137 // grab metadata
0 commit comments