| 1 | import { useAtomValue } from "@effect/atom-react"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import { FetchHttpClient, HttpClient, HttpClientResponse } from "effect/http"; |
| 4 | import { AsyncResult, Atom } from "effect/reactivity"; |
| 5 | |
| 6 | /** |
| 7 | * Whether `t3@<version>` is published on npm. A 404 is false; network and |
| 8 | * registry errors stay unknown. Both re-check every few minutes, so a release |
| 9 | * that lands later shows up without a reload. |
| 10 | */ |
| 11 | export const npmReleasePublishedAtom = Atom.family((version: string) => |
| 12 | Atom.make( |
| 13 | Effect.gen(function* () { |
| 14 | const response = yield* HttpClient.get( |
| 15 | `https://registry.npmjs.org/t3/${encodeURIComponent(version)}`, |
| 16 | ); |
| 17 | if (response.status === 404) return false; |
| 18 | const body = yield* HttpClientResponse.filterStatusOk(response).pipe( |
| 19 | Effect.flatMap((ok) => ok.json), |
| 20 | ); |
| 21 | return typeof body === "object" && body !== null && "version" in body |
| 22 | ? body.version === version |
| 23 | : false; |
| 24 | }).pipe( |
| 25 | // Tracing headers would make the browser send a CORS preflight, which npm rejects. |
| 26 | Effect.provideService(HttpClient.TracerPropagationEnabled, false), |
| 27 | Effect.provide(FetchHttpClient.layer), |
| 28 | ), |
| 29 | ).pipe( |
| 30 | Atom.withRefresh("5 minutes"), |
| 31 | Atom.keepAlive, |
| 32 | Atom.withLabel(`npm-release-published:${version}`), |
| 33 | ), |
| 34 | ); |
| 35 | |
| 36 | const NO_VERSION_ATOM = Atom.make(AsyncResult.success(false)).pipe( |
| 37 | Atom.withLabel("npm-release-published:none"), |
| 38 | ); |
| 39 | |
| 40 | /** True only once npm confirms the version, so callers can hide work that |
| 41 | would download it until then. */ |
| 42 | export function useNpmReleasePublished(version: string | null): boolean { |
| 43 | const result = useAtomValue(version ? npmReleasePublishedAtom(version) : NO_VERSION_ATOM); |
| 44 | return AsyncResult.getOrElse(result, () => false); |
| 45 | } |