1import {
2 attachEnvironmentDescriptor,
3 createKnownEnvironment,
4 type KnownEnvironment,
5} from "@t3tools/client-runtime/environment";
6import type { ExecutionEnvironmentDescriptor } from "@t3tools/contracts";
7import * as Effect from "effect/Effect";
8
9import { PrimaryEnvironmentRequestError, retryTransientBootstrap } from "./auth";
10import { PrimaryEnvironmentHttpClient } from "./httpClient";
11
12import { runPrimaryHttp } from "../../lib/runtime";
13import { readPrimaryEnvironmentTarget } from "./target";
14
15let primaryEnvironmentDescriptor: ExecutionEnvironmentDescriptor | null = null;
16let primaryEnvironmentDescriptorPromise: Promise<ExecutionEnvironmentDescriptor> | null = null;
17
18function createPrimaryKnownEnvironment(input: {
19 readonly source: KnownEnvironment["source"];
20 readonly target: KnownEnvironment["target"];
21}): KnownEnvironment | null {
22 const descriptor = readPrimaryEnvironmentDescriptor();
23 if (!descriptor) {
24 return null;
25 }
26
27 return attachEnvironmentDescriptor(
28 createKnownEnvironment({
29 id: descriptor.environmentId,
30 label: descriptor.label,
31 source: input.source,
32 target: input.target,
33 }),
34 descriptor,
35 );
36}
37
38async function fetchPrimaryEnvironmentDescriptor(): Promise<ExecutionEnvironmentDescriptor> {
39 return retryTransientBootstrap(async () => {
40 let descriptor: ExecutionEnvironmentDescriptor;
41 try {
42 descriptor = await runPrimaryHttp(
43 PrimaryEnvironmentHttpClient.pipe(Effect.flatMap((client) => client.metadata.descriptor())),
44 );
45 } catch (error) {
46 throw PrimaryEnvironmentRequestError.fromCause({
47 operation: "fetch-environment-descriptor",
48 cause: error,
49 });
50 }
51
52 writePrimaryEnvironmentDescriptor(descriptor);
53 return descriptor;
54 });
55}
56
57function readPrimaryEnvironmentDescriptor(): ExecutionEnvironmentDescriptor | null {
58 return primaryEnvironmentDescriptor;
59}
60
61export function writePrimaryEnvironmentDescriptor(
62 descriptor: ExecutionEnvironmentDescriptor | null,
63): void {
64 primaryEnvironmentDescriptor = descriptor;
65}
66
67export function getPrimaryKnownEnvironment(): KnownEnvironment | null {
68 const primaryTarget = readPrimaryEnvironmentTarget();
69 if (!primaryTarget) {
70 return null;
71 }
72
73 return createPrimaryKnownEnvironment({
74 source: primaryTarget.source,
75 target: primaryTarget.target,
76 });
77}
78
79export function resolveInitialPrimaryEnvironmentDescriptor(): Promise<ExecutionEnvironmentDescriptor> {
80 const descriptor = readPrimaryEnvironmentDescriptor();
81 if (descriptor) {
82 return Promise.resolve(descriptor);
83 }
84
85 if (primaryEnvironmentDescriptorPromise) {
86 return primaryEnvironmentDescriptorPromise;
87 }
88
89 const nextPromise = fetchPrimaryEnvironmentDescriptor();
90 primaryEnvironmentDescriptorPromise = nextPromise;
91 return nextPromise.finally(() => {
92 if (primaryEnvironmentDescriptorPromise === nextPromise) {
93 primaryEnvironmentDescriptorPromise = null;
94 }
95 });
96}
97
98export function resetPrimaryEnvironmentDescriptorForTests(): void {
99 primaryEnvironmentDescriptorPromise = null;
100 primaryEnvironmentDescriptor = null;
101}