functype-os 1.2.2 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/ConfigSource-n4VKw8Vi.d.ts +35 -0
  2. package/dist/Layered-4k7fpktc.d.ts +30 -0
  3. package/dist/ProcessEnvSource-C9EzlVbN.d.ts +17 -0
  4. package/dist/StaticSource-BjmavHQr.d.ts +21 -0
  5. package/dist/bootDiagnostics-WXjyslc9.d.ts +72 -0
  6. package/dist/config/ConfigResolver.js.map +1 -1
  7. package/dist/config/ConfigSource.d.ts +2 -0
  8. package/dist/config/ConfigSource.js +1 -0
  9. package/dist/config/Layered.d.ts +2 -0
  10. package/dist/config/Layered.js +2 -0
  11. package/dist/config/Layered.js.map +1 -0
  12. package/dist/config/ProcessEnvSource.d.ts +2 -0
  13. package/dist/config/ProcessEnvSource.js +2 -0
  14. package/dist/config/ProcessEnvSource.js.map +1 -0
  15. package/dist/config/StaticSource.d.ts +2 -0
  16. package/dist/config/StaticSource.js +2 -0
  17. package/dist/config/StaticSource.js.map +1 -0
  18. package/dist/config/bootDiagnostics.d.ts +2 -0
  19. package/dist/config/bootDiagnostics.js +2 -0
  20. package/dist/config/bootDiagnostics.js.map +1 -0
  21. package/dist/config/colors.d.ts +25 -0
  22. package/dist/config/colors.js +2 -0
  23. package/dist/config/colors.js.map +1 -0
  24. package/dist/config/consoleBootLogger.d.ts +2 -0
  25. package/dist/config/consoleBootLogger.js +2 -0
  26. package/dist/config/consoleBootLogger.js.map +1 -0
  27. package/dist/config/index.d.ts +8 -1
  28. package/dist/config/index.js +1 -1
  29. package/dist/config/mask.d.ts +2 -0
  30. package/dist/config/mask.js +2 -0
  31. package/dist/config/mask.js.map +1 -0
  32. package/dist/consoleBootLogger-DY3lxFlo.d.ts +25 -0
  33. package/dist/env/Env.js.map +1 -1
  34. package/dist/fs/Fs.js.map +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/mask-BLjNp9FP.d.ts +22 -0
  37. package/dist/platform/Platform.js.map +1 -1
  38. package/package.json +3 -3
@@ -0,0 +1,35 @@
1
+ import { Option } from "functype";
2
+
3
+ //#region src/config/ConfigSource.d.ts
4
+ /**
5
+ * Uniform contract for config lookup.
6
+ *
7
+ * Any value satisfying `get: (key: string) => Option<string>` is a `ConfigSource`.
8
+ * The same shape covers `process.env`, secrets fetched from a vault, static
9
+ * defaults, file-based config, or any user-written adapter.
10
+ *
11
+ * Synchronous lookup by design — async work (vault auth, secret fetching)
12
+ * happens at construction time. Once a `ConfigSource` exists, `get` returns
13
+ * an `Option<string>` without blocking.
14
+ *
15
+ * `name` is the source identifier shown in `bootDiagnostics` headers and in
16
+ * the composed name returned by `Layered`.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // User-written vault adapter (~12 lines, no SDK ships from functype):
21
+ * const InfisicalSource = async (opts: { token: string; env: string }): Promise<ConfigSource> => {
22
+ * const client = new InfisicalSDK().auth().accessToken(opts.token)
23
+ * const result = await client.secrets().listSecrets({ environment: opts.env })
24
+ * const map = new Map((result.secrets ?? []).map((s) => [s.secretKey, s.secretValue]))
25
+ * return { name: "infisical", get: (key) => Option(map.get(key)) }
26
+ * }
27
+ * ```
28
+ */
29
+ interface ConfigSource {
30
+ readonly name: string;
31
+ get(key: string): Option<string>;
32
+ }
33
+ //#endregion
34
+ export { ConfigSource as t };
35
+ //# sourceMappingURL=ConfigSource-n4VKw8Vi.d.ts.map
@@ -0,0 +1,30 @@
1
+ import { t as ConfigSource } from "./ConfigSource-n4VKw8Vi.js";
2
+
3
+ //#region src/config/Layered.d.ts
4
+ /**
5
+ * First-wins composition over N `ConfigSource`s.
6
+ *
7
+ * Lookup walks the sources in order and returns the first `Some(value)` it
8
+ * finds. Falls through to the next source when one returns `None`. The
9
+ * composed source's `name` reflects the resolution chain
10
+ * (e.g. `"process.env > infisical > defaults"`) so `bootDiagnostics` can echo
11
+ * it in its header.
12
+ *
13
+ * Sensitivity (which keys to mask) is declared at diagnostics time, not at
14
+ * source time — a `Layered([env, vault])` is structurally identical to any
15
+ * other `ConfigSource`.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const config = Layered([
20
+ * ProcessEnvSource(),
21
+ * await InfisicalSource({ token, env, projectId }),
22
+ * StaticSource({ ENV: "local" }, "defaults"),
23
+ * ])
24
+ * // config.name === "process.env > infisical > defaults"
25
+ * ```
26
+ */
27
+ declare const Layered: (sources: readonly ConfigSource[]) => ConfigSource;
28
+ //#endregion
29
+ export { Layered as t };
30
+ //# sourceMappingURL=Layered-4k7fpktc.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { t as ConfigSource } from "./ConfigSource-n4VKw8Vi.js";
2
+
3
+ //#region src/config/ProcessEnvSource.d.ts
4
+ /**
5
+ * Built-in `ConfigSource` backed by `process.env` via the existing `Env` wrapper.
6
+ *
7
+ * Use as the first layer of a `Layered` chain so env vars override vault and
8
+ * default values:
9
+ *
10
+ * ```ts
11
+ * const config = Layered([ProcessEnvSource(), await InfisicalSource(...), StaticSource(defaults)])
12
+ * ```
13
+ */
14
+ declare const ProcessEnvSource: () => ConfigSource;
15
+ //#endregion
16
+ export { ProcessEnvSource as t };
17
+ //# sourceMappingURL=ProcessEnvSource-C9EzlVbN.d.ts.map
@@ -0,0 +1,21 @@
1
+ import { t as ConfigSource } from "./ConfigSource-n4VKw8Vi.js";
2
+
3
+ //#region src/config/StaticSource.d.ts
4
+ /**
5
+ * Built-in `ConfigSource` backed by an in-memory map.
6
+ *
7
+ * Useful for default values, test fixtures, or any static config layer that
8
+ * doesn't need an external lookup. The `name` argument defaults to `"static"`
9
+ * but should be set when composing multiple static sources for clearer
10
+ * diagnostics output.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const defaults = StaticSource({ ENV: "local", PORT: "3000" }, "defaults")
15
+ * const config = Layered([ProcessEnvSource(), defaults])
16
+ * ```
17
+ */
18
+ declare const StaticSource: (entries: Readonly<Record<string, string>>, name?: string) => ConfigSource;
19
+ //#endregion
20
+ export { StaticSource as t };
21
+ //# sourceMappingURL=StaticSource-BjmavHQr.d.ts.map
@@ -0,0 +1,72 @@
1
+ import { t as ConfigSource } from "./ConfigSource-n4VKw8Vi.js";
2
+ import { Either, List } from "functype";
3
+ import { Logger } from "functype/logger";
4
+
5
+ //#region src/config/bootDiagnostics.d.ts
6
+ interface MissingKey {
7
+ readonly key: string;
8
+ readonly required: boolean;
9
+ }
10
+ interface BootDiagnosticsOptions {
11
+ /** The composed `ConfigSource` to inspect. */
12
+ readonly source: ConfigSource;
13
+ /** Keys that MUST be present. Missing required keys produce a `Left`. */
14
+ readonly required?: readonly string[];
15
+ /** Keys to log with masked values (via {@link maskValue}). */
16
+ readonly sensitive?: readonly string[];
17
+ /** Keys to log with raw values. */
18
+ readonly public?: readonly string[];
19
+ /**
20
+ * If provided with `vaultEnvKey`, compares the host environment string
21
+ * (e.g. `process.env.NODE_ENV`) against the value at `vaultEnvKey` in the
22
+ * source. Logs `✓` on match, an error-level message on mismatch.
23
+ */
24
+ readonly hostEnv?: string;
25
+ readonly vaultEnvKey?: string;
26
+ /**
27
+ * `"missing"` triggers `process.exit(1)` after logging when any required key
28
+ * is missing. `"never"` (default) returns `Left(missing)` and lets the caller
29
+ * decide. Set to `"missing"` in production / staging boot paths to guarantee
30
+ * fail-fast on misconfiguration.
31
+ */
32
+ readonly failOn?: "missing" | "never";
33
+ /**
34
+ * Logger to use. Defaults to {@link consoleBootLogger}. Any value satisfying
35
+ * the core `Logger` interface from `functype` — including `DirectLogger`
36
+ * from `functype-log/direct` — works without an adapter.
37
+ */
38
+ readonly logger?: Logger;
39
+ /** Service identifier shown in the diagnostics header. */
40
+ readonly serviceName?: string;
41
+ }
42
+ /**
43
+ * Log a standardized boot diagnostics block and verify required-key presence.
44
+ *
45
+ * The block always emits, regardless of outcome — operational visibility ("did
46
+ * the deploy pick up my config?") is the point. Sensitive keys are masked;
47
+ * public keys are logged verbatim. An optional host-vs-vault env-mismatch
48
+ * check catches the common deploy footgun where the host's `NODE_ENV` and the
49
+ * vault's `ENV` (or equivalent) diverge.
50
+ *
51
+ * Returns `Either<List<MissingKey>, void>` so non-fatal callers can inspect
52
+ * the result. Setting `failOn: "missing"` short-circuits to `process.exit(1)`
53
+ * for production guards.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const result = bootDiagnostics({
58
+ * serviceName: "cq-api",
59
+ * source: Layered([ProcessEnvSource(), await InfisicalSource(...)]),
60
+ * required: ["SUPABASE_URL", "SUPABASE_KEY", "OPENAI_API_KEY"],
61
+ * sensitive: ["SUPABASE_URL", "SUPABASE_KEY", "OPENAI_API_KEY"],
62
+ * public: ["ENV", "ENABLE_CHAT"],
63
+ * hostEnv: process.env.NODE_ENV ?? "local",
64
+ * vaultEnvKey: "ENV",
65
+ * failOn: process.env.NODE_ENV === "production" ? "missing" : "never",
66
+ * })
67
+ * ```
68
+ */
69
+ declare const bootDiagnostics: (opts: BootDiagnosticsOptions) => Either<List<MissingKey>, void>;
70
+ //#endregion
71
+ export { MissingKey as n, bootDiagnostics as r, BootDiagnosticsOptions as t };
72
+ //# sourceMappingURL=bootDiagnostics-WXjyslc9.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigResolver.js","names":["ConfigErrorConstructor"],"sources":["../../src/config/ConfigResolver.ts"],"sourcesContent":["import type { Either, TaskResult } from \"functype\"\nimport { Err, Left, List, Ok, Option, Right } from \"functype\"\n\nimport type { ConfigError } from \"../errors/errors\"\nimport { ConfigError as ConfigErrorConstructor } from \"../errors/errors\"\nimport { Fs } from \"../fs/Fs\"\nimport { expandPath } from \"../path/PathExpander\"\n\nconst tryExpandPath = (candidate: string): Option<string> => {\n const result = expandPath(candidate)\n return result.fold(\n () => Option<string>(undefined),\n (v) => Option(v),\n )\n}\n\nexport const ConfigResolver = {\n // Async methods — return TaskResult<T>\n\n resolve: async (options: { readonly candidates: readonly string[] }): TaskResult<Option<string>> => {\n for (const candidate of options.candidates) {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) continue\n\n const existsResult = await Fs.exists(expanded.orThrow())\n if (existsResult.isErr()) continue\n if (existsResult.value) {\n return Ok(expanded)\n }\n }\n return Ok(Option<string>(undefined))\n },\n\n resolveRequired: async (options: { readonly candidates: readonly string[] }): TaskResult<string> => {\n const result = await ConfigResolver.resolve(options)\n if (result.isErr()) return Err(result.error)\n\n const optValue = result.orThrow()\n if (optValue.isSome()) {\n return Ok(optValue.orThrow())\n }\n\n return Err(ConfigErrorConstructor(options.candidates))\n },\n\n resolveAll: async (options: { readonly candidates: readonly string[] }): TaskResult<List<string>> => {\n const found: readonly string[] = options.candidates.reduce<readonly string[]>((acc, candidate) => {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) return acc\n return [...acc, expanded.orThrow()]\n }, [])\n\n const results: string[] = []\n for (const expanded of found) {\n const existsResult = await Fs.exists(expanded)\n if (existsResult.isOk() && existsResult.value) {\n results.push(expanded)\n }\n }\n\n return Ok(List(results))\n },\n\n // Sync methods — return Either<ConfigError, T>\n\n resolveSync: (options: { readonly candidates: readonly string[] }): Option<string> => {\n for (const candidate of options.candidates) {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) continue\n if (Fs.existsSync(expanded.orThrow())) {\n return expanded\n }\n }\n return Option<string>(undefined)\n },\n\n resolveRequiredSync: (options: { readonly candidates: readonly string[] }): Either<ConfigError, string> => {\n const result = ConfigResolver.resolveSync(options)\n return result.fold(\n () => Left(ConfigErrorConstructor(options.candidates)),\n (v) => Right(v),\n )\n },\n\n resolveAllSync: (options: { readonly candidates: readonly string[] }): List<string> => {\n return List(\n options.candidates.reduce<readonly string[]>((acc, candidate) => {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) return acc\n const path = expanded.orThrow()\n return Fs.existsSync(path) ? [...acc, path] : acc\n }, []),\n )\n },\n}\n"],"mappings":"0NAQA,MAAM,EAAiB,GACN,EAAW,CACd,EAAE,SACN,EAAe,IAAA,EAAS,EAC7B,GAAM,EAAO,CAAC,CACjB,EAGW,EAAiB,CAG5B,QAAS,KAAO,IAAoF,CAClG,IAAK,IAAM,KAAa,EAAQ,WAAY,CAC1C,IAAM,EAAW,EAAc,CAAS,EACxC,GAAI,EAAS,OAAO,EAAG,SAEvB,IAAM,EAAe,MAAM,EAAG,OAAO,EAAS,QAAQ,CAAC,EACnD,MAAa,MAAM,GACnB,EAAa,MACf,OAAO,EAAG,CAAQ,CAEtB,CACA,OAAO,EAAG,EAAe,IAAA,EAAS,CAAC,CACrC,EAEA,gBAAiB,KAAO,IAA4E,CAClG,IAAM,EAAS,MAAM,EAAe,QAAQ,CAAO,EACnD,GAAI,EAAO,MAAM,EAAG,OAAO,EAAI,EAAO,KAAK,EAE3C,IAAM,EAAW,EAAO,QAAQ,EAKhC,OAJI,EAAS,OAAO,EACX,EAAG,EAAS,QAAQ,CAAC,EAGvB,EAAIA,EAAuB,EAAQ,UAAU,CAAC,CACvD,EAEA,WAAY,KAAO,IAAkF,CACnG,IAAM,EAA2B,EAAQ,WAAW,QAA2B,EAAK,IAAc,CAChG,IAAM,EAAW,EAAc,CAAS,EAExC,OADI,EAAS,OAAO,EAAU,EACvB,CAAC,GAAG,EAAK,EAAS,QAAQ,CAAC,CACpC,EAAG,CAAC,CAAC,EAEC,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAY,EAAO,CAC5B,IAAM,EAAe,MAAM,EAAG,OAAO,CAAQ,EACzC,EAAa,KAAK,GAAK,EAAa,OACtC,EAAQ,KAAK,CAAQ,CAEzB,CAEA,OAAO,EAAG,EAAK,CAAO,CAAC,CACzB,EAIA,YAAc,GAAwE,CACpF,IAAK,IAAM,KAAa,EAAQ,WAAY,CAC1C,IAAM,EAAW,EAAc,CAAS,EACpC,MAAS,OAAO,GAChB,EAAG,WAAW,EAAS,QAAQ,CAAC,EAClC,OAAO,CAEX,CACA,OAAO,EAAe,IAAA,EAAS,CACjC,EAEA,oBAAsB,GACL,EAAe,YAAY,CAC9B,EAAE,SACN,EAAKA,EAAuB,EAAQ,UAAU,CAAC,EACpD,GAAM,EAAM,CAAC,CAChB,EAGF,eAAiB,GACR,EACL,EAAQ,WAAW,QAA2B,EAAK,IAAc,CAC/D,IAAM,EAAW,EAAc,CAAS,EACxC,GAAI,EAAS,OAAO,EAAG,OAAO,EAC9B,IAAM,EAAO,EAAS,QAAQ,EAC9B,OAAO,EAAG,WAAW,CAAI,EAAI,CAAC,GAAG,EAAK,CAAI,EAAI,CAChD,EAAG,CAAC,CAAC,CACP,CAEJ"}
1
+ {"version":3,"file":"ConfigResolver.js","names":["ConfigErrorConstructor"],"sources":["../../src/config/ConfigResolver.ts"],"sourcesContent":["import type { Either, TaskResult } from \"functype\"\nimport { Err, Left, List, Ok, Option, Right } from \"functype\"\n\nimport type { ConfigError } from \"../errors/errors\"\nimport { ConfigError as ConfigErrorConstructor } from \"../errors/errors\"\nimport { Fs } from \"../fs/Fs\"\nimport { expandPath } from \"../path/PathExpander\"\n\nconst tryExpandPath = (candidate: string): Option<string> => {\n const result = expandPath(candidate)\n return result.fold(\n () => Option<string>(undefined),\n (v) => Option(v),\n )\n}\n\nexport const ConfigResolver = {\n // Async methods — return TaskResult<T>\n\n resolve: async (options: { readonly candidates: readonly string[] }): TaskResult<Option<string>> => {\n for (const candidate of options.candidates) {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) continue\n\n const existsResult = await Fs.exists(expanded.orThrow())\n if (existsResult.isErr()) continue\n if (existsResult.value) {\n return Ok(expanded)\n }\n }\n return Ok(Option<string>(undefined))\n },\n\n resolveRequired: async (options: { readonly candidates: readonly string[] }): TaskResult<string> => {\n const result = await ConfigResolver.resolve(options)\n if (result.isErr()) return Err(result.error)\n\n const optValue = result.orThrow()\n if (optValue.isSome()) {\n return Ok(optValue.orThrow())\n }\n\n return Err(ConfigErrorConstructor(options.candidates))\n },\n\n resolveAll: async (options: { readonly candidates: readonly string[] }): TaskResult<List<string>> => {\n const found: readonly string[] = options.candidates.reduce<readonly string[]>((acc, candidate) => {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) return acc\n return [...acc, expanded.orThrow()]\n }, [])\n\n const results: string[] = []\n for (const expanded of found) {\n const existsResult = await Fs.exists(expanded)\n if (existsResult.isOk() && existsResult.value) {\n results.push(expanded)\n }\n }\n\n return Ok(List(results))\n },\n\n // Sync methods — return Either<ConfigError, T>\n\n resolveSync: (options: { readonly candidates: readonly string[] }): Option<string> => {\n for (const candidate of options.candidates) {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) continue\n if (Fs.existsSync(expanded.orThrow())) {\n return expanded\n }\n }\n return Option<string>(undefined)\n },\n\n resolveRequiredSync: (options: { readonly candidates: readonly string[] }): Either<ConfigError, string> => {\n const result = ConfigResolver.resolveSync(options)\n return result.fold(\n () => Left(ConfigErrorConstructor(options.candidates)),\n (v) => Right(v),\n )\n },\n\n resolveAllSync: (options: { readonly candidates: readonly string[] }): List<string> => {\n return List(\n options.candidates.reduce<readonly string[]>((acc, candidate) => {\n const expanded = tryExpandPath(candidate)\n if (expanded.isNone()) return acc\n const path = expanded.orThrow()\n return Fs.existsSync(path) ? [...acc, path] : acc\n }, []),\n )\n },\n}\n"],"mappings":"0NAQA,MAAM,EAAiB,GACN,EAAW,CACd,CAAC,CAAC,SACN,EAAe,IAAA,EAAS,EAC7B,GAAM,EAAO,CAAC,CACjB,EAGW,EAAiB,CAG5B,QAAS,KAAO,IAAoF,CAClG,IAAK,IAAM,KAAa,EAAQ,WAAY,CAC1C,IAAM,EAAW,EAAc,CAAS,EACxC,GAAI,EAAS,OAAO,EAAG,SAEvB,IAAM,EAAe,MAAM,EAAG,OAAO,EAAS,QAAQ,CAAC,EACnD,MAAa,MAAM,GACnB,EAAa,MACf,OAAO,EAAG,CAAQ,CAEtB,CACA,OAAO,EAAG,EAAe,IAAA,EAAS,CAAC,CACrC,EAEA,gBAAiB,KAAO,IAA4E,CAClG,IAAM,EAAS,MAAM,EAAe,QAAQ,CAAO,EACnD,GAAI,EAAO,MAAM,EAAG,OAAO,EAAI,EAAO,KAAK,EAE3C,IAAM,EAAW,EAAO,QAAQ,EAKhC,OAJI,EAAS,OAAO,EACX,EAAG,EAAS,QAAQ,CAAC,EAGvB,EAAIA,EAAuB,EAAQ,UAAU,CAAC,CACvD,EAEA,WAAY,KAAO,IAAkF,CACnG,IAAM,EAA2B,EAAQ,WAAW,QAA2B,EAAK,IAAc,CAChG,IAAM,EAAW,EAAc,CAAS,EAExC,OADI,EAAS,OAAO,EAAU,EACvB,CAAC,GAAG,EAAK,EAAS,QAAQ,CAAC,CACpC,EAAG,CAAC,CAAC,EAEC,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAY,EAAO,CAC5B,IAAM,EAAe,MAAM,EAAG,OAAO,CAAQ,EACzC,EAAa,KAAK,GAAK,EAAa,OACtC,EAAQ,KAAK,CAAQ,CAEzB,CAEA,OAAO,EAAG,EAAK,CAAO,CAAC,CACzB,EAIA,YAAc,GAAwE,CACpF,IAAK,IAAM,KAAa,EAAQ,WAAY,CAC1C,IAAM,EAAW,EAAc,CAAS,EACpC,MAAS,OAAO,GAChB,EAAG,WAAW,EAAS,QAAQ,CAAC,EAClC,OAAO,CAEX,CACA,OAAO,EAAe,IAAA,EAAS,CACjC,EAEA,oBAAsB,GACL,EAAe,YAAY,CAC9B,CAAC,CAAC,SACN,EAAKA,EAAuB,EAAQ,UAAU,CAAC,EACpD,GAAM,EAAM,CAAC,CAChB,EAGF,eAAiB,GACR,EACL,EAAQ,WAAW,QAA2B,EAAK,IAAc,CAC/D,IAAM,EAAW,EAAc,CAAS,EACxC,GAAI,EAAS,OAAO,EAAG,OAAO,EAC9B,IAAM,EAAO,EAAS,QAAQ,EAC9B,OAAO,EAAG,WAAW,CAAI,EAAI,CAAC,GAAG,EAAK,CAAI,EAAI,CAChD,EAAG,CAAC,CAAC,CACP,CAEJ"}
@@ -0,0 +1,2 @@
1
+ import { t as ConfigSource } from "../ConfigSource-n4VKw8Vi.js";
2
+ export { ConfigSource };
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,2 @@
1
+ import { t as Layered } from "../Layered-4k7fpktc.js";
2
+ export { Layered };
@@ -0,0 +1,2 @@
1
+ import{Option as e}from"functype";const t=t=>({name:t.map(e=>e.name).join(` > `),get:n=>t.reduce((e,t)=>e.fold(()=>t.get(n),()=>e),e(void 0))});export{t as Layered};
2
+ //# sourceMappingURL=Layered.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Layered.js","names":["OptionCtor"],"sources":["../../src/config/Layered.ts"],"sourcesContent":["import type { Option } from \"functype\"\nimport { Option as OptionCtor } from \"functype\"\n\nimport type { ConfigSource } from \"./ConfigSource\"\n\n/**\n * First-wins composition over N `ConfigSource`s.\n *\n * Lookup walks the sources in order and returns the first `Some(value)` it\n * finds. Falls through to the next source when one returns `None`. The\n * composed source's `name` reflects the resolution chain\n * (e.g. `\"process.env > infisical > defaults\"`) so `bootDiagnostics` can echo\n * it in its header.\n *\n * Sensitivity (which keys to mask) is declared at diagnostics time, not at\n * source time — a `Layered([env, vault])` is structurally identical to any\n * other `ConfigSource`.\n *\n * @example\n * ```ts\n * const config = Layered([\n * ProcessEnvSource(),\n * await InfisicalSource({ token, env, projectId }),\n * StaticSource({ ENV: \"local\" }, \"defaults\"),\n * ])\n * // config.name === \"process.env > infisical > defaults\"\n * ```\n */\nexport const Layered = (sources: readonly ConfigSource[]): ConfigSource => ({\n name: sources.map((s) => s.name).join(\" > \"),\n get: (key) =>\n sources.reduce<Option<string>>(\n (acc, source) =>\n acc.fold(\n () => source.get(key),\n () => acc,\n ),\n OptionCtor<string>(undefined),\n ),\n})\n"],"mappings":"kCA4BA,MAAa,EAAW,IAAoD,CAC1E,KAAM,EAAQ,IAAK,GAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,EAC3C,IAAM,GACJ,EAAQ,QACL,EAAK,IACJ,EAAI,SACI,EAAO,IAAI,CAAG,MACd,CACR,EACFA,EAAmB,IAAA,EAAS,CAC9B,CACJ"}
@@ -0,0 +1,2 @@
1
+ import { t as ProcessEnvSource } from "../ProcessEnvSource-C9EzlVbN.js";
2
+ export { ProcessEnvSource };
@@ -0,0 +1,2 @@
1
+ import{Env as e}from"../env/Env.js";const t=()=>({name:`process.env`,get:t=>e(t)});export{t as ProcessEnvSource};
2
+ //# sourceMappingURL=ProcessEnvSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessEnvSource.js","names":[],"sources":["../../src/config/ProcessEnvSource.ts"],"sourcesContent":["import { Env } from \"../env/Env\"\nimport type { ConfigSource } from \"./ConfigSource\"\n\n/**\n * Built-in `ConfigSource` backed by `process.env` via the existing `Env` wrapper.\n *\n * Use as the first layer of a `Layered` chain so env vars override vault and\n * default values:\n *\n * ```ts\n * const config = Layered([ProcessEnvSource(), await InfisicalSource(...), StaticSource(defaults)])\n * ```\n */\nexport const ProcessEnvSource = (): ConfigSource => ({\n name: \"process.env\",\n get: (key) => Env(key),\n})\n"],"mappings":"oCAaA,MAAa,OAAwC,CACnD,KAAM,cACN,IAAM,GAAQ,EAAI,CAAG,CACvB"}
@@ -0,0 +1,2 @@
1
+ import { t as StaticSource } from "../StaticSource-BjmavHQr.js";
2
+ export { StaticSource };
@@ -0,0 +1,2 @@
1
+ import{Option as e}from"functype";const t=(t,n=`static`)=>({name:n,get:n=>e(t[n])});export{t as StaticSource};
2
+ //# sourceMappingURL=StaticSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaticSource.js","names":[],"sources":["../../src/config/StaticSource.ts"],"sourcesContent":["import { Option } from \"functype\"\n\nimport type { ConfigSource } from \"./ConfigSource\"\n\n/**\n * Built-in `ConfigSource` backed by an in-memory map.\n *\n * Useful for default values, test fixtures, or any static config layer that\n * doesn't need an external lookup. The `name` argument defaults to `\"static\"`\n * but should be set when composing multiple static sources for clearer\n * diagnostics output.\n *\n * @example\n * ```ts\n * const defaults = StaticSource({ ENV: \"local\", PORT: \"3000\" }, \"defaults\")\n * const config = Layered([ProcessEnvSource(), defaults])\n * ```\n */\nexport const StaticSource = (entries: Readonly<Record<string, string>>, name = \"static\"): ConfigSource => ({\n name,\n get: (key) => Option(entries[key]),\n})\n"],"mappings":"kCAkBA,MAAa,GAAgB,EAA2C,EAAO,YAA4B,CACzG,OACA,IAAM,GAAQ,EAAO,EAAQ,EAAI,CACnC"}
@@ -0,0 +1,2 @@
1
+ import { n as MissingKey, r as bootDiagnostics, t as BootDiagnosticsOptions } from "../bootDiagnostics-WXjyslc9.js";
2
+ export { BootDiagnosticsOptions, MissingKey, bootDiagnostics };
@@ -0,0 +1,2 @@
1
+ import{blue as e,green as t,magenta as n,red as r,yellow as i}from"./colors.js";import{consoleBootLogger as a}from"./consoleBootLogger.js";import{maskValue as o}from"./mask.js";import{Left as s,List as c,Right as l}from"functype";const u=u=>{let d=u.logger??a,f=u.required??[],p=u.sensitive??[],m=u.public??[];d.info(`${e(`📦`)} Boot diagnostics: ${u.serviceName??`service`}`),d.info(` source: ${u.source.name}`);let h=f.filter(e=>u.source.get(e).isEmpty);if(p.length>0){d.info(i(`🔐 Sensitive:`));for(let e of p){let t=u.source.get(e).fold(()=>r(`NOT_LOADED`),e=>o(e));d.info(` ${e.padEnd(28)} ${t}`)}}if(m.length>0){d.info(t(`⚙️ Public:`));for(let e of m){let t=u.source.get(e).fold(()=>r(`NOT_LOADED`),e=>e);d.info(` ${e.padEnd(28)} ${t}`)}}if(u.hostEnv!==void 0&&u.vaultEnvKey!==void 0){let e=u.source.get(u.vaultEnvKey),{hostEnv:r}=u,{vaultEnvKey:i}=u;e.fold(()=>{d.warn(`Vault env key '${i}' not present — skipping mismatch check`)},e=>{e===r?d.info(`${n(`🌍`)} Environment: ${r} (host) ↔ ${e} (vault) ${t(`✓`)}`):d.error(`${n(`🌍`)} Environment MISMATCH: host=${r} vault=${e}`,{hostEnv:r,vaultEnv:e})})}if(h.length===0)return d.info(t(`✅ All required keys present`)),l(void 0);d.error(r(`❌ Missing required keys (${h.length}):`));for(let e of h)d.error(` ${e}`);let g=c(h.map(e=>({key:e,required:!0})));return u.failOn===`missing`&&process.exit(1),s(g)};export{u as bootDiagnostics};
2
+ //# sourceMappingURL=bootDiagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootDiagnostics.js","names":[],"sources":["../../src/config/bootDiagnostics.ts"],"sourcesContent":["import type { Either, List as ListType } from \"functype\"\nimport { Left, List, Right } from \"functype\"\n// Logger is type-only and subpath-only in 1.3.x (rolldown chunk-splitter\n// workaround — see comment in packages/functype/src/index.ts). Restore to\n// `import type { ..., Logger } from \"functype\"` when rolldown is fixed.\nimport type { Logger } from \"functype/logger\"\n\nimport { blue, green, magenta, red, yellow } from \"./colors\"\nimport type { ConfigSource } from \"./ConfigSource\"\nimport { consoleBootLogger } from \"./consoleBootLogger\"\nimport { maskValue } from \"./mask\"\n\nexport interface MissingKey {\n readonly key: string\n readonly required: boolean\n}\n\nexport interface BootDiagnosticsOptions {\n /** The composed `ConfigSource` to inspect. */\n readonly source: ConfigSource\n /** Keys that MUST be present. Missing required keys produce a `Left`. */\n readonly required?: readonly string[]\n /** Keys to log with masked values (via {@link maskValue}). */\n readonly sensitive?: readonly string[]\n /** Keys to log with raw values. */\n readonly public?: readonly string[]\n /**\n * If provided with `vaultEnvKey`, compares the host environment string\n * (e.g. `process.env.NODE_ENV`) against the value at `vaultEnvKey` in the\n * source. Logs `✓` on match, an error-level message on mismatch.\n */\n readonly hostEnv?: string\n readonly vaultEnvKey?: string\n /**\n * `\"missing\"` triggers `process.exit(1)` after logging when any required key\n * is missing. `\"never\"` (default) returns `Left(missing)` and lets the caller\n * decide. Set to `\"missing\"` in production / staging boot paths to guarantee\n * fail-fast on misconfiguration.\n */\n readonly failOn?: \"missing\" | \"never\"\n /**\n * Logger to use. Defaults to {@link consoleBootLogger}. Any value satisfying\n * the core `Logger` interface from `functype` — including `DirectLogger`\n * from `functype-log/direct` — works without an adapter.\n */\n readonly logger?: Logger\n /** Service identifier shown in the diagnostics header. */\n readonly serviceName?: string\n}\n\n/**\n * Log a standardized boot diagnostics block and verify required-key presence.\n *\n * The block always emits, regardless of outcome — operational visibility (\"did\n * the deploy pick up my config?\") is the point. Sensitive keys are masked;\n * public keys are logged verbatim. An optional host-vs-vault env-mismatch\n * check catches the common deploy footgun where the host's `NODE_ENV` and the\n * vault's `ENV` (or equivalent) diverge.\n *\n * Returns `Either<List<MissingKey>, void>` so non-fatal callers can inspect\n * the result. Setting `failOn: \"missing\"` short-circuits to `process.exit(1)`\n * for production guards.\n *\n * @example\n * ```ts\n * const result = bootDiagnostics({\n * serviceName: \"cq-api\",\n * source: Layered([ProcessEnvSource(), await InfisicalSource(...)]),\n * required: [\"SUPABASE_URL\", \"SUPABASE_KEY\", \"OPENAI_API_KEY\"],\n * sensitive: [\"SUPABASE_URL\", \"SUPABASE_KEY\", \"OPENAI_API_KEY\"],\n * public: [\"ENV\", \"ENABLE_CHAT\"],\n * hostEnv: process.env.NODE_ENV ?? \"local\",\n * vaultEnvKey: \"ENV\",\n * failOn: process.env.NODE_ENV === \"production\" ? \"missing\" : \"never\",\n * })\n * ```\n */\nexport const bootDiagnostics = (opts: BootDiagnosticsOptions): Either<ListType<MissingKey>, void> => {\n const logger = opts.logger ?? consoleBootLogger\n const required = opts.required ?? []\n const sensitive = opts.sensitive ?? []\n const publicKeys = opts.public ?? []\n\n logger.info(`${blue(\"📦\")} Boot diagnostics: ${opts.serviceName ?? \"service\"}`)\n logger.info(` source: ${opts.source.name}`)\n\n const missing = required.filter((k) => opts.source.get(k).isEmpty)\n\n if (sensitive.length > 0) {\n logger.info(yellow(\"🔐 Sensitive:\"))\n for (const key of sensitive) {\n const value = opts.source.get(key)\n const display = value.fold(\n () => red(\"NOT_LOADED\"),\n (v) => maskValue(v),\n )\n logger.info(` ${key.padEnd(28)} ${display}`)\n }\n }\n\n if (publicKeys.length > 0) {\n logger.info(green(\"⚙️ Public:\"))\n for (const key of publicKeys) {\n const value = opts.source.get(key)\n const display = value.fold(\n () => red(\"NOT_LOADED\"),\n (v) => v,\n )\n logger.info(` ${key.padEnd(28)} ${display}`)\n }\n }\n\n if (opts.hostEnv !== undefined && opts.vaultEnvKey !== undefined) {\n const vaultEnv = opts.source.get(opts.vaultEnvKey)\n const { hostEnv } = opts\n const { vaultEnvKey } = opts\n vaultEnv.fold(\n () => {\n logger.warn(`Vault env key '${vaultEnvKey}' not present — skipping mismatch check`)\n return undefined\n },\n (v) => {\n if (v === hostEnv) {\n logger.info(`${magenta(\"🌍\")} Environment: ${hostEnv} (host) ↔ ${v} (vault) ${green(\"✓\")}`)\n } else {\n logger.error(`${magenta(\"🌍\")} Environment MISMATCH: host=${hostEnv} vault=${v}`, {\n hostEnv,\n vaultEnv: v,\n })\n }\n return undefined\n },\n )\n }\n\n if (missing.length === 0) {\n logger.info(green(\"✅ All required keys present\"))\n return Right<ListType<MissingKey>, void>(undefined)\n }\n\n logger.error(red(`❌ Missing required keys (${missing.length}):`))\n for (const key of missing) logger.error(` ${key}`)\n\n const missingList = List(missing.map((key): MissingKey => ({ key, required: true })))\n\n if (opts.failOn === \"missing\") {\n process.exit(1)\n }\n return Left<ListType<MissingKey>, void>(missingList)\n}\n"],"mappings":"sOA6EA,MAAa,EAAmB,GAAqE,CACnG,IAAM,EAAS,EAAK,QAAU,EACxB,EAAW,EAAK,UAAY,CAAC,EAC7B,EAAY,EAAK,WAAa,CAAC,EAC/B,EAAa,EAAK,QAAU,CAAC,EAEnC,EAAO,KAAK,GAAG,EAAK,IAAI,EAAE,qBAAqB,EAAK,aAAe,WAAW,EAC9E,EAAO,KAAK,cAAc,EAAK,OAAO,MAAM,EAE5C,IAAM,EAAU,EAAS,OAAQ,GAAM,EAAK,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,EAEjE,GAAI,EAAU,OAAS,EAAG,CACxB,EAAO,KAAK,EAAO,eAAe,CAAC,EACnC,IAAK,IAAM,KAAO,EAAW,CAE3B,IAAM,EADQ,EAAK,OAAO,IAAI,CACV,CAAC,CAAC,SACd,EAAI,YAAY,EACrB,GAAM,EAAU,CAAC,CACpB,EACA,EAAO,KAAK,MAAM,EAAI,OAAO,EAAE,EAAE,GAAG,GAAS,CAC/C,CACF,CAEA,GAAI,EAAW,OAAS,EAAG,CACzB,EAAO,KAAK,EAAM,aAAa,CAAC,EAChC,IAAK,IAAM,KAAO,EAAY,CAE5B,IAAM,EADQ,EAAK,OAAO,IAAI,CACV,CAAC,CAAC,SACd,EAAI,YAAY,EACrB,GAAM,CACT,EACA,EAAO,KAAK,MAAM,EAAI,OAAO,EAAE,EAAE,GAAG,GAAS,CAC/C,CACF,CAEA,GAAI,EAAK,UAAY,IAAA,IAAa,EAAK,cAAgB,IAAA,GAAW,CAChE,IAAM,EAAW,EAAK,OAAO,IAAI,EAAK,WAAW,EAC3C,CAAE,WAAY,EACd,CAAE,eAAgB,EACxB,EAAS,SACD,CACJ,EAAO,KAAK,kBAAkB,EAAY,wCAAwC,CAEpF,EACC,GAAM,CACD,IAAM,EACR,EAAO,KAAK,GAAG,EAAQ,IAAI,EAAE,gBAAgB,EAAQ,YAAY,EAAE,WAAW,EAAM,GAAG,GAAG,EAE1F,EAAO,MAAM,GAAG,EAAQ,IAAI,EAAE,8BAA8B,EAAQ,SAAS,IAAK,CAChF,UACA,SAAU,CACZ,CAAC,CAGL,CACF,CACF,CAEA,GAAI,EAAQ,SAAW,EAErB,OADA,EAAO,KAAK,EAAM,6BAA6B,CAAC,EACzC,EAAkC,IAAA,EAAS,EAGpD,EAAO,MAAM,EAAI,4BAA4B,EAAQ,OAAO,GAAG,CAAC,EAChE,IAAK,IAAM,KAAO,EAAS,EAAO,MAAM,MAAM,GAAK,EAEnD,IAAM,EAAc,EAAK,EAAQ,IAAK,IAAqB,CAAE,MAAK,SAAU,EAAK,EAAE,CAAC,EAKpF,OAHI,EAAK,SAAW,WAClB,QAAQ,KAAK,CAAC,EAET,EAAiC,CAAW,CACrD"}
@@ -0,0 +1,25 @@
1
+ //#region src/config/colors.d.ts
2
+ /**
3
+ * Internal raw-ANSI color helpers for boot diagnostics output.
4
+ *
5
+ * Respects established conventions:
6
+ * - `NO_COLOR=1` (any value) → output uncolored, regardless of TTY. See
7
+ * https://no-color.org/.
8
+ * - `FORCE_COLOR=1` (any value) → output colored, regardless of TTY. Useful
9
+ * for CI logs that pipe stdout but want the colors preserved.
10
+ * - Default: color when `process.stdout.isTTY`, plain otherwise.
11
+ *
12
+ * Zero deps. ANSI escape codes haven't changed since 1979.
13
+ *
14
+ * @internal — not re-exported from the package barrel.
15
+ */
16
+ declare const red: (s: string) => string;
17
+ declare const green: (s: string) => string;
18
+ declare const yellow: (s: string) => string;
19
+ declare const blue: (s: string) => string;
20
+ declare const magenta: (s: string) => string;
21
+ declare const cyan: (s: string) => string;
22
+ declare const gray: (s: string) => string;
23
+ //#endregion
24
+ export { blue, cyan, gray, green, magenta, red, yellow };
25
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1,2 @@
1
+ const e=()=>process.env.NO_COLOR===void 0?process.env.FORCE_COLOR===void 0?process.stdout?.isTTY??!1:!0:!1,t=t=>n=>e()?`\x1b[${t}m${n}\x1b[0m`:n,n=t(`31`),r=t(`32`),i=t(`33`),a=t(`34`),o=t(`35`),s=t(`36`),c=t(`90`);export{a as blue,s as cyan,c as gray,r as green,o as magenta,n as red,i as yellow};
2
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","names":[],"sources":["../../src/config/colors.ts"],"sourcesContent":["/**\n * Internal raw-ANSI color helpers for boot diagnostics output.\n *\n * Respects established conventions:\n * - `NO_COLOR=1` (any value) → output uncolored, regardless of TTY. See\n * https://no-color.org/.\n * - `FORCE_COLOR=1` (any value) → output colored, regardless of TTY. Useful\n * for CI logs that pipe stdout but want the colors preserved.\n * - Default: color when `process.stdout.isTTY`, plain otherwise.\n *\n * Zero deps. ANSI escape codes haven't changed since 1979.\n *\n * @internal — not re-exported from the package barrel.\n */\n\nconst shouldColor = (): boolean => {\n if (process.env.NO_COLOR !== undefined) return false\n if (process.env.FORCE_COLOR !== undefined) return true\n return process.stdout?.isTTY ?? false\n}\n\nconst wrap =\n (code: string) =>\n (s: string): string =>\n shouldColor() ? `\\x1b[${code}m${s}\\x1b[0m` : s\n\nexport const red = wrap(\"31\")\nexport const green = wrap(\"32\")\nexport const yellow = wrap(\"33\")\nexport const blue = wrap(\"34\")\nexport const magenta = wrap(\"35\")\nexport const cyan = wrap(\"36\")\nexport const gray = wrap(\"90\")\n"],"mappings":"AAeA,MAAM,MACA,QAAQ,IAAI,WAAa,IAAA,GACzB,QAAQ,IAAI,cAAgB,IAAA,GACzB,QAAQ,QAAQ,OAAS,GADkB,GADH,GAK3C,EACH,GACA,GACC,EAAY,EAAI,QAAQ,EAAK,GAAG,EAAE,SAAW,EAEpC,EAAM,EAAK,IAAI,EACf,EAAQ,EAAK,IAAI,EACjB,EAAS,EAAK,IAAI,EAClB,EAAO,EAAK,IAAI,EAChB,EAAU,EAAK,IAAI,EACnB,EAAO,EAAK,IAAI,EAChB,EAAO,EAAK,IAAI"}
@@ -0,0 +1,2 @@
1
+ import { t as consoleBootLogger } from "../consoleBootLogger-DY3lxFlo.js";
2
+ export { consoleBootLogger };
@@ -0,0 +1,2 @@
1
+ import{gray as e,red as t,yellow as n}from"./colors.js";const r=()=>new Date().toISOString(),i={debug:(t,n)=>console.debug(`${e(r())} ${e(`DEBUG`)} ${t}`,n??``),info:(t,n)=>console.log(`${e(r())} ${t}`,n??``),warn:(t,i)=>console.warn(`${e(r())} ${n(`WARN`)} ${t}`,i??``),error:(n,i)=>console.error(`${e(r())} ${t(`ERROR`)} ${n}`,i??``)};export{i as consoleBootLogger};
2
+ //# sourceMappingURL=consoleBootLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consoleBootLogger.js","names":[],"sources":["../../src/config/consoleBootLogger.ts"],"sourcesContent":["import type { Logger } from \"functype/logger\"\n\nimport { gray, red, yellow } from \"./colors\"\n\nconst stamp = (): string => new Date().toISOString()\n\n/**\n * Default `Logger` impl for boot diagnostics — zero-dep, raw ANSI colors,\n * NO_COLOR / FORCE_COLOR / isTTY-respecting.\n *\n * All 4 methods of core `Logger` are present (debug included). For consumers\n * already using `functype-log`, pass `createDirectConsoleLogger()` from\n * `functype-log/direct` directly — its `DirectLogger` structurally satisfies\n * core `Logger` (no adapter required).\n *\n * @example\n * ```ts\n * import { bootDiagnostics, consoleBootLogger } from \"functype-os/config\"\n *\n * await bootDiagnostics({ source, required, logger: consoleBootLogger })\n * // (logger defaults to consoleBootLogger anyway — passed explicitly here\n * // to show the swap point for `createDirectConsoleLogger()` etc.)\n * ```\n */\nexport const consoleBootLogger: Logger = {\n debug: (msg, meta) => console.debug(`${gray(stamp())} ${gray(\"DEBUG\")} ${msg}`, meta ?? \"\"),\n info: (msg, meta) => console.log(`${gray(stamp())} ${msg}`, meta ?? \"\"),\n warn: (msg, meta) => console.warn(`${gray(stamp())} ${yellow(\"WARN\")} ${msg}`, meta ?? \"\"),\n error: (msg, meta) => console.error(`${gray(stamp())} ${red(\"ERROR\")} ${msg}`, meta ?? \"\"),\n}\n"],"mappings":"wDAIA,MAAM,MAAsB,IAAI,KAAK,CAAA,CAAE,YAAY,EAoBtC,EAA4B,CACvC,OAAQ,EAAK,IAAS,QAAQ,MAAM,GAAG,EAAK,EAAM,CAAC,EAAE,GAAG,EAAK,OAAO,EAAE,GAAG,IAAO,GAAQ,EAAE,EAC1F,MAAO,EAAK,IAAS,QAAQ,IAAI,GAAG,EAAK,EAAM,CAAC,EAAE,GAAG,IAAO,GAAQ,EAAE,EACtE,MAAO,EAAK,IAAS,QAAQ,KAAK,GAAG,EAAK,EAAM,CAAC,EAAE,GAAG,EAAO,MAAM,EAAE,GAAG,IAAO,GAAQ,EAAE,EACzF,OAAQ,EAAK,IAAS,QAAQ,MAAM,GAAG,EAAK,EAAM,CAAC,EAAE,GAAG,EAAI,OAAO,EAAE,GAAG,IAAO,GAAQ,EAAE,CAC3F"}
@@ -1,2 +1,9 @@
1
1
  import { t as ConfigResolver } from "../ConfigResolver-CovnoBgc.js";
2
- export { ConfigResolver };
2
+ import { t as ConfigSource } from "../ConfigSource-n4VKw8Vi.js";
3
+ import { t as Layered } from "../Layered-4k7fpktc.js";
4
+ import { t as ProcessEnvSource } from "../ProcessEnvSource-C9EzlVbN.js";
5
+ import { t as StaticSource } from "../StaticSource-BjmavHQr.js";
6
+ import { n as MissingKey, r as bootDiagnostics, t as BootDiagnosticsOptions } from "../bootDiagnostics-WXjyslc9.js";
7
+ import { t as consoleBootLogger } from "../consoleBootLogger-DY3lxFlo.js";
8
+ import { t as maskValue } from "../mask-BLjNp9FP.js";
9
+ export { type BootDiagnosticsOptions, ConfigResolver, type ConfigSource, Layered, type MissingKey, ProcessEnvSource, StaticSource, bootDiagnostics, consoleBootLogger, maskValue };
@@ -1 +1 @@
1
- import{ConfigResolver as e}from"./ConfigResolver.js";export{e as ConfigResolver};
1
+ import{consoleBootLogger as e}from"./consoleBootLogger.js";import{maskValue as t}from"./mask.js";import{bootDiagnostics as n}from"./bootDiagnostics.js";import{ConfigResolver as r}from"./ConfigResolver.js";import{Layered as i}from"./Layered.js";import{ProcessEnvSource as a}from"./ProcessEnvSource.js";import{StaticSource as o}from"./StaticSource.js";export{r as ConfigResolver,i as Layered,a as ProcessEnvSource,o as StaticSource,n as bootDiagnostics,e as consoleBootLogger,t as maskValue};
@@ -0,0 +1,2 @@
1
+ import { t as maskValue } from "../mask-BLjNp9FP.js";
2
+ export { maskValue };
@@ -0,0 +1,2 @@
1
+ const e=e=>e.length<=8?`****`:`${e.slice(0,2)}****${e.slice(-2)}`;export{e as maskValue};
2
+ //# sourceMappingURL=mask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mask.js","names":[],"sources":["../../src/config/mask.ts"],"sourcesContent":["/**\n * Mask a sensitive value for display in boot diagnostics or logs.\n *\n * - Values ≤ 8 characters become `****` (no leakage of length or characters).\n * - Longer values become `ab****yz` — first two and last two characters\n * preserved so an operator can confirm which secret loaded without\n * exposing the bulk of it.\n *\n * Exported from `functype-os/config` for ad-hoc masking outside\n * `bootDiagnostics`.\n *\n * @example\n * ```ts\n * maskValue(\"short\") // \"****\"\n * maskValue(\"supersecretvalue\") // \"su****ue\"\n * ```\n */\nexport const maskValue = (value: string): string => {\n if (value.length <= 8) return \"****\"\n return `${value.slice(0, 2)}****${value.slice(-2)}`\n}\n"],"mappings":"AAiBA,MAAa,EAAa,GACpB,EAAM,QAAU,EAAU,OACvB,GAAG,EAAM,MAAM,EAAG,CAAC,EAAE,MAAM,EAAM,MAAM,EAAE"}
@@ -0,0 +1,25 @@
1
+ import { Logger } from "functype/logger";
2
+
3
+ //#region src/config/consoleBootLogger.d.ts
4
+ /**
5
+ * Default `Logger` impl for boot diagnostics — zero-dep, raw ANSI colors,
6
+ * NO_COLOR / FORCE_COLOR / isTTY-respecting.
7
+ *
8
+ * All 4 methods of core `Logger` are present (debug included). For consumers
9
+ * already using `functype-log`, pass `createDirectConsoleLogger()` from
10
+ * `functype-log/direct` directly — its `DirectLogger` structurally satisfies
11
+ * core `Logger` (no adapter required).
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { bootDiagnostics, consoleBootLogger } from "functype-os/config"
16
+ *
17
+ * await bootDiagnostics({ source, required, logger: consoleBootLogger })
18
+ * // (logger defaults to consoleBootLogger anyway — passed explicitly here
19
+ * // to show the swap point for `createDirectConsoleLogger()` etc.)
20
+ * ```
21
+ */
22
+ declare const consoleBootLogger: Logger;
23
+ //#endregion
24
+ export { consoleBootLogger as t };
25
+ //# sourceMappingURL=consoleBootLogger-DY3lxFlo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Env.js","names":[],"sources":["../../src/env/Env.ts"],"sourcesContent":["import type { Either } from \"functype\"\nimport { Left, List, Option, Right } from \"functype\"\n\nimport { EnvError } from \"../errors/errors\"\n\nconst EnvConstructor = (name: string): Option<string> => Option(process.env[name])\n\nconst EnvCompanion = {\n get: (name: string): Option<string> => Option(process.env[name]),\n\n getRequired: (name: string): Either<EnvError, string> => {\n const value = process.env[name]\n return value !== undefined ? Right(value) : Left(EnvError(name))\n },\n\n getOrDefault: (name: string, defaultValue: string): string => process.env[name] ?? defaultValue,\n\n has: (name: string): boolean => process.env[name] !== undefined,\n\n entries: (): List<readonly [string, string]> => {\n const pairs: Readonly<Array<readonly [string, string]>> = Object.entries(process.env)\n .filter((entry): entry is [string, string] => entry[1] !== undefined)\n .map(([k, v]) => [k, v] as const)\n return List(pairs)\n },\n\n parse: <T>(name: string, parser: (value: string) => T): Either<EnvError, T> => {\n const value = process.env[name]\n if (value === undefined) {\n return Left(EnvError(name))\n }\n try {\n const parsed = parser(value)\n if (typeof parsed === \"number\" && isNaN(parsed)) {\n return Left(EnvError(name, `Cannot parse '${value}' as number for '${name}'`))\n }\n return Right(parsed)\n } catch (error) {\n return Left(\n EnvError(name, `Failed to parse '${name}': ${error instanceof Error ? error.message : String(error)}`),\n )\n }\n },\n}\n\nexport const Env: typeof EnvConstructor & typeof EnvCompanion = Object.assign(EnvConstructor, EnvCompanion)\n"],"mappings":"gHA6CA,MAAa,EAAmD,OAAO,OAxC/C,GAAiC,EAAO,QAAQ,IAAI,EAAK,EAwCa,CArC5F,IAAM,GAAiC,EAAO,QAAQ,IAAI,EAAK,EAE/D,YAAc,GAA2C,CACvD,IAAM,EAAQ,QAAQ,IAAI,GAC1B,OAAO,IAAU,IAAA,GAA2B,EAAK,EAAS,CAAI,CAAC,EAAlC,EAAM,CAAK,CAC1C,EAEA,cAAe,EAAc,IAAiC,QAAQ,IAAI,IAAS,EAEnF,IAAM,GAA0B,QAAQ,IAAI,KAAU,IAAA,GAEtD,YAIS,EAHmD,OAAO,QAAQ,QAAQ,GAAG,EACjF,OAAQ,GAAqC,EAAM,KAAO,IAAA,EAAS,EACnE,KAAK,CAAC,EAAG,KAAO,CAAC,EAAG,CAAC,CACR,CAAC,EAGnB,OAAW,EAAc,IAAsD,CAC7E,IAAM,EAAQ,QAAQ,IAAI,GAC1B,GAAI,IAAU,IAAA,GACZ,OAAO,EAAK,EAAS,CAAI,CAAC,EAE5B,GAAI,CACF,IAAM,EAAS,EAAO,CAAK,EAI3B,OAHI,OAAO,GAAW,UAAY,MAAM,CAAM,EACrC,EAAK,EAAS,EAAM,iBAAiB,EAAM,mBAAmB,EAAK,EAAE,CAAC,EAExE,EAAM,CAAM,CACrB,OAAS,EAAO,CACd,OAAO,EACL,EAAS,EAAM,oBAAoB,EAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GAAG,CACvG,CACF,CACF,CAG4F,CAAY"}
1
+ {"version":3,"file":"Env.js","names":[],"sources":["../../src/env/Env.ts"],"sourcesContent":["import type { Either } from \"functype\"\nimport { Left, List, Option, Right } from \"functype\"\n\nimport { EnvError } from \"../errors/errors\"\n\nconst EnvConstructor = (name: string): Option<string> => Option(process.env[name])\n\nconst EnvCompanion = {\n get: (name: string): Option<string> => Option(process.env[name]),\n\n getRequired: (name: string): Either<EnvError, string> => {\n const value = process.env[name]\n return value !== undefined ? Right(value) : Left(EnvError(name))\n },\n\n getOrDefault: (name: string, defaultValue: string): string => process.env[name] ?? defaultValue,\n\n has: (name: string): boolean => process.env[name] !== undefined,\n\n entries: (): List<readonly [string, string]> => {\n const pairs: Readonly<Array<readonly [string, string]>> = Object.entries(process.env)\n .filter((entry): entry is [string, string] => entry[1] !== undefined)\n .map(([k, v]) => [k, v] as const)\n return List(pairs)\n },\n\n parse: <T>(name: string, parser: (value: string) => T): Either<EnvError, T> => {\n const value = process.env[name]\n if (value === undefined) {\n return Left(EnvError(name))\n }\n try {\n const parsed = parser(value)\n if (typeof parsed === \"number\" && isNaN(parsed)) {\n return Left(EnvError(name, `Cannot parse '${value}' as number for '${name}'`))\n }\n return Right(parsed)\n } catch (error) {\n return Left(\n EnvError(name, `Failed to parse '${name}': ${error instanceof Error ? error.message : String(error)}`),\n )\n }\n },\n}\n\nexport const Env: typeof EnvConstructor & typeof EnvCompanion = Object.assign(EnvConstructor, EnvCompanion)\n"],"mappings":"gHA6CA,MAAa,EAAmD,OAAO,OAxC/C,GAAiC,EAAO,QAAQ,IAAI,EAAK,EAwCa,CArC5F,IAAM,GAAiC,EAAO,QAAQ,IAAI,EAAK,EAE/D,YAAc,GAA2C,CACvD,IAAM,EAAQ,QAAQ,IAAI,GAC1B,OAAO,IAAU,IAAA,GAA2B,EAAK,EAAS,CAAI,CAAC,EAAlC,EAAM,CAAK,CAC1C,EAEA,cAAe,EAAc,IAAiC,QAAQ,IAAI,IAAS,EAEnF,IAAM,GAA0B,QAAQ,IAAI,KAAU,IAAA,GAEtD,YAIS,EAHmD,OAAO,QAAQ,QAAQ,GAAG,CAAC,CAClF,OAAQ,GAAqC,EAAM,KAAO,IAAA,EAAS,CAAC,CACpE,KAAK,CAAC,EAAG,KAAO,CAAC,EAAG,CAAC,CACR,CAAC,EAGnB,OAAW,EAAc,IAAsD,CAC7E,IAAM,EAAQ,QAAQ,IAAI,GAC1B,GAAI,IAAU,IAAA,GACZ,OAAO,EAAK,EAAS,CAAI,CAAC,EAE5B,GAAI,CACF,IAAM,EAAS,EAAO,CAAK,EAI3B,OAHI,OAAO,GAAW,UAAY,MAAM,CAAM,EACrC,EAAK,EAAS,EAAM,iBAAiB,EAAM,mBAAmB,EAAK,EAAE,CAAC,EAExE,EAAM,CAAM,CACrB,OAAS,EAAO,CACd,OAAO,EACL,EAAS,EAAM,oBAAoB,EAAK,KAAK,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GAAG,CACvG,CACF,CACF,CAG4F,CAAY"}
package/dist/fs/Fs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Fs.js","names":[],"sources":["../../src/fs/Fs.ts"],"sourcesContent":["import * as fsSync from \"node:fs\"\nimport * as fs from \"node:fs/promises\"\n\nimport type { Either, TaskResult } from \"functype\"\nimport { Err, Left, List, Ok, Option, Right } from \"functype\"\n\nimport { FsError } from \"../errors/errors\"\n\nexport type FileInfo = {\n readonly size: number\n readonly isFile: boolean\n readonly isDirectory: boolean\n readonly isSymbolicLink: boolean\n readonly createdAt: Date\n readonly modifiedAt: Date\n readonly accessedAt: Date\n readonly permissions: number\n}\n\nconst toFileInfo = (stats: fsSync.Stats): FileInfo => ({\n size: stats.size,\n isFile: stats.isFile(),\n isDirectory: stats.isDirectory(),\n isSymbolicLink: stats.isSymbolicLink(),\n createdAt: stats.birthtime,\n modifiedAt: stats.mtime,\n accessedAt: stats.atime,\n permissions: stats.mode,\n})\n\nconst toFsError = (p: string, op: string, error: unknown): FsError =>\n FsError(p, op, error instanceof Error ? error : new Error(String(error)))\n\n/**\n * Linux magic filesystems where recursive mkdir can hang indefinitely against\n * unwritable subpaths (libuv quirk; macOS errors immediately instead).\n * Refusing recursive mkdir under these prefixes keeps `mkdir({ recursive: true })`\n * fast-failing and predictable across platforms. See issue #135.\n */\nconst MAGIC_FS_PREFIXES: ReadonlyArray<string> = [\"/proc/\", \"/sys/\", \"/dev/\"]\n\nconst isMagicFsPath = (p: string): boolean => MAGIC_FS_PREFIXES.some((prefix) => p.startsWith(prefix))\n\nconst matchGlob = (filePath: string, pattern: string): boolean => {\n // Escape every regex metachar EXCEPT '*' first, so the subsequent\n // '*' transformations are the only special characters in the output.\n // Order matters: backslashes must be escaped before any '\\\\' insertion below.\n const escaped = pattern.replace(/[\\\\^$+?.()|[\\]{}]/g, \"\\\\$&\")\n const regex = escaped\n .replace(/\\*\\*\\//g, \"{{GLOBSTAR}}\")\n .replace(/\\*\\*/g, \"{{GLOBSTAR}}\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/\\{\\{GLOBSTAR\\}\\}/g, \"(?:.*/)?\")\n return new RegExp(`^${regex}$`).test(filePath)\n}\n\nexport const Fs = {\n // Async methods — return TaskResult<T>\n\n exists: async (p: string): TaskResult<boolean> => {\n try {\n await fs.access(p)\n return Ok(true)\n } catch {\n return Ok(false)\n }\n },\n\n readFile: async (p: string, encoding: BufferEncoding = \"utf8\"): TaskResult<string> => {\n try {\n return Ok(await fs.readFile(p, { encoding }))\n } catch (error) {\n return Err(toFsError(p, \"readFile\", error))\n }\n },\n\n readFileOpt: async (p: string, encoding: BufferEncoding = \"utf8\"): TaskResult<Option<string>> => {\n try {\n return Ok(Option(await fs.readFile(p, { encoding })))\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return Ok(Option<string>(undefined))\n }\n return Err(toFsError(p, \"readFile\", error))\n }\n },\n\n stat: async (p: string): TaskResult<FileInfo> => {\n try {\n return Ok(toFileInfo(await fs.stat(p)))\n } catch (error) {\n return Err(toFsError(p, \"stat\", error))\n }\n },\n\n copyFile: async (src: string, dest: string): TaskResult<void> => {\n try {\n await fs.copyFile(src, dest)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(src, \"copyFile\", error))\n }\n },\n\n rename: async (oldPath: string, newPath: string): TaskResult<void> => {\n try {\n await fs.rename(oldPath, newPath)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(oldPath, \"rename\", error))\n }\n },\n\n readdir: async (p: string): TaskResult<List<string>> => {\n try {\n return Ok(List(await fs.readdir(p)))\n } catch (error) {\n return Err(toFsError(p, \"readdir\", error))\n }\n },\n\n glob: async (dir: string, pattern: string): TaskResult<List<string>> => {\n try {\n const entries = await fs.readdir(dir, { recursive: true, encoding: \"utf8\" })\n const matched = entries.filter((entry) => matchGlob(entry, pattern))\n return Ok(List(matched))\n } catch (error) {\n return Err(toFsError(dir, \"glob\", error))\n }\n },\n\n writeFile: async (p: string, data: string, encoding: BufferEncoding = \"utf8\"): TaskResult<void> => {\n try {\n await fs.writeFile(p, data, { encoding })\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"writeFile\", error))\n }\n },\n\n appendFile: async (p: string, data: string, encoding: BufferEncoding = \"utf8\"): TaskResult<void> => {\n try {\n await fs.appendFile(p, data, { encoding })\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"appendFile\", error))\n }\n },\n\n mkdir: async (p: string, options?: { recursive?: boolean }): TaskResult<void> => {\n if (options?.recursive && isMagicFsPath(p)) {\n return Err(\n toFsError(p, \"mkdir\", new Error(\"recursive mkdir refused under magic filesystem root (/proc, /sys, /dev)\")),\n )\n }\n try {\n await fs.mkdir(p, options)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"mkdir\", error))\n }\n },\n\n // Sync methods — return Either<FsError, T>\n\n existsSync: (p: string): boolean => {\n try {\n fsSync.accessSync(p)\n return true\n } catch {\n return false\n }\n },\n\n readFileSync: (p: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, string> => {\n try {\n return Right(fsSync.readFileSync(p, { encoding }))\n } catch (error) {\n return Left(toFsError(p, \"readFileSync\", error))\n }\n },\n\n readFileOptSync: (p: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, Option<string>> => {\n try {\n return Right(Option(fsSync.readFileSync(p, { encoding })))\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return Right(Option<string>(undefined))\n }\n return Left(toFsError(p, \"readFileOptSync\", error))\n }\n },\n\n statSync: (p: string): Either<FsError, FileInfo> => {\n try {\n return Right(toFileInfo(fsSync.statSync(p)))\n } catch (error) {\n return Left(toFsError(p, \"statSync\", error))\n }\n },\n\n copyFileSync: (src: string, dest: string): Either<FsError, void> => {\n try {\n fsSync.copyFileSync(src, dest)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(src, \"copyFileSync\", error))\n }\n },\n\n renameSync: (oldPath: string, newPath: string): Either<FsError, void> => {\n try {\n fsSync.renameSync(oldPath, newPath)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(oldPath, \"renameSync\", error))\n }\n },\n\n readdirSync: (p: string): Either<FsError, List<string>> => {\n try {\n return Right(List(fsSync.readdirSync(p)))\n } catch (error) {\n return Left(toFsError(p, \"readdirSync\", error))\n }\n },\n\n writeFileSync: (p: string, data: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, void> => {\n try {\n fsSync.writeFileSync(p, data, { encoding })\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"writeFileSync\", error))\n }\n },\n\n appendFileSync: (p: string, data: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, void> => {\n try {\n fsSync.appendFileSync(p, data, { encoding })\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"appendFileSync\", error))\n }\n },\n\n mkdirSync: (p: string, options?: { recursive?: boolean }): Either<FsError, void> => {\n if (options?.recursive && isMagicFsPath(p)) {\n return Left(\n toFsError(p, \"mkdirSync\", new Error(\"recursive mkdir refused under magic filesystem root (/proc, /sys, /dev)\")),\n )\n }\n try {\n fsSync.mkdirSync(p, options)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"mkdirSync\", error))\n }\n },\n\n unlink: async (p: string): TaskResult<void> => {\n try {\n await fs.unlink(p)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"unlink\", error))\n }\n },\n\n unlinkSync: (p: string): Either<FsError, void> => {\n try {\n fsSync.unlinkSync(p)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"unlinkSync\", error))\n }\n },\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Fs.js","names":[],"sources":["../../src/fs/Fs.ts"],"sourcesContent":["import * as fsSync from \"node:fs\"\nimport * as fs from \"node:fs/promises\"\n\nimport type { Either, TaskResult } from \"functype\"\nimport { Err, Left, List, Ok, Option, Right } from \"functype\"\n\nimport { FsError } from \"../errors/errors\"\n\nexport type FileInfo = {\n readonly size: number\n readonly isFile: boolean\n readonly isDirectory: boolean\n readonly isSymbolicLink: boolean\n readonly createdAt: Date\n readonly modifiedAt: Date\n readonly accessedAt: Date\n readonly permissions: number\n}\n\nconst toFileInfo = (stats: fsSync.Stats): FileInfo => ({\n size: stats.size,\n isFile: stats.isFile(),\n isDirectory: stats.isDirectory(),\n isSymbolicLink: stats.isSymbolicLink(),\n createdAt: stats.birthtime,\n modifiedAt: stats.mtime,\n accessedAt: stats.atime,\n permissions: stats.mode,\n})\n\nconst toFsError = (p: string, op: string, error: unknown): FsError =>\n FsError(p, op, error instanceof Error ? error : new Error(String(error)))\n\n/**\n * Linux magic filesystems where recursive mkdir can hang indefinitely against\n * unwritable subpaths (libuv quirk; macOS errors immediately instead).\n * Refusing recursive mkdir under these prefixes keeps `mkdir({ recursive: true })`\n * fast-failing and predictable across platforms. See issue #135.\n */\nconst MAGIC_FS_PREFIXES: ReadonlyArray<string> = [\"/proc/\", \"/sys/\", \"/dev/\"]\n\nconst isMagicFsPath = (p: string): boolean => MAGIC_FS_PREFIXES.some((prefix) => p.startsWith(prefix))\n\nconst matchGlob = (filePath: string, pattern: string): boolean => {\n // Escape every regex metachar EXCEPT '*' first, so the subsequent\n // '*' transformations are the only special characters in the output.\n // Order matters: backslashes must be escaped before any '\\\\' insertion below.\n const escaped = pattern.replace(/[\\\\^$+?.()|[\\]{}]/g, \"\\\\$&\")\n const regex = escaped\n .replace(/\\*\\*\\//g, \"{{GLOBSTAR}}\")\n .replace(/\\*\\*/g, \"{{GLOBSTAR}}\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/\\{\\{GLOBSTAR\\}\\}/g, \"(?:.*/)?\")\n return new RegExp(`^${regex}$`).test(filePath)\n}\n\nexport const Fs = {\n // Async methods — return TaskResult<T>\n\n exists: async (p: string): TaskResult<boolean> => {\n try {\n await fs.access(p)\n return Ok(true)\n } catch {\n return Ok(false)\n }\n },\n\n readFile: async (p: string, encoding: BufferEncoding = \"utf8\"): TaskResult<string> => {\n try {\n return Ok(await fs.readFile(p, { encoding }))\n } catch (error) {\n return Err(toFsError(p, \"readFile\", error))\n }\n },\n\n readFileOpt: async (p: string, encoding: BufferEncoding = \"utf8\"): TaskResult<Option<string>> => {\n try {\n return Ok(Option(await fs.readFile(p, { encoding })))\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return Ok(Option<string>(undefined))\n }\n return Err(toFsError(p, \"readFile\", error))\n }\n },\n\n stat: async (p: string): TaskResult<FileInfo> => {\n try {\n return Ok(toFileInfo(await fs.stat(p)))\n } catch (error) {\n return Err(toFsError(p, \"stat\", error))\n }\n },\n\n copyFile: async (src: string, dest: string): TaskResult<void> => {\n try {\n await fs.copyFile(src, dest)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(src, \"copyFile\", error))\n }\n },\n\n rename: async (oldPath: string, newPath: string): TaskResult<void> => {\n try {\n await fs.rename(oldPath, newPath)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(oldPath, \"rename\", error))\n }\n },\n\n readdir: async (p: string): TaskResult<List<string>> => {\n try {\n return Ok(List(await fs.readdir(p)))\n } catch (error) {\n return Err(toFsError(p, \"readdir\", error))\n }\n },\n\n glob: async (dir: string, pattern: string): TaskResult<List<string>> => {\n try {\n const entries = await fs.readdir(dir, { recursive: true, encoding: \"utf8\" })\n const matched = entries.filter((entry) => matchGlob(entry, pattern))\n return Ok(List(matched))\n } catch (error) {\n return Err(toFsError(dir, \"glob\", error))\n }\n },\n\n writeFile: async (p: string, data: string, encoding: BufferEncoding = \"utf8\"): TaskResult<void> => {\n try {\n await fs.writeFile(p, data, { encoding })\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"writeFile\", error))\n }\n },\n\n appendFile: async (p: string, data: string, encoding: BufferEncoding = \"utf8\"): TaskResult<void> => {\n try {\n await fs.appendFile(p, data, { encoding })\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"appendFile\", error))\n }\n },\n\n mkdir: async (p: string, options?: { recursive?: boolean }): TaskResult<void> => {\n if (options?.recursive && isMagicFsPath(p)) {\n return Err(\n toFsError(p, \"mkdir\", new Error(\"recursive mkdir refused under magic filesystem root (/proc, /sys, /dev)\")),\n )\n }\n try {\n await fs.mkdir(p, options)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"mkdir\", error))\n }\n },\n\n // Sync methods — return Either<FsError, T>\n\n existsSync: (p: string): boolean => {\n try {\n fsSync.accessSync(p)\n return true\n } catch {\n return false\n }\n },\n\n readFileSync: (p: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, string> => {\n try {\n return Right(fsSync.readFileSync(p, { encoding }))\n } catch (error) {\n return Left(toFsError(p, \"readFileSync\", error))\n }\n },\n\n readFileOptSync: (p: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, Option<string>> => {\n try {\n return Right(Option(fsSync.readFileSync(p, { encoding })))\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return Right(Option<string>(undefined))\n }\n return Left(toFsError(p, \"readFileOptSync\", error))\n }\n },\n\n statSync: (p: string): Either<FsError, FileInfo> => {\n try {\n return Right(toFileInfo(fsSync.statSync(p)))\n } catch (error) {\n return Left(toFsError(p, \"statSync\", error))\n }\n },\n\n copyFileSync: (src: string, dest: string): Either<FsError, void> => {\n try {\n fsSync.copyFileSync(src, dest)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(src, \"copyFileSync\", error))\n }\n },\n\n renameSync: (oldPath: string, newPath: string): Either<FsError, void> => {\n try {\n fsSync.renameSync(oldPath, newPath)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(oldPath, \"renameSync\", error))\n }\n },\n\n readdirSync: (p: string): Either<FsError, List<string>> => {\n try {\n return Right(List(fsSync.readdirSync(p)))\n } catch (error) {\n return Left(toFsError(p, \"readdirSync\", error))\n }\n },\n\n writeFileSync: (p: string, data: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, void> => {\n try {\n fsSync.writeFileSync(p, data, { encoding })\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"writeFileSync\", error))\n }\n },\n\n appendFileSync: (p: string, data: string, encoding: BufferEncoding = \"utf8\"): Either<FsError, void> => {\n try {\n fsSync.appendFileSync(p, data, { encoding })\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"appendFileSync\", error))\n }\n },\n\n mkdirSync: (p: string, options?: { recursive?: boolean }): Either<FsError, void> => {\n if (options?.recursive && isMagicFsPath(p)) {\n return Left(\n toFsError(p, \"mkdirSync\", new Error(\"recursive mkdir refused under magic filesystem root (/proc, /sys, /dev)\")),\n )\n }\n try {\n fsSync.mkdirSync(p, options)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"mkdirSync\", error))\n }\n },\n\n unlink: async (p: string): TaskResult<void> => {\n try {\n await fs.unlink(p)\n return Ok(undefined as void)\n } catch (error) {\n return Err(toFsError(p, \"unlink\", error))\n }\n },\n\n unlinkSync: (p: string): Either<FsError, void> => {\n try {\n fsSync.unlinkSync(p)\n return Right(undefined as void)\n } catch (error) {\n return Left(toFsError(p, \"unlinkSync\", error))\n }\n },\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{ConfigError as e,EnvError as t,FsError as n,PathError as r,ProcessError as i}from"./errors/errors.js";import{Fs as a}from"./fs/Fs.js";import{Path as o,expandPath as s,expandTilde as c,expandVars as l}from"./path/PathExpander.js";import{ConfigResolver as u}from"./config/ConfigResolver.js";import"./config/index.js";import{Env as d}from"./env/Env.js";import"./env/index.js";import"./errors/index.js";import"./fs/index.js";import"./path/index.js";import{Platform as f}from"./platform/Platform.js";import"./platform/index.js";import{Process as p}from"./process/Process.js";import"./process/index.js";export{e as ConfigError,u as ConfigResolver,d as Env,t as EnvError,a as Fs,n as FsError,o as Path,r as PathError,f as Platform,p as Process,i as ProcessError,s as expandPath,c as expandTilde,l as expandVars};
1
+ import{ConfigError as e,EnvError as t,FsError as n,PathError as r,ProcessError as i}from"./errors/errors.js";import{Fs as a}from"./fs/Fs.js";import{Path as o,expandPath as s,expandTilde as c,expandVars as l}from"./path/PathExpander.js";import{ConfigResolver as u}from"./config/ConfigResolver.js";import{Env as d}from"./env/Env.js";import"./config/index.js";import"./env/index.js";import"./errors/index.js";import"./fs/index.js";import"./path/index.js";import{Platform as f}from"./platform/Platform.js";import"./platform/index.js";import{Process as p}from"./process/Process.js";import"./process/index.js";export{e as ConfigError,u as ConfigResolver,d as Env,t as EnvError,a as Fs,n as FsError,o as Path,r as PathError,f as Platform,p as Process,i as ProcessError,s as expandPath,c as expandTilde,l as expandVars};
@@ -0,0 +1,22 @@
1
+ //#region src/config/mask.d.ts
2
+ /**
3
+ * Mask a sensitive value for display in boot diagnostics or logs.
4
+ *
5
+ * - Values ≤ 8 characters become `****` (no leakage of length or characters).
6
+ * - Longer values become `ab****yz` — first two and last two characters
7
+ * preserved so an operator can confirm which secret loaded without
8
+ * exposing the bulk of it.
9
+ *
10
+ * Exported from `functype-os/config` for ad-hoc masking outside
11
+ * `bootDiagnostics`.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * maskValue("short") // "****"
16
+ * maskValue("supersecretvalue") // "su****ue"
17
+ * ```
18
+ */
19
+ declare const maskValue: (value: string) => string;
20
+ //#endregion
21
+ export { maskValue as t };
22
+ //# sourceMappingURL=mask-BLjNp9FP.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Platform.js","names":[],"sources":["../../src/platform/Platform.ts"],"sourcesContent":["import { execSync } from \"node:child_process\"\nimport * as fs from \"node:fs\"\nimport * as os from \"node:os\"\nimport * as path from \"node:path\"\n\nimport { List, Option } from \"functype\"\n\nexport type UserInfo = {\n readonly username: string\n readonly uid: number\n readonly gid: number\n readonly shell: string | null\n readonly homedir: string\n}\n\nexport type CloudProvider = \"onedrive\" | \"gdrive\" | \"dropbox\" | \"icloud\"\n\nexport type CloudStorageDir = {\n readonly provider: CloudProvider\n readonly path: string\n readonly label: string\n}\n\nconst hasDockerEnv = (): boolean => {\n try {\n fs.statSync(\"/.dockerenv\")\n return true\n } catch {\n return false\n }\n}\nconst hasDockerCGroup = (): boolean => {\n try {\n return fs.readFileSync(\"/proc/self/cgroup\", \"utf8\").includes(\"docker\")\n } catch {\n return false\n }\n}\n\nconst memo = <T>(fn: () => T): (() => T) => {\n const cache: { value?: T } = {}\n return () => {\n if (!(\"value\" in cache)) {\n cache.value = fn()\n }\n return cache.value as T\n }\n}\n\nconst cachedIsDocker = memo(() => hasDockerEnv() || hasDockerCGroup())\nconst cachedIsKube = memo(() => {\n try {\n return fs.readFileSync(\"/proc/self/cgroup\", \"utf8\").includes(\"kube\")\n } catch {\n return false\n }\n})\nconst cachedIsWSL = memo(() => {\n try {\n const version = fs.readFileSync(\"/proc/version\", \"utf8\")\n return version.includes(\"Microsoft\") || version.includes(\"WSL\")\n } catch {\n return false\n }\n})\nconst cachedIsCI = memo(\n () =>\n process.env[\"CI\"] !== undefined ||\n process.env[\"GITHUB_ACTIONS\"] !== undefined ||\n process.env[\"GITLAB_CI\"] !== undefined ||\n process.env[\"CIRCLECI\"] !== undefined ||\n process.env[\"JENKINS_URL\"] !== undefined ||\n process.env[\"TRAVIS\"] !== undefined ||\n process.env[\"BUILDKITE\"] !== undefined,\n)\n\nconst SYSTEM_ACCOUNTS = new Set([\"all users\", \"default\", \"default user\", \"public\"])\n\nconst isSystemAccount = (name: string): boolean => {\n const lower = name.toLowerCase()\n if (SYSTEM_ACCOUNTS.has(lower)) return true\n if (lower.startsWith(\"defaultuser\") || lower.startsWith(\"desktop.\") || lower.startsWith(\"wsiaccount\")) return true\n return false\n}\n\nconst isDirectory = (p: string): boolean => {\n try {\n return fs.statSync(p).isDirectory()\n } catch {\n return false\n }\n}\n\nconst readRealUsers = (usersDir: string): readonly string[] | undefined => {\n try {\n const entries = fs.readdirSync(usersDir)\n return entries.filter((name) => !isSystemAccount(name) && isDirectory(path.join(usersDir, name)))\n } catch {\n return undefined\n }\n}\n\nconst resolveViaCmdExe = (usersDir: string, fallback: string): Option<string> => {\n try {\n const raw = execSync(\"cmd.exe /c echo %USERPROFILE%\", { timeout: 3000, encoding: \"utf8\" }).trim()\n // Convert Windows path C:\\Users\\foo → /mnt/c/Users/foo\n const match = raw.match(/^([A-Za-z]):\\\\(.*)$/)\n if (match) {\n const drive = match[1].toLowerCase()\n const rest = match[2].replace(/\\\\/g, \"/\")\n const wslPath = `/mnt/${drive}/${rest}`\n if (isDirectory(wslPath)) {\n return Option(wslPath)\n }\n }\n } catch {\n // cmd.exe failed, fall through to best guess\n }\n return Option(fallback)\n}\n\nconst resolveWindowsHome = (): Option<string> => {\n if (!cachedIsWSL()) return Option<string>(undefined)\n\n const usersDir = \"/mnt/c/Users\"\n const realUsers = readRealUsers(usersDir)\n if (realUsers === undefined) return Option<string>(undefined)\n\n if (realUsers.length === 1) {\n return Option(path.join(usersDir, realUsers[0]))\n }\n\n if (realUsers.length > 1) {\n return resolveViaCmdExe(usersDir, path.join(usersDir, realUsers[0]))\n }\n\n return Option<string>(undefined)\n}\n\nconst cachedWindowsHome = memo(resolveWindowsHome)\n\nconst detectCloudProvider = (name: string): { provider: CloudProvider; label: string } | undefined => {\n if (name === \"OneDrive\" || name.startsWith(\"OneDrive \") || name.startsWith(\"OneDrive-\")) {\n return { provider: \"onedrive\", label: name }\n }\n if (name === \"Google Drive\") {\n return { provider: \"gdrive\", label: name }\n }\n if (name === \"Dropbox\") {\n return { provider: \"dropbox\", label: name }\n }\n return undefined\n}\n\nconst scanCloudStorageDirs = (home: string): CloudStorageDir[] => {\n const results: CloudStorageDir[] = []\n\n // Direct home children\n try {\n const entries = fs.readdirSync(home)\n for (const name of entries) {\n const fullPath = path.join(home, name)\n const detection = detectCloudProvider(name)\n if (detection && isDirectory(fullPath)) {\n results.push({ provider: detection.provider, path: fullPath, label: detection.label })\n }\n }\n } catch {\n // home dir may not exist or be readable\n }\n\n // macOS Library/CloudStorage children\n const cloudStoragePath = path.join(home, \"Library\", \"CloudStorage\")\n try {\n const entries = fs.readdirSync(cloudStoragePath)\n for (const name of entries) {\n const fullPath = path.join(cloudStoragePath, name)\n if (!isDirectory(fullPath)) continue\n if (name.startsWith(\"OneDrive\")) {\n results.push({ provider: \"onedrive\", path: fullPath, label: name })\n } else if (name.startsWith(\"GoogleDrive\")) {\n results.push({ provider: \"gdrive\", path: fullPath, label: name })\n }\n }\n } catch {\n // Library/CloudStorage may not exist\n }\n\n // macOS iCloud fixed path\n const icloudPath = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\")\n if (isDirectory(icloudPath)) {\n results.push({ provider: \"icloud\", path: icloudPath, label: \"iCloud Drive\" })\n }\n\n return results\n}\n\nexport const Platform = {\n os: (): \"darwin\" | \"linux\" | \"win32\" | string => process.platform,\n arch: (): string => process.arch,\n homeDir: (): string => os.homedir(),\n tmpDir: (): string => os.tmpdir(),\n hostname: (): string => os.hostname(),\n eol: (): string => os.EOL,\n pathSep: (): string => path.sep,\n\n isWindows: (): boolean => process.platform === \"win32\",\n isMac: (): boolean => process.platform === \"darwin\",\n isLinux: (): boolean => process.platform === \"linux\",\n\n userInfo: (): Option<UserInfo> => {\n try {\n const info = os.userInfo()\n return Option({\n username: info.username,\n uid: info.uid,\n gid: info.gid,\n shell: info.shell,\n homedir: info.homedir,\n })\n } catch {\n return Option<UserInfo>(undefined)\n }\n },\n\n isDocker: (): boolean => cachedIsDocker(),\n isKubernetes: (): boolean => cachedIsKube(),\n isWSL: (): boolean => cachedIsWSL(),\n isCI: (): boolean => cachedIsCI(),\n\n isContainer: (): boolean => Platform.isDocker() || Platform.isKubernetes(),\n\n windowsHomeDir: (): Option<string> => cachedWindowsHome(),\n\n homeDirs: (): List<string> => {\n const homes: string[] = [os.homedir()]\n Platform.windowsHomeDir().forEach((winHome) => {\n if (winHome !== os.homedir()) {\n homes.push(winHome)\n }\n })\n return List(homes)\n },\n\n cloudStorageDirs: (home?: string): List<CloudStorageDir> => {\n if (home !== undefined) {\n return List(scanCloudStorageDirs(home))\n }\n const allDirs: CloudStorageDir[] = []\n const seenPaths = new Set<string>()\n Platform.homeDirs().forEach((h) => {\n for (const dir of scanCloudStorageDirs(h)) {\n if (!seenPaths.has(dir.path)) {\n seenPaths.add(dir.path)\n allDirs.push(dir)\n }\n }\n })\n return List(allDirs)\n },\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Platform.js","names":[],"sources":["../../src/platform/Platform.ts"],"sourcesContent":["import { execSync } from \"node:child_process\"\nimport * as fs from \"node:fs\"\nimport * as os from \"node:os\"\nimport * as path from \"node:path\"\n\nimport { List, Option } from \"functype\"\n\nexport type UserInfo = {\n readonly username: string\n readonly uid: number\n readonly gid: number\n readonly shell: string | null\n readonly homedir: string\n}\n\nexport type CloudProvider = \"onedrive\" | \"gdrive\" | \"dropbox\" | \"icloud\"\n\nexport type CloudStorageDir = {\n readonly provider: CloudProvider\n readonly path: string\n readonly label: string\n}\n\nconst hasDockerEnv = (): boolean => {\n try {\n fs.statSync(\"/.dockerenv\")\n return true\n } catch {\n return false\n }\n}\nconst hasDockerCGroup = (): boolean => {\n try {\n return fs.readFileSync(\"/proc/self/cgroup\", \"utf8\").includes(\"docker\")\n } catch {\n return false\n }\n}\n\nconst memo = <T>(fn: () => T): (() => T) => {\n const cache: { value?: T } = {}\n return () => {\n if (!(\"value\" in cache)) {\n cache.value = fn()\n }\n return cache.value as T\n }\n}\n\nconst cachedIsDocker = memo(() => hasDockerEnv() || hasDockerCGroup())\nconst cachedIsKube = memo(() => {\n try {\n return fs.readFileSync(\"/proc/self/cgroup\", \"utf8\").includes(\"kube\")\n } catch {\n return false\n }\n})\nconst cachedIsWSL = memo(() => {\n try {\n const version = fs.readFileSync(\"/proc/version\", \"utf8\")\n return version.includes(\"Microsoft\") || version.includes(\"WSL\")\n } catch {\n return false\n }\n})\nconst cachedIsCI = memo(\n () =>\n process.env[\"CI\"] !== undefined ||\n process.env[\"GITHUB_ACTIONS\"] !== undefined ||\n process.env[\"GITLAB_CI\"] !== undefined ||\n process.env[\"CIRCLECI\"] !== undefined ||\n process.env[\"JENKINS_URL\"] !== undefined ||\n process.env[\"TRAVIS\"] !== undefined ||\n process.env[\"BUILDKITE\"] !== undefined,\n)\n\nconst SYSTEM_ACCOUNTS = new Set([\"all users\", \"default\", \"default user\", \"public\"])\n\nconst isSystemAccount = (name: string): boolean => {\n const lower = name.toLowerCase()\n if (SYSTEM_ACCOUNTS.has(lower)) return true\n if (lower.startsWith(\"defaultuser\") || lower.startsWith(\"desktop.\") || lower.startsWith(\"wsiaccount\")) return true\n return false\n}\n\nconst isDirectory = (p: string): boolean => {\n try {\n return fs.statSync(p).isDirectory()\n } catch {\n return false\n }\n}\n\nconst readRealUsers = (usersDir: string): readonly string[] | undefined => {\n try {\n const entries = fs.readdirSync(usersDir)\n return entries.filter((name) => !isSystemAccount(name) && isDirectory(path.join(usersDir, name)))\n } catch {\n return undefined\n }\n}\n\nconst resolveViaCmdExe = (usersDir: string, fallback: string): Option<string> => {\n try {\n const raw = execSync(\"cmd.exe /c echo %USERPROFILE%\", { timeout: 3000, encoding: \"utf8\" }).trim()\n // Convert Windows path C:\\Users\\foo → /mnt/c/Users/foo\n const match = raw.match(/^([A-Za-z]):\\\\(.*)$/)\n if (match) {\n const drive = match[1].toLowerCase()\n const rest = match[2].replace(/\\\\/g, \"/\")\n const wslPath = `/mnt/${drive}/${rest}`\n if (isDirectory(wslPath)) {\n return Option(wslPath)\n }\n }\n } catch {\n // cmd.exe failed, fall through to best guess\n }\n return Option(fallback)\n}\n\nconst resolveWindowsHome = (): Option<string> => {\n if (!cachedIsWSL()) return Option<string>(undefined)\n\n const usersDir = \"/mnt/c/Users\"\n const realUsers = readRealUsers(usersDir)\n if (realUsers === undefined) return Option<string>(undefined)\n\n if (realUsers.length === 1) {\n return Option(path.join(usersDir, realUsers[0]))\n }\n\n if (realUsers.length > 1) {\n return resolveViaCmdExe(usersDir, path.join(usersDir, realUsers[0]))\n }\n\n return Option<string>(undefined)\n}\n\nconst cachedWindowsHome = memo(resolveWindowsHome)\n\nconst detectCloudProvider = (name: string): { provider: CloudProvider; label: string } | undefined => {\n if (name === \"OneDrive\" || name.startsWith(\"OneDrive \") || name.startsWith(\"OneDrive-\")) {\n return { provider: \"onedrive\", label: name }\n }\n if (name === \"Google Drive\") {\n return { provider: \"gdrive\", label: name }\n }\n if (name === \"Dropbox\") {\n return { provider: \"dropbox\", label: name }\n }\n return undefined\n}\n\nconst scanCloudStorageDirs = (home: string): CloudStorageDir[] => {\n const results: CloudStorageDir[] = []\n\n // Direct home children\n try {\n const entries = fs.readdirSync(home)\n for (const name of entries) {\n const fullPath = path.join(home, name)\n const detection = detectCloudProvider(name)\n if (detection && isDirectory(fullPath)) {\n results.push({ provider: detection.provider, path: fullPath, label: detection.label })\n }\n }\n } catch {\n // home dir may not exist or be readable\n }\n\n // macOS Library/CloudStorage children\n const cloudStoragePath = path.join(home, \"Library\", \"CloudStorage\")\n try {\n const entries = fs.readdirSync(cloudStoragePath)\n for (const name of entries) {\n const fullPath = path.join(cloudStoragePath, name)\n if (!isDirectory(fullPath)) continue\n if (name.startsWith(\"OneDrive\")) {\n results.push({ provider: \"onedrive\", path: fullPath, label: name })\n } else if (name.startsWith(\"GoogleDrive\")) {\n results.push({ provider: \"gdrive\", path: fullPath, label: name })\n }\n }\n } catch {\n // Library/CloudStorage may not exist\n }\n\n // macOS iCloud fixed path\n const icloudPath = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\")\n if (isDirectory(icloudPath)) {\n results.push({ provider: \"icloud\", path: icloudPath, label: \"iCloud Drive\" })\n }\n\n return results\n}\n\nexport const Platform = {\n os: (): \"darwin\" | \"linux\" | \"win32\" | string => process.platform,\n arch: (): string => process.arch,\n homeDir: (): string => os.homedir(),\n tmpDir: (): string => os.tmpdir(),\n hostname: (): string => os.hostname(),\n eol: (): string => os.EOL,\n pathSep: (): string => path.sep,\n\n isWindows: (): boolean => process.platform === \"win32\",\n isMac: (): boolean => process.platform === \"darwin\",\n isLinux: (): boolean => process.platform === \"linux\",\n\n userInfo: (): Option<UserInfo> => {\n try {\n const info = os.userInfo()\n return Option({\n username: info.username,\n uid: info.uid,\n gid: info.gid,\n shell: info.shell,\n homedir: info.homedir,\n })\n } catch {\n return Option<UserInfo>(undefined)\n }\n },\n\n isDocker: (): boolean => cachedIsDocker(),\n isKubernetes: (): boolean => cachedIsKube(),\n isWSL: (): boolean => cachedIsWSL(),\n isCI: (): boolean => cachedIsCI(),\n\n isContainer: (): boolean => Platform.isDocker() || Platform.isKubernetes(),\n\n windowsHomeDir: (): Option<string> => cachedWindowsHome(),\n\n homeDirs: (): List<string> => {\n const homes: string[] = [os.homedir()]\n Platform.windowsHomeDir().forEach((winHome) => {\n if (winHome !== os.homedir()) {\n homes.push(winHome)\n }\n })\n return List(homes)\n },\n\n cloudStorageDirs: (home?: string): List<CloudStorageDir> => {\n if (home !== undefined) {\n return List(scanCloudStorageDirs(home))\n }\n const allDirs: CloudStorageDir[] = []\n const seenPaths = new Set<string>()\n Platform.homeDirs().forEach((h) => {\n for (const dir of scanCloudStorageDirs(h)) {\n if (!seenPaths.has(dir.path)) {\n seenPaths.add(dir.path)\n allDirs.push(dir)\n }\n }\n })\n return List(allDirs)\n },\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functype-os",
3
- "version": "1.2.2",
3
+ "version": "1.3.1",
4
4
  "description": "Functional OS utilities using functype data structures — env vars, path expansion, file ops, platform detection",
5
5
  "keywords": [
6
6
  "functype",
@@ -28,10 +28,10 @@
28
28
  "devDependencies": {
29
29
  "@types/node": "^24.12.4",
30
30
  "eslint-config-prettier": "^10.1.8",
31
- "ts-builds": "^2.8.1",
31
+ "ts-builds": "^3.0.0",
32
32
  "tsdown": "^0.22.1",
33
33
  "vitest": "^4.1.8",
34
- "functype": "^1.2.2"
34
+ "functype": "^1.3.1"
35
35
  },
36
36
  "type": "module",
37
37
  "main": "./dist/index.js",