@@ -47,25 +47,50 @@ import { getServiceConfigByServiceId } from '@/lib/oauth/utils'
4747
4848export type OciRequestMethod = 'GET' | 'HEAD' | 'DELETE' | 'POST' | 'PUT' | 'PATCH'
4949
50- export type OciRetryPolicy =
51- | { readonly kind : 'safe' ; readonly maxAttempts : number }
52- | { readonly kind : 'tokenized' ; readonly maxAttempts : number ; readonly retryToken : string }
50+ export interface OciSafeRetryPolicy {
51+ readonly kind : 'safe'
52+ readonly maxAttempts : number
53+ }
54+
55+ export interface OciTokenizedRetryPolicy {
56+ readonly kind : 'tokenized'
57+ readonly maxAttempts : number
58+ readonly retryToken : string
59+ }
5360
54- export interface OciRequest {
61+ export type OciRetryPolicy = OciSafeRetryPolicy | OciTokenizedRetryPolicy
62+
63+ interface OciRequestBase {
5564 readonly endpoint : OciPreparedEndpoint
56- readonly method : OciRequestMethod
5765 readonly encodedPath : string
5866 readonly queryPairs ?: readonly ( readonly [ string , string ] ) [ ]
5967 readonly headers ?: Readonly < Record < string , string > >
60- readonly body ?: Uint8Array
61- readonly contentType ?: string
6268 readonly timeoutMs : number
6369 readonly maxResponseBytes : number
6470 readonly responseHeaders ?: readonly string [ ]
65- readonly retry ?: OciRetryPolicy
6671 readonly signal ?: AbortSignal
6772}
6873
74+ export type OciRequest =
75+ | ( OciRequestBase & {
76+ readonly method : 'GET' | 'HEAD'
77+ readonly body ?: never
78+ readonly contentType ?: never
79+ readonly retry ?: OciRetryPolicy
80+ } )
81+ | ( OciRequestBase & {
82+ readonly method : 'DELETE'
83+ readonly body ?: never
84+ readonly contentType ?: never
85+ readonly retry ?: OciTokenizedRetryPolicy
86+ } )
87+ | ( OciRequestBase & {
88+ readonly method : 'POST' | 'PUT' | 'PATCH'
89+ readonly body : Uint8Array
90+ readonly contentType : string
91+ readonly retry ?: OciTokenizedRetryPolicy
92+ } )
93+
6994declare const authenticatedOciResponseBrand : unique symbol
7095
7196export interface OciAuthenticatedResponse {
@@ -121,6 +146,7 @@ interface SignedOciRequest {
121146}
122147
123148const BODY_METHODS : ReadonlySet < OciRequestMethod > = new Set ( [ 'POST' , 'PUT' , 'PATCH' ] )
149+ const SAFE_RETRY_METHODS : ReadonlySet < OciRequestMethod > = new Set ( [ 'GET' , 'HEAD' ] )
124150const REQUEST_METHODS : ReadonlySet < string > = new Set ( [
125151 'GET' ,
126152 'HEAD' ,
@@ -470,6 +496,7 @@ function validateRequest(request: OciRequest): {
470496 typeof request . retry !== 'object' ||
471497 Array . isArray ( request . retry ) ||
472498 ( request . retry . kind !== 'safe' && request . retry . kind !== 'tokenized' ) ||
499+ ( request . retry . kind === 'safe' && ! SAFE_RETRY_METHODS . has ( request . method ) ) ||
473500 Object . keys ( request . retry ) . some (
474501 ( key ) =>
475502 key !== 'kind' &&
@@ -715,17 +742,19 @@ export async function createOciClient(params: CreateOciClientParams): Promise<Oc
715742 }
716743
717744 let materialPromise : Promise < OciCredentialMaterial > | undefined
718- let lastSigningTime = 0
745+ let credentialMaterial : OciCredentialMaterial | undefined
719746 const preparedEndpoints = new WeakSet < object > ( )
720747 const endpointPolicies = new WeakMap < object , OciEndpointPolicy > ( )
721748 const responseSnapshots = new WeakMap < object , BoundResponseSnapshot > ( )
722749
723- const getMaterial = ( ) => {
750+ const getMaterial = async ( ) => {
724751 materialPromise ??= loadCredentialMaterial ( {
725752 credentialId : params . credentialId ,
726753 workspaceId : params . workspaceId ,
727754 } )
728- return materialPromise
755+ const material = await materialPromise
756+ credentialMaterial = material
757+ return material
729758 }
730759 const assertPolicyOwner = ( policy : OciEndpointPolicy ) => {
731760 if ( policy . serviceId !== params . serviceId ) throw new OciClientError ( 'invalid_endpoint' )
@@ -734,12 +763,6 @@ export async function createOciClient(params: CreateOciClientParams): Promise<Oc
734763 const material = await getMaterial ( )
735764 return resolveEffectiveOciRegion ( material . region , params . region )
736765 }
737- const nextSigningDate = ( ) => {
738- const now = Math . max ( Date . now ( ) , lastSigningTime + 1000 )
739- lastSigningTime = now
740- return new Date ( now )
741- }
742-
743766 const client : OciClient = {
744767 async prepareStaticEndpoint ( policy ) {
745768 assertPolicyOwner ( policy )
@@ -781,11 +804,12 @@ export async function createOciClient(params: CreateOciClientParams): Promise<Oc
781804 }
782805 const endpointPolicy = endpointPolicies . get ( request . endpoint )
783806 if ( ! endpointPolicy ) throw new OciClientError ( 'invalid_endpoint' )
807+ const material = credentialMaterial
808+ if ( ! material ) throw new OciClientError ( 'invalid_endpoint' )
784809 const validated = validateRequest ( request )
785810 const url = buildRequestUrl ( request . endpoint , request . encodedPath , validated . queryPairs )
786811 const deadline = createDeadline ( request . timeoutMs , request . signal )
787812 try {
788- const material = await getMaterial ( )
789813 for ( let attempt = 1 ; attempt <= validated . attempts ; attempt += 1 ) {
790814 if ( deadline . signal . aborted ) {
791815 throw new OciClientError ( deadline . expired ( ) ? 'deadline_exceeded' : 'aborted' )
@@ -802,7 +826,7 @@ export async function createOciClient(params: CreateOciClientParams): Promise<Oc
802826 } ,
803827 body : validated . body ,
804828 contentType : request . contentType ,
805- signingDate : nextSigningDate ( ) ,
829+ signingDate : new Date ( ) ,
806830 } )
807831
808832 let response : SecureFetchResponse
0 commit comments