@@ -2,13 +2,18 @@ import Constants from "expo-constants";
22import * as Data from "effect/Data" ;
33import * as Effect from "effect/Effect" ;
44import * as Schema from "effect/Schema" ;
5- import { HttpClient , HttpClientRequest , HttpClientResponse } from "effect/unstable/http" ;
5+ import { HttpClient } from "effect/unstable/http" ;
6+ import {
7+ EnvironmentCloudEndpointUnavailableError ,
8+ EnvironmentHttpBadRequestError ,
9+ EnvironmentHttpConflictError ,
10+ EnvironmentHttpForbiddenError ,
11+ EnvironmentHttpInternalServerError ,
12+ EnvironmentHttpUnauthorizedError ,
13+ } from "@t3tools/contracts" ;
614import { stripPairingTokenFromUrl } from "@t3tools/shared/remote" ;
715import {
816 type RelayEnvironmentConnectResponse as RelayEnvironmentConnectResponseType ,
9- RelayEnvironmentConfigRequest ,
10- RelayEnvironmentLinkProof ,
11- RelayLinkProofRequest ,
1217 type RelayEnvironmentLinkResponse as RelayEnvironmentLinkResponseType ,
1318 RelayEnvironmentConnectScope ,
1419 RelayEnvironmentStatusScope ,
@@ -22,6 +27,7 @@ import {
2227import {
2328 exchangeRemoteDpopAccessToken ,
2429 fetchRemoteEnvironmentDescriptor ,
30+ makeEnvironmentHttpApiClient ,
2531 ManagedRelayClient ,
2632 ManagedRelayDpopSigner ,
2733} from "@t3tools/client-runtime" ;
@@ -33,12 +39,6 @@ const RELAY_STATUS_AND_CONNECT_SCOPES = [
3339 RelayEnvironmentStatusScope ,
3440 RelayEnvironmentConnectScope ,
3541] satisfies ReadonlyArray < RelayDpopAccessTokenScope > ;
36- const encodeRelayLinkProofRequest = Schema . encodeEffect (
37- Schema . fromJsonString ( RelayLinkProofRequest ) ,
38- ) ;
39- const encodeRelayEnvironmentConfigRequest = Schema . encodeEffect (
40- Schema . fromJsonString ( RelayEnvironmentConfigRequest ) ,
41- ) ;
4242
4343export function normalizeRelayBaseUrl ( value : string | null | undefined ) : string | null {
4444 const trimmed = value ?. trim ( ) ;
@@ -66,12 +66,31 @@ export interface CloudEnvironmentRecordWithStatus {
6666 readonly statusError : string | null ;
6767}
6868
69+ const isRelayProtectedError = Schema . is ( RelayProtectedError ) ;
70+ const isEnvironmentCloudApiError = Schema . is (
71+ Schema . Union ( [
72+ EnvironmentHttpBadRequestError ,
73+ EnvironmentHttpUnauthorizedError ,
74+ EnvironmentHttpForbiddenError ,
75+ EnvironmentHttpConflictError ,
76+ EnvironmentHttpInternalServerError ,
77+ EnvironmentCloudEndpointUnavailableError ,
78+ ] ) ,
79+ ) ;
80+
6981const MANAGED_ENDPOINT_PROVIDER_KIND =
7082 "cloudflare_tunnel" satisfies RelayManagedEndpointProviderKind ;
7183
7284function cloudEnvironmentLinkError ( message : string ) {
73- return ( cause : unknown ) =>
74- new CloudEnvironmentLinkError ( { message : withDevCause ( message , cause ) , cause } ) ;
85+ return ( cause : unknown ) => {
86+ const environmentError = findEnvironmentCloudApiError ( cause ) ;
87+ return new CloudEnvironmentLinkError ( {
88+ message : environmentError
89+ ? `${ message . replace ( / [ . : ] $ / , "" ) } : ${ environmentError . message } `
90+ : withDevCause ( message , cause ) ,
91+ cause,
92+ } ) ;
93+ } ;
7594}
7695
7796function isDevRuntime ( ) : boolean {
@@ -100,8 +119,6 @@ function withDevCause(message: string, cause: unknown): string {
100119 return detail ? `${ message } (${ detail } )` : message ;
101120}
102121
103- const decodeRelayProtectedError = Schema . decodeUnknownEffect ( RelayProtectedError ) ;
104-
105122function relayProtectedErrorMessage ( error : RelayProtectedErrorType ) : string {
106123 switch ( error . _tag ) {
107124 case "RelayAuthInvalidError" :
@@ -149,101 +166,32 @@ function decodedRelayClientError(message: string) {
149166}
150167
151168function findRelayProtectedError ( cause : unknown ) : RelayProtectedErrorType | null {
169+ if ( isRelayProtectedError ( cause ) ) {
170+ return cause ;
171+ }
152172 if ( typeof cause !== "object" || cause === null ) {
153173 return null ;
154174 }
155- if ( "_tag" in cause && String ( cause . _tag ) . startsWith ( "Relay" ) ) {
156- return cause as RelayProtectedErrorType ;
157- }
158175 return "cause" in cause ? findRelayProtectedError ( cause . cause ) : null ;
159176}
160177
178+ function findEnvironmentCloudApiError ( cause : unknown ) : { readonly message : string } | null {
179+ if ( isEnvironmentCloudApiError ( cause ) ) {
180+ return cause ;
181+ }
182+ if ( typeof cause !== "object" || cause === null ) {
183+ return null ;
184+ }
185+ return "cause" in cause ? findEnvironmentCloudApiError ( cause . cause ) : null ;
186+ }
187+
161188function requireRelayUrl ( ) : Effect . Effect < string , CloudEnvironmentLinkError > {
162189 const relayUrl = readRelayUrl ( ) ;
163190 return relayUrl
164191 ? Effect . succeed ( relayUrl )
165192 : Effect . fail ( new CloudEnvironmentLinkError ( { message : "Relay URL is not configured." } ) ) ;
166193}
167194
168- function requestFromInit ( url : string , init : RequestInit ) {
169- return HttpClientRequest . make ( ( init . method ?? "GET" ) as "GET" | "POST" | "DELETE" ) ( url , {
170- headers : init . headers as Record < string , string > | undefined ,
171- } ) . pipe (
172- typeof init . body === "string"
173- ? HttpClientRequest . bodyText (
174- init . body ,
175- ( init . headers as Record < string , string > | undefined ) ?. [ "content-type" ] ??
176- "application/json" ,
177- )
178- : ( request ) => request ,
179- ) ;
180- }
181-
182- function jsonResponse (
183- url : string ,
184- init : RequestInit ,
185- ) : Effect . Effect <
186- HttpClientResponse . HttpClientResponse ,
187- CloudEnvironmentLinkError ,
188- HttpClient . HttpClient
189- > {
190- return Effect . gen ( function * ( ) {
191- const response = yield * HttpClient . execute ( requestFromInit ( url , init ) ) . pipe (
192- Effect . mapError ( cloudEnvironmentLinkError ( `${ url } request failed.` ) ) ,
193- ) ;
194- if ( response . status < 200 || response . status >= 300 ) {
195- const relayError = yield * response . json . pipe (
196- Effect . orElseSucceed ( ( ) => null ) ,
197- Effect . flatMap ( ( json ) =>
198- json === null
199- ? Effect . succeed ( null )
200- : decodeRelayProtectedError ( json ) . pipe ( Effect . catch ( ( ) => Effect . succeed ( null ) ) ) ,
201- ) ,
202- ) ;
203- return yield * new CloudEnvironmentLinkError ( {
204- message : relayError
205- ? `${ url } failed with ${ response . status } : ${ relayProtectedErrorMessage ( relayError ) } `
206- : `${ url } failed with ${ response . status } ` ,
207- } ) ;
208- }
209- return response ;
210- } ) ;
211- }
212-
213- function jsonFetch < T > (
214- url : string ,
215- init : RequestInit ,
216- ) : Effect . Effect < T , CloudEnvironmentLinkError , HttpClient . HttpClient > {
217- return Effect . gen ( function * ( ) {
218- const response = yield * jsonResponse ( url , init ) ;
219- return yield * response . json . pipe (
220- Effect . map ( ( json ) => json as T ) ,
221- Effect . mapError ( cloudEnvironmentLinkError ( `${ url } returned invalid JSON.` ) ) ,
222- ) ;
223- } ) ;
224- }
225-
226- function jsonFetchSchema < S extends Schema . Top > ( input : {
227- readonly url : string ;
228- readonly init : RequestInit ;
229- readonly schema : S ;
230- readonly errorMessage : string ;
231- } ) : Effect . Effect <
232- S [ "Type" ] ,
233- CloudEnvironmentLinkError ,
234- S [ "DecodingServices" ] | HttpClient . HttpClient
235- > {
236- return jsonResponse ( input . url , input . init ) . pipe (
237- Effect . flatMap ( HttpClientResponse . schemaJson ( Schema . Struct ( { body : input . schema } ) ) ) ,
238- Effect . map ( ( response ) => ( response as { readonly body : S [ "Type" ] } ) . body ) ,
239- Effect . mapError ( ( cause ) =>
240- cause instanceof CloudEnvironmentLinkError
241- ? cause
242- : new CloudEnvironmentLinkError ( { message : input . errorMessage , cause } ) ,
243- ) ,
244- ) ;
245- }
246-
247195function endpointOrigin ( httpBaseUrl : string ) {
248196 const url = new URL ( httpBaseUrl ) ;
249197 return {
@@ -354,31 +302,22 @@ export function linkEnvironmentToCloud(input: {
354302 decodedRelayClientError ( `${ relayUrl } /v1/client/environment-link-challenges failed` ) ,
355303 ) ,
356304 ) ;
357- const proofRequestBody = yield * encodeRelayLinkProofRequest ( {
358- challenge : challenge . challenge ,
359- relayIssuer : relayUrl ,
360- endpoint : {
361- httpBaseUrl : input . connection . httpBaseUrl ,
362- wsBaseUrl : input . connection . wsBaseUrl ,
363- providerKind : MANAGED_ENDPOINT_PROVIDER_KIND ,
364- } ,
365- origin : endpointOrigin ( input . connection . httpBaseUrl ) ,
366- } ) . pipe (
367- Effect . mapError ( cloudEnvironmentLinkError ( "Could not encode cloud link proof request." ) ) ,
368- ) ;
369- const proof = yield * jsonFetchSchema ( {
370- url : `${ input . connection . httpBaseUrl } /api/cloud/link-proof` ,
371- schema : RelayEnvironmentLinkProof ,
372- errorMessage : "Environment returned an invalid cloud link proof." ,
373- init : {
374- method : "POST" ,
375- headers : {
376- authorization : `Bearer ${ localBearerToken } ` ,
377- "content-type" : "application/json" ,
305+ const environmentClient = yield * makeEnvironmentHttpApiClient ( input . connection . httpBaseUrl ) ;
306+ const proof = yield * environmentClient . cloud
307+ . linkProof ( {
308+ headers : { authorization : `Bearer ${ localBearerToken } ` } ,
309+ payload : {
310+ challenge : challenge . challenge ,
311+ relayIssuer : relayUrl ,
312+ endpoint : {
313+ httpBaseUrl : input . connection . httpBaseUrl ,
314+ wsBaseUrl : input . connection . wsBaseUrl ,
315+ providerKind : MANAGED_ENDPOINT_PROVIDER_KIND ,
316+ } ,
317+ origin : endpointOrigin ( input . connection . httpBaseUrl ) ,
378318 } ,
379- body : proofRequestBody ,
380- } ,
381- } ) ;
319+ } )
320+ . pipe ( Effect . mapError ( cloudEnvironmentLinkError ( "Could not obtain environment link proof." ) ) ) ;
382321 const link = yield * relayClient
383322 . linkEnvironment ( {
384323 clerkToken : input . clerkToken ,
@@ -399,24 +338,21 @@ export function linkEnvironmentToCloud(input: {
399338 link,
400339 } ) ;
401340
402- const relayConfigRequestBody = yield * encodeRelayEnvironmentConfigRequest ( {
403- relayUrl,
404- relayIssuer : link . relayIssuer ,
405- cloudUserId : link . cloudUserId ,
406- environmentCredential : link . environmentCredential ,
407- cloudMintPublicKey : link . cloudMintPublicKey ,
408- endpointRuntime : link . endpointRuntime ,
409- } ) . pipe (
410- Effect . mapError ( cloudEnvironmentLinkError ( "Could not encode cloud relay config request." ) ) ,
411- ) ;
412- yield * jsonFetch ( `${ input . connection . httpBaseUrl } /api/cloud/relay-config` , {
413- method : "POST" ,
414- headers : {
415- authorization : `Bearer ${ localBearerToken } ` ,
416- "content-type" : "application/json" ,
417- } ,
418- body : relayConfigRequestBody ,
419- } ) ;
341+ yield * environmentClient . cloud
342+ . relayConfig ( {
343+ headers : { authorization : `Bearer ${ localBearerToken } ` } ,
344+ payload : {
345+ relayUrl,
346+ relayIssuer : link . relayIssuer ,
347+ cloudUserId : link . cloudUserId ,
348+ environmentCredential : link . environmentCredential ,
349+ cloudMintPublicKey : link . cloudMintPublicKey ,
350+ endpointRuntime : link . endpointRuntime ,
351+ } ,
352+ } )
353+ . pipe (
354+ Effect . mapError ( cloudEnvironmentLinkError ( "Could not configure environment relay access." ) ) ,
355+ ) ;
420356 } ) ;
421357}
422358
0 commit comments