@@ -2,6 +2,7 @@ import { createClerkClient, verifyToken } from "@clerk/backend";
22import { describe , expect , it } from "@effect/vitest" ;
33import { vi } from "vite-plus/test" ;
44import * as Context from "effect/Context" ;
5+ import * as NodeCrypto from "@effect/platform-node/NodeCrypto" ;
56import * as Duration from "effect/Duration" ;
67import * as Effect from "effect/Effect" ;
78import * as Fiber from "effect/Fiber" ;
@@ -14,11 +15,21 @@ import * as Tracer from "effect/Tracer";
1415import * as HttpRouter from "effect/unstable/http/HttpRouter" ;
1516import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest" ;
1617import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse" ;
18+ import * as HttpServer from "effect/unstable/http/HttpServer" ;
19+ import * as HttpApiTest from "effect/unstable/httpapi/HttpApiTest" ;
20+ import * as HttpApi from "effect/unstable/httpapi/HttpApi" ;
1721import { EnvironmentId } from "@t3tools/contracts" ;
18- import { RelayEnvironmentAuth } from "@t3tools/contracts/relay" ;
22+ import {
23+ RelayApi ,
24+ RelayClientAuth ,
25+ RelayClientPrincipal ,
26+ RelayEnvironmentAuth ,
27+ type RelayClientDeviceRecord ,
28+ } from "@t3tools/contracts/relay" ;
1929
2030import {
2131 RELAY_REQUEST_DEADLINE_MS ,
32+ clientApi ,
2233 relayCors ,
2334 relayDocsRedirectRoute ,
2435 relayEnvironmentAuthLayer ,
@@ -35,6 +46,9 @@ import * as RelayDb from "../db.ts";
3546import * as EnvironmentCredentials from "../environments/EnvironmentCredentials.ts" ;
3647import * as EnvironmentLinks from "../environments/EnvironmentLinks.ts" ;
3748import * as ManagedEndpointProvider from "../environments/ManagedEndpointProvider.ts" ;
49+ import * as EnvironmentLinker from "../environments/EnvironmentLinker.ts" ;
50+ import * as RelayTokens from "../auth/RelayTokens.ts" ;
51+ import * as Devices from "../agentActivity/Devices.ts" ;
3852
3953vi . mock ( "@clerk/backend" , ( ) => ( {
4054 createClerkClient : vi . fn ( ) ,
@@ -60,6 +74,79 @@ const relaySettings: RelayConfiguration.RelayConfiguration["Service"] = {
6074 managedEndpointNamespace : undefined ,
6175} ;
6276
77+ describe ( "device listing compatibility" , ( ) => {
78+ it . effect ( "keeps v1 iOS-only while v2 returns every platform for the same account" , ( ) => {
79+ const iphone : RelayClientDeviceRecord = {
80+ deviceId : "iphone" ,
81+ label : "iPhone" ,
82+ platform : "ios" ,
83+ iosMajorVersion : 18 ,
84+ appVersion : "1.0.0" ,
85+ notifications : {
86+ enabled : true ,
87+ notifyOnApproval : true ,
88+ notifyOnInput : true ,
89+ notifyOnCompletion : true ,
90+ notifyOnFailure : true ,
91+ } ,
92+ liveActivities : { enabled : true } ,
93+ updatedAt : "2026-09-09T00:00:00.000Z" ,
94+ } ;
95+ const android : RelayClientDeviceRecord = {
96+ ...iphone ,
97+ deviceId : "android" ,
98+ label : "Android" ,
99+ platform : "android" ,
100+ iosMajorVersion : null ,
101+ androidApiLevel : 36 ,
102+ } ;
103+ const handlers = clientApi . pipe (
104+ HttpRouter . provideRequest (
105+ Layer . mergeAll (
106+ Layer . mock ( EnvironmentCredentials . EnvironmentCredentials , { } ) ,
107+ Layer . mock ( EnvironmentLinks . EnvironmentLinks , { } ) ,
108+ Layer . mock ( ManagedEndpointProvider . ManagedEndpointProvider , { } ) ,
109+ Layer . mock ( RelayDb . RelayTransactions , { } ) ,
110+ ) ,
111+ ) ,
112+ Layer . provide (
113+ Layer . mergeAll (
114+ Layer . succeed ( RelayConfiguration . RelayConfiguration , relaySettings ) ,
115+ NodeCrypto . layer ,
116+ Layer . mock ( RelayTokens . RelayTokens , { resolveDpopAccessTokenScopes : ( ) => null } ) ,
117+ Layer . mock ( EnvironmentLinker . EnvironmentLinker , { } ) ,
118+ Layer . mock ( EnvironmentLinks . EnvironmentLinks , { } ) ,
119+ Layer . mock ( ManagedEndpointProvider . ManagedEndpointProvider , { } ) ,
120+ Layer . mock ( Devices . Devices , {
121+ listForUser : ( { userId } ) => {
122+ expect ( userId ) . toBe ( "user-1" ) ;
123+ return Effect . succeed ( [ iphone , android ] ) ;
124+ } ,
125+ } ) ,
126+ ) ,
127+ ) ,
128+ Layer . provideMerge (
129+ Layer . succeed ( RelayClientAuth , {
130+ clientBearer : ( effect ) =>
131+ Effect . provideService ( effect , RelayClientPrincipal , {
132+ userId : "user-1" ,
133+ token : "test-token" ,
134+ } ) ,
135+ } ) ,
136+ ) ,
137+ ) ;
138+ return Effect . gen ( function * ( ) {
139+ const client = yield * HttpApiTest . groups (
140+ HttpApi . make ( "RelayApi" ) . add ( RelayApi . groups . client ) ,
141+ [ "client" ] ,
142+ ) ;
143+ const request = { headers : { authorization : "Bearer test-token" } } ;
144+ expect ( yield * client . client . listDevices ( request ) ) . toEqual ( { devices : [ iphone ] } ) ;
145+ expect ( yield * client . client . listDevicesV2 ( request ) ) . toEqual ( { devices : [ iphone , android ] } ) ;
146+ } ) . pipe ( Effect . provide ( Layer . mergeAll ( handlers , HttpServer . layerServices ) ) , Effect . scoped ) ;
147+ } ) ;
148+ } ) ;
149+
63150describe ( "relay client authentication" , ( ) => {
64151 it . effect ( "preserves the existing Clerk session JWT path" , ( ) =>
65152 Effect . gen ( function * ( ) {
0 commit comments