1import { ProviderSetupError, type CodexAuthCallbackInput } from "@t3tools/contracts";
2import { receiveCodexAuthCallback } from "@t3tools/shared/codexAuthCallback";
3import { codexAuthorizationRequest } from "@t3tools/shared/codexAuthHandoff";
4import { providerAuthReturnUrl } from "@t3tools/shared/providerAuthReturnUrl";
5import { isLoopbackHost } from "@t3tools/shared/preview";
6import * as Deferred from "effect/Deferred";
7import * as Effect from "effect/Effect";
8import * as Fiber from "effect/Fiber";
9import * as Stream from "effect/Stream";
10
11/** A connected local environment receives the callback; only the remote environment owns tokens. */
12export function subscribeCodexAuthCallback(input: CodexAuthCallbackInput) {
13 const failure = (error: unknown) =>
14 new ProviderSetupError({
15 instanceId: input.instanceId,
16 operation: "callback",
17 detail:
18 error instanceof Error ? error.message : "Could not receive sign-in on this computer.",
19 });
20 return Stream.unwrap(
21 Effect.gen(function* () {
22 const destination = yield* Effect.try({
23 try: () => {
24 codexAuthorizationRequest(input.authorizationUrl);
25 const destination = providerAuthReturnUrl(input.returnUrl);
26 if (!destination || !isLoopbackHost(new URL(destination).hostname))
27 throw new Error("The local sign-in receiver needs a local T3 Code return address.");
28 return destination;
29 },
30 catch: failure,
31 });
32 const ready = yield* Deferred.make<void, ProviderSetupError>();
33 const runSync = Effect.runSyncWith(yield* Effect.context<never>());
34 const callback = yield* Effect.tryPromise({
35 try: (signal) =>
36 receiveCodexAuthCallback(
37 input.authorizationUrl,
38 async () => {
39 runSync(Deferred.succeed(ready, undefined));
40 return true;
41 },
42 () => destination,
43 signal,
44 ),
45 catch: failure,
46 }).pipe(
47 Effect.tapError((error) => Deferred.fail(ready, error)),
48 Effect.forkScoped,
49 );
50 return Stream.fromEffect(Deferred.await(ready)).pipe(
51 Stream.map(() => ({ phase: "ready" as const })),
52 Stream.concat(
53 Stream.fromEffect(Fiber.join(callback)).pipe(
54 Stream.map((callbackUrl) => ({ phase: "finished" as const, callbackUrl })),
55 ),
56 ),
57 );
58 }),
59 );
60}