1import * as Config from "effect/Config";
2import * as Context from "effect/Context";
3import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";
5import * as Layer from "effect/Layer";
6import * as Path from "effect/Path";
7import { SystemError } from "../RuntimeError.shared.ts";
8import * as System from "./System.ts";
9
10export class Paths extends Context.Service<
11 Paths,
12 {
13 readonly cache: (
14 ...prefix: Array<string>
15 ) => Effect.Effect<string, SystemError>;
16 readonly config: (
17 ...prefix: Array<string>
18 ) => Effect.Effect<string, SystemError>;
19 readonly data: (
20 ...prefix: Array<string>
21 ) => Effect.Effect<string, SystemError>;
22 readonly state: (
23 ...prefix: Array<string>
24 ) => Effect.Effect<string, SystemError>;
25 }
26>()("cloudflare-runtime/Paths") {}
27
28export const PathsLive = Layer.effect(
29 Paths,
30 Effect.gen(function* () {
31 const fs = yield* FileSystem.FileSystem;
32 const path = yield* Path.Path;
33 const home = yield* System.home;
34
35 const makeWith =
36 (options: { env: string; fallback: Array<string> }) =>
37 (...prefix: Array<string>) =>
38 Config.String(options.env).pipe(
39 Effect.orElseSucceed(() => path.join(home, ...options.fallback)),
40 Effect.map((root) => path.join(root, ...prefix)),
41 Effect.tap((path) =>
42 fs.makeDirectory(path, { recursive: true }).pipe(
43 Effect.catchTag(
44 "PlatformError",
45 (error) =>
46 new SystemError({
47 subtag: "Paths",
48 message: `Failed to create directory "${path}".`,
49 cause: error,
50 detail: { path, env: options.env },
51 }),
52 ),
53 ),
54 ),
55 );
56
57 return Paths.of({
58 cache: makeWith({ env: "XDG_CACHE_HOME", fallback: [".cache"] }),
59 config: makeWith({ env: "XDG_CONFIG_HOME", fallback: [".config"] }),
60 data: makeWith({ env: "XDG_DATA_HOME", fallback: [".local", "share"] }),
61 state: makeWith({ env: "XDG_STATE_HOME", fallback: [".local", "state"] }),
62 });
63 }),
64);
65
66export const data = (...prefix: Array<string>) =>
67 Paths.use((Paths) => Paths.data(...prefix));
68
69export const config = (...prefix: Array<string>) =>
70 Paths.use((Paths) => Paths.config(...prefix));
71
72export const cache = (...prefix: Array<string>) =>
73 Paths.use((Paths) => Paths.cache(...prefix));
74
75export const state = (...prefix: Array<string>) =>
76 Paths.use((Paths) => Paths.state(...prefix));