tyfigo 0.1.0-next.d1e2fdf.0 → 0.1.0-next.ddcaab3.0

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 (73) hide show
  1. package/dist/bun.d.mts +7 -0
  2. package/dist/bun.mjs +35 -0
  3. package/dist/bun.mjs.map +1 -0
  4. package/dist/effect.d.mts +80 -2
  5. package/dist/effect.mjs +84 -1
  6. package/dist/effect.mjs.map +1 -0
  7. package/dist/{index-_5Tr2XH2.d.mts → index-CM4xg0II.d.mts} +9 -2
  8. package/dist/index.d.mts +4 -42
  9. package/dist/index.mjs +2 -78
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/load-BqGJgpfo.mjs +124 -0
  12. package/dist/load-BqGJgpfo.mjs.map +1 -0
  13. package/dist/load-CKIE0mVT.d.mts +53 -0
  14. package/dist/mapper.d.mts +1 -0
  15. package/dist/mapper.mjs +398 -0
  16. package/dist/mapper.mjs.map +1 -0
  17. package/dist/{resolve-wE3GPIQp.mjs → placeholders-MqW2KO9Z.mjs} +2 -324
  18. package/dist/placeholders-MqW2KO9Z.mjs.map +1 -0
  19. package/dist/plugins/esbuild.d.mts +5 -0
  20. package/dist/plugins/esbuild.mjs +6 -0
  21. package/dist/plugins/esbuild.mjs.map +1 -0
  22. package/dist/plugins/jest.d.mts +10 -0
  23. package/dist/plugins/jest.mjs +29 -0
  24. package/dist/plugins/jest.mjs.map +1 -0
  25. package/dist/plugins/loader.d.mts +15 -0
  26. package/dist/plugins/loader.mjs +21 -0
  27. package/dist/plugins/loader.mjs.map +1 -0
  28. package/dist/plugins/next.d.mts +21 -0
  29. package/dist/plugins/next.mjs +121 -0
  30. package/dist/plugins/next.mjs.map +1 -0
  31. package/dist/plugins/rolldown.d.mts +5 -0
  32. package/dist/plugins/rolldown.mjs +6 -0
  33. package/dist/plugins/rolldown.mjs.map +1 -0
  34. package/dist/plugins/rollup.d.mts +5 -0
  35. package/dist/plugins/rollup.mjs +6 -0
  36. package/dist/plugins/rollup.mjs.map +1 -0
  37. package/dist/plugins/rsbuild.d.mts +5 -0
  38. package/dist/plugins/rsbuild.mjs +6 -0
  39. package/dist/plugins/rsbuild.mjs.map +1 -0
  40. package/dist/plugins/rspack.d.mts +5 -0
  41. package/dist/plugins/rspack.mjs +6 -0
  42. package/dist/plugins/rspack.mjs.map +1 -0
  43. package/dist/plugins/vite.d.mts +5 -0
  44. package/dist/plugins/vite.mjs +6 -0
  45. package/dist/plugins/vite.mjs.map +1 -0
  46. package/dist/plugins/webpack.d.mts +5 -0
  47. package/dist/plugins/webpack.mjs +6 -0
  48. package/dist/plugins/webpack.mjs.map +1 -0
  49. package/dist/register.d.mts +1 -0
  50. package/dist/register.mjs +25 -0
  51. package/dist/register.mjs.map +1 -0
  52. package/dist/resolve-B7e2uGDa.mjs +308 -0
  53. package/dist/resolve-B7e2uGDa.mjs.map +1 -0
  54. package/dist/{resolve-Oj4qvq9_.d.mts → resolve-BUtCDolX.d.mts} +16 -3
  55. package/dist/resolve-hzoKURuJ.mjs +72 -0
  56. package/dist/resolve-hzoKURuJ.mjs.map +1 -0
  57. package/dist/runtime/index.d.mts +3 -45
  58. package/dist/runtime/index.mjs +4 -98
  59. package/dist/runtime-la_KkjCS.mjs +1 -0
  60. package/dist/schema-C_damFGy.mjs +90 -0
  61. package/dist/schema-C_damFGy.mjs.map +1 -0
  62. package/dist/schema-CkQRd2Uf.d.mts +43 -0
  63. package/dist/shared-BuRtKsWB.mjs +16 -0
  64. package/dist/shared-BuRtKsWB.mjs.map +1 -0
  65. package/dist/shared-CLa0Be_8.d.mts +22 -0
  66. package/dist/sync-DUNHC9W6.mjs +14 -0
  67. package/dist/sync-DUNHC9W6.mjs.map +1 -0
  68. package/dist/unplugin-DXVarwkX.mjs +69 -0
  69. package/dist/unplugin-DXVarwkX.mjs.map +1 -0
  70. package/dist/unplugin-ce5g3K3a.d.mts +2 -0
  71. package/package.json +117 -2
  72. package/dist/resolve-wE3GPIQp.mjs.map +0 -1
  73. package/dist/runtime/index.mjs.map +0 -1
package/dist/bun.d.mts ADDED
@@ -0,0 +1,7 @@
1
+ import { BunPlugin } from "bun";
2
+ /**
3
+ * The tyfigo Bun plugin. Importing `tyfigo/bun` (a `bunfig.toml` `preload`) registers it for the runtime and `bun test`; pass
4
+ * `tyfigo()` in `plugins` to use it with `Bun.build`, where a `browser` target inlines the config and a server target inlines the config files and reads `.env` files when it runs.
5
+ */
6
+ export declare const tyfigo: () => BunPlugin;
7
+ //# sourceMappingURL=bun.d.mts.map
package/dist/bun.mjs ADDED
@@ -0,0 +1,35 @@
1
+ import { i as configSuffixes, u as formatProblems } from "./placeholders-MqW2KO9Z.mjs";
2
+ import { t as load } from "./load-BqGJgpfo.mjs";
3
+ import { extname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { plugin } from "bun";
6
+ // src/bun.ts
7
+ const runtime = fileURLToPath(new URL(`runtime/index${extname(import.meta.url)}`, import.meta.url));
8
+ const filter = new RegExp(`[^/\\\\](?:${configSuffixes.map((suffix) => suffix.replaceAll(".", "\\.")).join("|")})$`);
9
+ /**
10
+ * The tyfigo Bun plugin. Importing `tyfigo/bun` (a `bunfig.toml` `preload`) registers it for the runtime and `bun test`; pass
11
+ * `tyfigo()` in `plugins` to use it with `Bun.build`, where a `browser` target inlines the config and a server target inlines the config files and reads `.env` files when it runs.
12
+ */
13
+ const tyfigo = () => ({
14
+ name: "tyfigo",
15
+ setup(build) {
16
+ const config = build.config;
17
+ const target = config?.target === "browser" ? "browser" : "server";
18
+ build.onLoad({ filter }, ({ path }) => {
19
+ const result = load(path, {
20
+ target,
21
+ runtime,
22
+ bundle: config !== void 0
23
+ });
24
+ if (result.problems.length > 0) throw new Error(`Invalid config ${path}\n${formatProblems(result.problems)}`);
25
+ return {
26
+ contents: result.code,
27
+ loader: "js"
28
+ };
29
+ });
30
+ }
31
+ });
32
+ plugin(tyfigo());
33
+ export { tyfigo };
34
+
35
+ //# sourceMappingURL=bun.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.mjs","names":[],"sources":["../src/bun.ts"],"sourcesContent":["import { extname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { plugin } from 'bun';\n\nimport { configSuffixes, formatProblems } from './core/index.ts';\nimport { load } from './runtime/index.ts';\n\nimport type { BunPlugin } from 'bun';\n\n// The runtime next to this module, so it resolves from config files outside the dependency tree: `.ts` in this repo, `.mjs` once built.\nconst runtime = fileURLToPath(new URL(`runtime/index${extname(import.meta.url)}`, import.meta.url));\n\nconst filter = new RegExp(`[^/\\\\\\\\](?:${configSuffixes.map((suffix) => suffix.replaceAll('.', '\\\\.')).join('|')})$`);\n\n/**\n * The tyfigo Bun plugin. Importing `tyfigo/bun` (a `bunfig.toml` `preload`) registers it for the runtime and `bun test`; pass\n * `tyfigo()` in `plugins` to use it with `Bun.build`, where a `browser` target inlines the config and a server target inlines the config files and reads `.env` files when it runs.\n */\nexport const tyfigo = (): BunPlugin => ({\n\tname: 'tyfigo',\n\tsetup(build) {\n\t\tconst config = build.config as typeof build.config | undefined;\n\t\tconst target = config?.target === 'browser' ? 'browser' : 'server';\n\t\tbuild.onLoad({ filter }, ({ path }) => {\n\t\t\tconst result = load(path, { target, runtime, bundle: config !== undefined });\n\t\t\tif (result.problems.length > 0) {\n\t\t\t\tthrow new Error(`Invalid config ${path}\\n${formatProblems(result.problems)}`);\n\t\t\t}\n\t\t\treturn { contents: result.code, loader: 'js' };\n\t\t});\n\t},\n});\n\nvoid plugin(tyfigo());\n"],"mappings":";;;;;;AAWA,MAAM,UAAU,cAAc,IAAI,IAAI,gBAAgB,QAAQ,YAAY,GAAG,KAAK,YAAY,GAAG,CAAC;AAElG,MAAM,SAAS,IAAI,OAAO,cAAc,eAAe,KAAK,WAAW,OAAO,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG;;;;;AAMnH,MAAa,gBAA2B;CACvC,MAAM;CACN,MAAM,OAAO;EACZ,MAAM,SAAS,MAAM;EACrB,MAAM,SAAS,QAAQ,WAAW,YAAY,YAAY;EAC1D,MAAM,OAAO,EAAE,OAAO,IAAI,EAAE,WAAW;GACtC,MAAM,SAAS,KAAK,MAAM;IAAE;IAAQ;IAAS,QAAQ,WAAW,KAAA;GAAU,CAAC;GAC3E,IAAI,OAAO,SAAS,SAAS,GAC5B,MAAM,IAAI,MAAM,kBAAkB,KAAK,IAAI,eAAe,OAAO,QAAQ,GAAG;GAE7E,OAAO;IAAE,UAAU,OAAO;IAAM,QAAQ;GAAK;EAC9C,CAAC;CACF;AACD;AAEK,OAAO,OAAO,CAAC"}
package/dist/effect.d.mts CHANGED
@@ -1,2 +1,80 @@
1
- import { a as Problem, c as Span, i as LocatedSpan, n as ResolvedConfig, o as ProblemCode, r as ResolvedLayer, s as Path, t as ResolveOptions } from "./index-_5Tr2XH2.mjs";
2
- export type { LocatedSpan, Path, Problem, ProblemCode, ResolveOptions, ResolvedConfig, ResolvedLayer, Span };
1
+ import { a as LocatedSpan, c as Path, i as ResolvedLayer, l as Span, n as ResolveOptions, o as Problem, r as ResolvedConfig, s as ProblemCode } from "./index-CM4xg0II.mjs";
2
+ import { i as ConfigSource } from "./resolve-BUtCDolX.mjs";
3
+ import { a as LooseInput, i as LoadEnvOptions, n as ConfigSchema, o as SchemaInput, r as ConfigValidationError, s as SchemaOutput, t as ConfigIssue } from "./schema-CkQRd2Uf.mjs";
4
+ import { ConfigProvider, Context, Effect, Layer, Schema } from "effect";
5
+ import { StandardSchemaV1 } from "@standard-schema/spec";
6
+ /**
7
+ * A Standard Schema or any Effect Schema, including one that needs services to decode.
8
+ */
9
+ export type EffectConfigSchema = StandardSchemaV1 | Schema.Top;
10
+ /**
11
+ * The services an Effect Schema needs to decode, or `never` for a Standard Schema.
12
+ */
13
+ export type SchemaServices<S> = S extends StandardSchemaV1 ? never : S extends Schema.Top ? S['DecodingServices'] : never;
14
+ /**
15
+ * The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not
16
+ * be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.
17
+ */
18
+ export declare const ConfigError: typeof ConfigValidationError;
19
+ export type ConfigError = ConfigValidationError;
20
+ /**
21
+ * Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by
22
+ * Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.
23
+ */
24
+ export declare const loadConfig: <S extends EffectConfigSchema>(config: LooseInput<SchemaInput<S>>, schema: S) => Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>>;
25
+ /**
26
+ * Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.
27
+ * Never writes `process.env`.
28
+ */
29
+ export declare const loadEnv: <S extends EffectConfigSchema>(schema: S, options?: LoadEnvOptions) => Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>>;
30
+ /**
31
+ * A layer that provides `service` with the validated config.
32
+ */
33
+ export declare const layerConfig: <I, S extends EffectConfigSchema>(service: Context.Key<I, SchemaOutput<S>>, config: LooseInput<SchemaInput<S>>, schema: S) => Layer.Layer<I, ConfigError, SchemaServices<S>>;
34
+ /**
35
+ * A layer that provides `service` with the validated environment variables.
36
+ */
37
+ export declare const layerEnv: <I, S extends EffectConfigSchema>(service: Context.Key<I, SchemaOutput<S>>, schema: S, options?: LoadEnvOptions) => Layer.Layer<I, ConfigError, SchemaServices<S>>;
38
+ /**
39
+ * Defines a service class for a validated config, with a `layer` that provides it.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}
44
+ *
45
+ * const program = Effect.gen(function* () {
46
+ * const { port } = yield* AppConfig;
47
+ * }).pipe(Effect.provide(AppConfig.layer));
48
+ * ```
49
+ */
50
+ export declare const ConfigService: <Self>() => <const Identifier extends string, S extends EffectConfigSchema>(id: Identifier, config: LooseInput<SchemaInput<S>>, schema: S) => Context.ServiceClass<Self, Identifier, SchemaOutput<S>> & {
51
+ layer: Layer.Layer<Self, ConfigValidationError, Exclude<SchemaServices<S>, import("effect/Scope").Scope>>;
52
+ };
53
+ export interface ConfigProviderLayerOptions {
54
+ /**
55
+ * Whether the config files win over the provider already installed (by default the environment variables). Defaults to
56
+ * `true`; with `false` the config files only fill in paths the current provider does not have.
57
+ */
58
+ readonly asPrimary?: boolean | undefined;
59
+ }
60
+ /**
61
+ * A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are
62
+ * numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.
63
+ */
64
+ export declare const configProvider: (config: unknown) => ConfigProvider.ConfigProvider;
65
+ /**
66
+ * Resolves a config file with its layers, `.env` files and placeholders, like a config import does, and returns a
67
+ * `ConfigProvider` over the merged value.
68
+ */
69
+ export declare const configProviderFromFile: (file: string, options?: ResolveOptions | undefined) => Effect.Effect<ConfigProvider.ConfigProvider, ConfigValidationError, never>;
70
+ /**
71
+ * Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the
72
+ * environment variables for paths the config does not have.
73
+ */
74
+ export declare const layerConfigProvider: (config: unknown, options?: ConfigProviderLayerOptions) => Layer.Layer<never>;
75
+ /**
76
+ * Installs {@link configProviderFromFile} on top of the current `ConfigProvider`.
77
+ */
78
+ export declare const layerConfigProviderFromFile: (file: string, options?: ResolveOptions & ConfigProviderLayerOptions) => Layer.Layer<never, ConfigError>;
79
+ export { type ConfigIssue, type ConfigSchema, type ConfigSource, ConfigValidationError, type LoadEnvOptions, type LocatedSpan, type LooseInput, type Path, type Problem, type ProblemCode, type ResolveOptions, type ResolvedConfig, type ResolvedLayer, type SchemaInput, type SchemaOutput, type Span };
80
+ //# sourceMappingURL=effect.d.mts.map
package/dist/effect.mjs CHANGED
@@ -1 +1,84 @@
1
- export {};
1
+ import { t as resolveConfig } from "./resolve-B7e2uGDa.mjs";
2
+ import { n as decodeConfig, r as decodeEnv, t as ConfigValidationError } from "./schema-C_damFGy.mjs";
3
+ import { ConfigProvider, Context, Effect, Layer } from "effect";
4
+ // src/effect/load.ts
5
+ /**
6
+ * The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not
7
+ * be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.
8
+ */
9
+ const ConfigError = ConfigValidationError;
10
+ /**
11
+ * Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by
12
+ * Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.
13
+ */
14
+ const loadConfig = (config, schema) => decodeConfig(config, schema, {
15
+ async: true,
16
+ native: true
17
+ });
18
+ /**
19
+ * Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.
20
+ * Never writes `process.env`.
21
+ */
22
+ const loadEnv = (schema, options = {}) => decodeEnv(schema, {
23
+ ...options,
24
+ async: true,
25
+ native: true
26
+ });
27
+ /**
28
+ * A layer that provides `service` with the validated config.
29
+ */
30
+ const layerConfig = (service, config, schema) => Layer.effect(service, loadConfig(config, schema));
31
+ /**
32
+ * A layer that provides `service` with the validated environment variables.
33
+ */
34
+ const layerEnv = (service, schema, options = {}) => Layer.effect(service, loadEnv(schema, options));
35
+ /**
36
+ * Defines a service class for a validated config, with a `layer` that provides it.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}
41
+ *
42
+ * const program = Effect.gen(function* () {
43
+ * const { port } = yield* AppConfig;
44
+ * }).pipe(Effect.provide(AppConfig.layer));
45
+ * ```
46
+ */
47
+ const ConfigService = () => (id, config, schema) => {
48
+ const service = Context.Service()(id);
49
+ return Object.assign(service, { layer: Layer.effect(service, loadConfig(config, schema)) });
50
+ };
51
+ // src/effect/provider.ts
52
+ /**
53
+ * A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are
54
+ * numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.
55
+ */
56
+ const configProvider = (config) => ConfigProvider.fromUnknown(config, { preserveEmptyStrings: true });
57
+ /**
58
+ * Resolves a config file with its layers, `.env` files and placeholders, like a config import does, and returns a
59
+ * `ConfigProvider` over the merged value.
60
+ */
61
+ const configProviderFromFile = Effect.fn("configProviderFromFile")(function* (file, options = {}) {
62
+ const result = yield* resolveConfig(file, options);
63
+ if (result.problems.length > 0) return yield* new ConfigValidationError({
64
+ subject: `config ${result.file}`,
65
+ issues: result.problems.map((problem) => ({
66
+ path: [],
67
+ message: `${problem.message} (${problem.code})`,
68
+ location: `${problem.file}:${problem.line}:${problem.column}`
69
+ }))
70
+ });
71
+ return configProvider(result.value);
72
+ });
73
+ /**
74
+ * Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the
75
+ * environment variables for paths the config does not have.
76
+ */
77
+ const layerConfigProvider = (config, options = {}) => ConfigProvider.layerAdd(configProvider(config), { asPrimary: options.asPrimary ?? true });
78
+ /**
79
+ * Installs {@link configProviderFromFile} on top of the current `ConfigProvider`.
80
+ */
81
+ const layerConfigProviderFromFile = (file, options = {}) => ConfigProvider.layerAdd(configProviderFromFile(file, options), { asPrimary: options.asPrimary ?? true });
82
+ export { ConfigError, ConfigService, ConfigValidationError, configProvider, configProviderFromFile, layerConfig, layerConfigProvider, layerConfigProviderFromFile, layerEnv, loadConfig, loadEnv };
83
+
84
+ //# sourceMappingURL=effect.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect.mjs","names":[],"sources":["../src/effect/load.ts","../src/effect/provider.ts"],"sourcesContent":["import { Context, Layer } from 'effect';\n\nimport { ConfigValidationError, decodeConfig, decodeEnv } from '../schema.ts';\n\nimport type { ConfigSchema, LoadEnvOptions, LooseInput, SchemaInput, SchemaOutput } from '../schema.ts';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Effect, Schema } from 'effect';\n\n/**\n * A Standard Schema or any Effect Schema, including one that needs services to decode.\n */\nexport type EffectConfigSchema = StandardSchemaV1 | Schema.Top;\n\n/**\n * The services an Effect Schema needs to decode, or `never` for a Standard Schema.\n */\nexport type SchemaServices<S> = S extends StandardSchemaV1\n\t? never\n\t: S extends Schema.Top\n\t\t? S['DecodingServices']\n\t\t: never;\n\n/**\n * The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not\n * be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.\n */\nexport const ConfigError = ConfigValidationError;\nexport type ConfigError = ConfigValidationError;\n\n/**\n * Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by\n * Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.\n */\nexport const loadConfig = <S extends EffectConfigSchema>(\n\tconfig: LooseInput<SchemaInput<S>>,\n\tschema: S,\n): Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>> =>\n\tdecodeConfig(config, schema as ConfigSchema, { async: true, native: true }) as Effect.Effect<\n\t\tSchemaOutput<S>,\n\t\tConfigError,\n\t\tSchemaServices<S>\n\t>;\n\n/**\n * Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.\n * Never writes `process.env`.\n */\nexport const loadEnv = <S extends EffectConfigSchema>(\n\tschema: S,\n\toptions: LoadEnvOptions = {},\n): Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>> =>\n\tdecodeEnv(schema as ConfigSchema, { ...options, async: true, native: true }) as Effect.Effect<\n\t\tSchemaOutput<S>,\n\t\tConfigError,\n\t\tSchemaServices<S>\n\t>;\n\n/**\n * A layer that provides `service` with the validated config.\n */\nexport const layerConfig = <I, S extends EffectConfigSchema>(\n\tservice: Context.Key<I, SchemaOutput<S>>,\n\tconfig: LooseInput<SchemaInput<S>>,\n\tschema: S,\n): Layer.Layer<I, ConfigError, SchemaServices<S>> => Layer.effect(service, loadConfig(config, schema));\n\n/**\n * A layer that provides `service` with the validated environment variables.\n */\nexport const layerEnv = <I, S extends EffectConfigSchema>(\n\tservice: Context.Key<I, SchemaOutput<S>>,\n\tschema: S,\n\toptions: LoadEnvOptions = {},\n): Layer.Layer<I, ConfigError, SchemaServices<S>> => Layer.effect(service, loadEnv(schema, options));\n\n/**\n * Defines a service class for a validated config, with a `layer` that provides it.\n *\n * @example\n * ```ts\n * class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}\n *\n * const program = Effect.gen(function* () {\n * \tconst { port } = yield* AppConfig;\n * }).pipe(Effect.provide(AppConfig.layer));\n * ```\n */\nexport const ConfigService =\n\t<Self>() =>\n\t<const Identifier extends string, S extends EffectConfigSchema>(\n\t\tid: Identifier,\n\t\tconfig: LooseInput<SchemaInput<S>>,\n\t\tschema: S,\n\t) => {\n\t\tconst service = Context.Service<Self, SchemaOutput<S>>()(id);\n\t\treturn Object.assign(service, {\n\t\t\tlayer: Layer.effect(service, loadConfig(config, schema)),\n\t\t});\n\t};\n","import { ConfigProvider, Effect } from 'effect';\n\nimport { resolveConfig } from '../core/resolve.ts';\nimport { ConfigValidationError } from '../schema.ts';\n\nimport type { ResolveOptions } from '../core/resolve.ts';\nimport type { ConfigError } from './load.ts';\nimport type { Layer } from 'effect';\n\nexport interface ConfigProviderLayerOptions {\n\t/**\n\t * Whether the config files win over the provider already installed (by default the environment variables). Defaults to\n\t * `true`; with `false` the config files only fill in paths the current provider does not have.\n\t */\n\treadonly asPrimary?: boolean | undefined;\n}\n\n/**\n * A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are\n * numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.\n */\nexport const configProvider = (config: unknown): ConfigProvider.ConfigProvider =>\n\tConfigProvider.fromUnknown(config, { preserveEmptyStrings: true });\n\n/**\n * Resolves a config file with its layers, `.env` files and placeholders, like a config import does, and returns a\n * `ConfigProvider` over the merged value.\n */\nexport const configProviderFromFile = Effect.fn('configProviderFromFile')(function* (\n\tfile: string,\n\toptions: ResolveOptions = {},\n): Effect.fn.Return<ConfigProvider.ConfigProvider, ConfigError> {\n\tconst result = yield* resolveConfig(file, options);\n\tif (result.problems.length > 0) {\n\t\treturn yield* new ConfigValidationError({\n\t\t\tsubject: `config ${result.file}`,\n\t\t\tissues: result.problems.map((problem) => ({\n\t\t\t\tpath: [],\n\t\t\t\tmessage: `${problem.message} (${problem.code})`,\n\t\t\t\tlocation: `${problem.file}:${problem.line}:${problem.column}`,\n\t\t\t})),\n\t\t});\n\t}\n\treturn configProvider(result.value);\n});\n\n/**\n * Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the\n * environment variables for paths the config does not have.\n */\nexport const layerConfigProvider = (config: unknown, options: ConfigProviderLayerOptions = {}): Layer.Layer<never> =>\n\tConfigProvider.layerAdd(configProvider(config), { asPrimary: options.asPrimary ?? true });\n\n/**\n * Installs {@link configProviderFromFile} on top of the current `ConfigProvider`.\n */\nexport const layerConfigProviderFromFile = (\n\tfile: string,\n\toptions: ResolveOptions & ConfigProviderLayerOptions = {},\n): Layer.Layer<never, ConfigError> =>\n\tConfigProvider.layerAdd(configProviderFromFile(file, options), { asPrimary: options.asPrimary ?? true });\n"],"mappings":";;;;;;;;AA0BA,MAAa,cAAc;;;;;AAO3B,MAAa,cACZ,QACA,WAEA,aAAa,QAAQ,QAAwB;CAAE,OAAO;CAAM,QAAQ;AAAK,CAAC;;;;;AAU3E,MAAa,WACZ,QACA,UAA0B,CAAC,MAE3B,UAAU,QAAwB;CAAE,GAAG;CAAS,OAAO;CAAM,QAAQ;AAAK,CAAC;;;;AAS5E,MAAa,eACZ,SACA,QACA,WACoD,MAAM,OAAO,SAAS,WAAW,QAAQ,MAAM,CAAC;;;;AAKrG,MAAa,YACZ,SACA,QACA,UAA0B,CAAC,MACyB,MAAM,OAAO,SAAS,QAAQ,QAAQ,OAAO,CAAC;;;;;;;;;;;;;AAcnG,MAAa,uBAGX,IACA,QACA,WACI;CACJ,MAAM,UAAU,QAAQ,QAA+B,CAAC,CAAC,EAAE;CAC3D,OAAO,OAAO,OAAO,SAAS,EAC7B,OAAO,MAAM,OAAO,SAAS,WAAW,QAAQ,MAAM,CAAC,EACxD,CAAC;AACF;;;;;;AC7ED,MAAa,kBAAkB,WAC9B,eAAe,YAAY,QAAQ,EAAE,sBAAsB,KAAK,CAAC;;;;;AAMlE,MAAa,yBAAyB,OAAO,GAAG,wBAAwB,CAAC,CAAC,WACzE,MACA,UAA0B,CAAC,GACoC;CAC/D,MAAM,SAAS,OAAO,cAAc,MAAM,OAAO;CACjD,IAAI,OAAO,SAAS,SAAS,GAC5B,OAAO,OAAO,IAAI,sBAAsB;EACvC,SAAS,UAAU,OAAO;EAC1B,QAAQ,OAAO,SAAS,KAAK,aAAa;GACzC,MAAM,CAAC;GACP,SAAS,GAAG,QAAQ,QAAQ,IAAI,QAAQ,KAAK;GAC7C,UAAU,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK,GAAG,QAAQ;EACtD,EAAE;CACH,CAAC;CAEF,OAAO,eAAe,OAAO,KAAK;AACnC,CAAC;;;;;AAMD,MAAa,uBAAuB,QAAiB,UAAsC,CAAC,MAC3F,eAAe,SAAS,eAAe,MAAM,GAAG,EAAE,WAAW,QAAQ,aAAa,KAAK,CAAC;;;;AAKzF,MAAa,+BACZ,MACA,UAAuD,CAAC,MAExD,eAAe,SAAS,uBAAuB,MAAM,OAAO,GAAG,EAAE,WAAW,QAAQ,aAAa,KAAK,CAAC"}
@@ -53,6 +53,10 @@ interface Format {
53
53
  readonly parseScalar: (text: string) => unknown;
54
54
  }
55
55
  type LayerKind = 'base' | 'environment' | 'local' | 'environment-local';
56
+ interface LayerFile {
57
+ readonly file: string;
58
+ readonly kind: LayerKind;
59
+ }
56
60
  /**
57
61
  * A {@link Span} together with the file it is in.
58
62
  */
@@ -102,5 +106,8 @@ interface ResolvedConfig {
102
106
  readonly positions: ReadonlyMap<string, LocatedSpan>;
103
107
  readonly problems: readonly Problem[];
104
108
  }
105
- export { Problem as a, Span as c, LocatedSpan as i, ResolvedConfig as n, ProblemCode as o, ResolvedLayer as r, Path as s, ResolveOptions as t };
106
- //# sourceMappingURL=index-_5Tr2XH2.d.mts.map
109
+ interface LayerSource extends LayerFile {
110
+ readonly text: string;
111
+ }
112
+ export { LocatedSpan as a, Path as c, ResolvedLayer as i, Span as l, ResolveOptions as n, Problem as o, ResolvedConfig as r, ProblemCode as s, LayerSource as t };
113
+ //# sourceMappingURL=index-CM4xg0II.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,44 +1,6 @@
1
- import { a as Problem, c as Span, i as LocatedSpan, n as ResolvedConfig, o as ProblemCode, r as ResolvedLayer, s as Path, t as ResolveOptions } from "./index-_5Tr2XH2.mjs";
2
- import { r as ConfigSource } from "./resolve-Oj4qvq9_.mjs";
3
- import { Effect, Schema } from "effect";
4
- import { StandardSchemaV1 } from "@standard-schema/spec";
5
- /**
6
- * A Standard Schema (Zod, Valibot, ArkType, …) or an Effect Schema without decoding services.
7
- */
8
- type ConfigSchema = StandardSchemaV1 | Schema.ConstraintDecoder<unknown>;
9
- type SchemaInput<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferInput<S> : S extends Schema.Top ? S['Encoded'] : never;
10
- type SchemaOutput<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : S extends Schema.Top ? S['Type'] : never;
11
- /**
12
- * A schema input with literals widened and everything readonly, so a schema-less config import is assignable to it.
13
- */
14
- type LooseInput<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends readonly (infer U)[] ? readonly LooseInput<U>[] : T extends object ? { readonly [K in keyof T]: LooseInput<T[K]>; } : T;
15
- interface ConfigIssue {
16
- readonly path: readonly PropertyKey[];
17
- readonly message: string;
18
- /**
19
- * `file:line:column` when the value came from a config import, or the file alone when the key has no position.
20
- */
21
- readonly location?: string | undefined;
22
- }
23
- declare const ConfigValidationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
24
- readonly _tag: "ConfigValidationError";
25
- } & Readonly<A>;
26
- /**
27
- * Thrown with every issue at once when a config or the environment does not match its schema.
28
- */
29
- export declare class ConfigValidationError extends ConfigValidationError_base<{
30
- readonly subject: string;
31
- readonly issues: readonly ConfigIssue[];
32
- }> {
33
- get message(): string;
34
- }
35
- interface LoadEnvOptions extends ResolveOptions {
36
- /**
37
- * Where to start looking for `.env` files, which are searched there and then in the nearest directory with a
38
- * `package.json`. Defaults to `process.cwd()`.
39
- */
40
- readonly cwd?: string | undefined;
41
- }
1
+ import { a as LocatedSpan, c as Path, i as ResolvedLayer, l as Span, n as ResolveOptions, o as Problem, r as ResolvedConfig, s as ProblemCode } from "./index-CM4xg0II.mjs";
2
+ import { i as ConfigSource } from "./resolve-BUtCDolX.mjs";
3
+ import { a as LooseInput, i as LoadEnvOptions, n as ConfigSchema, o as SchemaInput, r as ConfigValidationError, s as SchemaOutput, t as ConfigIssue } from "./schema-CkQRd2Uf.mjs";
42
4
  /**
43
5
  * Validates a config import (or any value) with a Standard Schema or an Effect Schema and returns the schema's output.
44
6
  *
@@ -62,5 +24,5 @@ export declare const loadEnv: <S extends ConfigSchema>(schema: S, options?: Load
62
24
  * {@link loadEnv} for schemas that may validate asynchronously.
63
25
  */
64
26
  export declare const loadEnvAsync: <S extends ConfigSchema>(schema: S, options?: LoadEnvOptions) => Promise<SchemaOutput<S>>;
65
- export type { ConfigIssue, ConfigSchema, ConfigSource, LoadEnvOptions, LocatedSpan, LooseInput, Path, Problem, ProblemCode, ResolveOptions, ResolvedConfig, ResolvedLayer, SchemaInput, SchemaOutput, Span };
27
+ export { type ConfigIssue, type ConfigSchema, type ConfigSource, ConfigValidationError, type LoadEnvOptions, type LocatedSpan, type LooseInput, type Path, type Problem, type ProblemCode, type ResolveOptions, type ResolvedConfig, type ResolvedLayer, type SchemaInput, type SchemaOutput, type Span };
66
28
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,81 +1,5 @@
1
- import { f as formatPath, g as readEnv, h as processEnv, i as getConfigSource } from "./resolve-wE3GPIQp.mjs";
2
- import { Data, Effect, Schema } from "effect";
3
- import { join } from "node:path";
4
- import process from "node:process";
5
- // src/schema.ts
6
- const formatIssue = (issue) => {
7
- const path = formatPath(issue.path.map((segment) => typeof segment === "symbol" ? String(segment) : segment));
8
- const text = path ? `${path}: ${issue.message}` : issue.message;
9
- return issue.location ? `${issue.location} - ${text}` : text;
10
- };
11
- /**
12
- * Thrown with every issue at once when a config or the environment does not match its schema.
13
- */
14
- var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
15
- get message() {
16
- return [`Invalid ${this.subject}:`, ...this.issues.map((issue) => ` ${formatIssue(issue)}`)].join("\n");
17
- }
18
- };
19
- const configSource = (config) => {
20
- const source = getConfigSource(config);
21
- return typeof source?.file === "string" && typeof source.position === "function" ? source : void 0;
22
- };
23
- const locateIssue = (source, path) => {
24
- const end = path.findIndex((segment) => typeof segment === "symbol");
25
- const keys = end === -1 ? path : path.slice(0, end);
26
- const position = source.position(keys);
27
- return position ? `${position.file}:${position.line}:${position.column}` : source.file;
28
- };
29
- const toStandardSchema = (schema) => Schema.isSchema(schema) ? Schema.toStandardSchemaV1(schema, { parseOptions: { errors: "all" } }) : schema;
30
- const validate = Effect.fn("validate")(function* (schema, value, options) {
31
- const pending = toStandardSchema(schema)["~standard"].validate(value);
32
- let result;
33
- if ("then" in pending) {
34
- if (!options.async) {
35
- pending.catch(() => void 0);
36
- return yield* Effect.die(/* @__PURE__ */ new TypeError(`The schema for the ${options.subject} validates asynchronously; use loadConfigAsync or loadEnvAsync`));
37
- }
38
- result = yield* Effect.promise(async () => pending);
39
- } else result = pending;
40
- const issues = [...options.issues ?? [], ...(result.issues ?? []).map((issue) => {
41
- const path = Array.from(issue.path ?? [], (segment) => typeof segment === "object" ? segment.key : segment);
42
- return {
43
- path,
44
- message: issue.message,
45
- location: options.source && locateIssue(options.source, path)
46
- };
47
- })];
48
- if (issues.length > 0) return yield* new ConfigValidationError({
49
- subject: options.subject,
50
- issues
51
- });
52
- return result.value;
53
- });
54
- const decodeConfig = (config, schema, options) => {
55
- const source = configSource(config);
56
- return validate(schema, config, {
57
- subject: source ? `config ${source.file}` : "config",
58
- source,
59
- async: options.async
60
- });
61
- };
62
- const decodeEnv = Effect.fn("decodeEnv")(function* (schema, options) {
63
- const env = options.env ?? processEnv();
64
- const environment = options.environment ?? (env.NODE_ENV || void 0);
65
- const result = yield* readEnv(join(options.cwd ?? process.cwd(), ".env"), {
66
- environment,
67
- env
68
- });
69
- return yield* validate(schema, result.variables, {
70
- subject: "environment variables",
71
- async: options.async,
72
- issues: result.problems.map((problem) => ({
73
- path: [],
74
- message: problem.message,
75
- location: `${problem.file}:${problem.line}:${problem.column}`
76
- }))
77
- });
78
- });
1
+ import { n as decodeConfig, r as decodeEnv, t as ConfigValidationError } from "./schema-C_damFGy.mjs";
2
+ import { Effect } from "effect";
79
3
  // src/index.ts
80
4
  /**
81
5
  * Validates a config import (or any value) with a Standard Schema or an Effect Schema and returns the schema's output.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/schema.ts","../src/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport process from 'node:process';\n\nimport { Data, Effect, Schema } from 'effect';\n\nimport { processEnv, readEnv } from './core/env.ts';\nimport { formatPath } from './core/positions.ts';\nimport { getConfigSource } from './runtime/resolve.ts';\n\nimport type { ResolveOptions } from './core/resolve.ts';\nimport type { ConfigSource } from './runtime/resolve.ts';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\n/**\n * A Standard Schema (Zod, Valibot, ArkType, …) or an Effect Schema without decoding services.\n */\nexport type ConfigSchema = StandardSchemaV1 | Schema.ConstraintDecoder<unknown>;\n\nexport type SchemaInput<S> = S extends StandardSchemaV1\n\t? StandardSchemaV1.InferInput<S>\n\t: S extends Schema.Top\n\t\t? S['Encoded']\n\t\t: never;\n\nexport type SchemaOutput<S> = S extends StandardSchemaV1\n\t? StandardSchemaV1.InferOutput<S>\n\t: S extends Schema.Top\n\t\t? S['Type']\n\t\t: never;\n\n/**\n * A schema input with literals widened and everything readonly, so a schema-less config import is assignable to it.\n */\nexport type LooseInput<T> = T extends string\n\t? string\n\t: T extends number\n\t\t? number\n\t\t: T extends boolean\n\t\t\t? boolean\n\t\t\t: T extends readonly (infer U)[]\n\t\t\t\t? readonly LooseInput<U>[]\n\t\t\t\t: T extends object\n\t\t\t\t\t? { readonly [K in keyof T]: LooseInput<T[K]> }\n\t\t\t\t\t: T;\n\nexport interface ConfigIssue {\n\treadonly path: readonly PropertyKey[];\n\treadonly message: string;\n\t/**\n\t * `file:line:column` when the value came from a config import, or the file alone when the key has no position.\n\t */\n\treadonly location?: string | undefined;\n}\n\nconst formatIssue = (issue: ConfigIssue): string => {\n\tconst path = formatPath(issue.path.map((segment) => (typeof segment === 'symbol' ? String(segment) : segment)));\n\tconst text = path ? `${path}: ${issue.message}` : issue.message;\n\treturn issue.location ? `${issue.location} - ${text}` : text;\n};\n\n/**\n * Thrown with every issue at once when a config or the environment does not match its schema.\n */\nexport class ConfigValidationError extends Data.TaggedError('ConfigValidationError')<{\n\treadonly subject: string;\n\treadonly issues: readonly ConfigIssue[];\n}> {\n\toverride get message(): string {\n\t\treturn [`Invalid ${this.subject}:`, ...this.issues.map((issue) => ` ${formatIssue(issue)}`)].join('\\n');\n\t}\n}\n\nexport type { ConfigSource } from './runtime/resolve.ts';\n\nexport const configSource = (config: unknown): ConfigSource | undefined => {\n\tconst source = getConfigSource(config);\n\treturn typeof source?.file === 'string' && typeof source.position === 'function' ? source : undefined;\n};\n\nconst locateIssue = (source: ConfigSource, path: readonly PropertyKey[]): string => {\n\tconst end = path.findIndex((segment) => typeof segment === 'symbol');\n\tconst keys = (end === -1 ? path : path.slice(0, end)) as readonly (string | number)[];\n\tconst position = source.position(keys);\n\treturn position ? `${position.file}:${position.line}:${position.column}` : source.file;\n};\n\nexport const toStandardSchema = (schema: ConfigSchema): StandardSchemaV1 =>\n\tSchema.isSchema(schema)\n\t\t? Schema.toStandardSchemaV1(schema as Schema.ConstraintDecoder<unknown>, { parseOptions: { errors: 'all' } })\n\t\t: (schema as StandardSchemaV1);\n\nexport interface ValidateOptions {\n\treadonly subject: string;\n\treadonly source?: ConfigSource | undefined;\n\t/**\n\t * Allow a schema that validates asynchronously. Without it, an async schema is a defect.\n\t */\n\treadonly async: boolean;\n\t/**\n\t * Issues found before validation, such as unreadable `.env` files. They fail validation even when the schema passes.\n\t */\n\treadonly issues?: readonly ConfigIssue[] | undefined;\n}\n\nexport const validate = Effect.fn('validate')(function* <S extends ConfigSchema>(\n\tschema: S,\n\tvalue: unknown,\n\toptions: ValidateOptions,\n) {\n\tconst pending = toStandardSchema(schema)['~standard'].validate(value);\n\tlet result: StandardSchemaV1.Result<unknown>;\n\tif ('then' in pending) {\n\t\tif (!options.async) {\n\t\t\tvoid pending.catch(() => undefined);\n\t\t\treturn yield* Effect.die(\n\t\t\t\tnew TypeError(\n\t\t\t\t\t`The schema for the ${options.subject} validates asynchronously; use loadConfigAsync or loadEnvAsync`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tresult = yield* Effect.promise(async () => pending);\n\t} else {\n\t\tresult = pending;\n\t}\n\n\tconst issues = [\n\t\t...(options.issues ?? []),\n\t\t...(result.issues ?? []).map((issue): ConfigIssue => {\n\t\t\tconst path = Array.from(issue.path ?? [], (segment) =>\n\t\t\t\ttypeof segment === 'object' ? segment.key : segment,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tpath,\n\t\t\t\tmessage: issue.message,\n\t\t\t\tlocation: options.source && locateIssue(options.source, path),\n\t\t\t};\n\t\t}),\n\t];\n\tif (issues.length > 0) return yield* new ConfigValidationError({ subject: options.subject, issues });\n\treturn (result as StandardSchemaV1.SuccessResult<SchemaOutput<S>>).value;\n});\n\nexport const decodeConfig = <S extends ConfigSchema>(\n\tconfig: unknown,\n\tschema: S,\n\toptions: { readonly async: boolean },\n) => {\n\tconst source = configSource(config);\n\treturn validate(schema, config, {\n\t\tsubject: source ? `config ${source.file}` : 'config',\n\t\tsource,\n\t\tasync: options.async,\n\t});\n};\n\nexport interface LoadEnvOptions extends ResolveOptions {\n\t/**\n\t * Where to start looking for `.env` files, which are searched there and then in the nearest directory with a\n\t * `package.json`. Defaults to `process.cwd()`.\n\t */\n\treadonly cwd?: string | undefined;\n}\n\nexport const decodeEnv = Effect.fn('decodeEnv')(function* <S extends ConfigSchema>(\n\tschema: S,\n\toptions: LoadEnvOptions & { readonly async: boolean },\n) {\n\tconst env = options.env ?? processEnv();\n\tconst environment = options.environment ?? (env.NODE_ENV || undefined);\n\tconst result = yield* readEnv(join(options.cwd ?? process.cwd(), '.env'), { environment, env });\n\treturn yield* validate(schema, result.variables, {\n\t\tsubject: 'environment variables',\n\t\tasync: options.async,\n\t\tissues: result.problems.map((problem) => ({\n\t\t\tpath: [],\n\t\t\tmessage: problem.message,\n\t\t\tlocation: `${problem.file}:${problem.line}:${problem.column}`,\n\t\t})),\n\t});\n});\n","import { Effect } from 'effect';\n\nimport { decodeConfig, decodeEnv } from './schema.ts';\n\nimport type { ConfigSchema, LoadEnvOptions, LooseInput, SchemaInput, SchemaOutput } from './schema.ts';\n\nexport type {\n\tLocatedSpan,\n\tPath,\n\tProblem,\n\tProblemCode,\n\tResolvedConfig,\n\tResolvedLayer,\n\tResolveOptions,\n\tSpan,\n} from './core/index.ts';\nexport { ConfigValidationError } from './schema.ts';\nexport type {\n\tConfigIssue,\n\tConfigSchema,\n\tConfigSource,\n\tLoadEnvOptions,\n\tLooseInput,\n\tSchemaInput,\n\tSchemaOutput,\n} from './schema.ts';\n\n/**\n * Validates a config import (or any value) with a Standard Schema or an Effect Schema and returns the schema's output.\n *\n * @throws {@link ConfigValidationError} listing every issue, with `file:line:column` when `config` is a config import.\n * @throws TypeError when the schema validates asynchronously; use {@link loadConfigAsync} for those.\n */\nexport const loadConfig = <S extends ConfigSchema>(config: LooseInput<SchemaInput<S>>, schema: S): SchemaOutput<S> =>\n\tEffect.runSync(decodeConfig(config, schema, { async: false }));\n\n/**\n * {@link loadConfig} for schemas that may validate asynchronously.\n */\nexport const loadConfigAsync = async <S extends ConfigSchema>(\n\tconfig: LooseInput<SchemaInput<S>>,\n\tschema: S,\n): Promise<SchemaOutput<S>> => Effect.runPromise(decodeConfig(config, schema, { async: true }));\n\n/**\n * Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.\n * Never writes `process.env`.\n *\n * @throws {@link ConfigValidationError} listing every issue and every `.env` file that could not be read.\n * @throws TypeError when the schema validates asynchronously; use {@link loadEnvAsync} for those.\n */\nexport const loadEnv = <S extends ConfigSchema>(schema: S, options: LoadEnvOptions = {}): SchemaOutput<S> =>\n\tEffect.runSync(decodeEnv(schema, { ...options, async: false }));\n\n/**\n * {@link loadEnv} for schemas that may validate asynchronously.\n */\nexport const loadEnvAsync = async <S extends ConfigSchema>(\n\tschema: S,\n\toptions: LoadEnvOptions = {},\n): Promise<SchemaOutput<S>> => Effect.runPromise(decodeEnv(schema, { ...options, async: true }));\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Effect } from 'effect';\n\nimport { decodeConfig, decodeEnv } from './schema.ts';\n\nimport type { ConfigSchema, LoadEnvOptions, LooseInput, SchemaInput, SchemaOutput } from './schema.ts';\n\nexport type {\n\tLocatedSpan,\n\tPath,\n\tProblem,\n\tProblemCode,\n\tResolvedConfig,\n\tResolvedLayer,\n\tResolveOptions,\n\tSpan,\n} from './core/index.ts';\nexport { ConfigValidationError } from './schema.ts';\nexport type {\n\tConfigIssue,\n\tConfigSchema,\n\tConfigSource,\n\tLoadEnvOptions,\n\tLooseInput,\n\tSchemaInput,\n\tSchemaOutput,\n} from './schema.ts';\n\n/**\n * Validates a config import (or any value) with a Standard Schema or an Effect Schema and returns the schema's output.\n *\n * @throws {@link ConfigValidationError} listing every issue, with `file:line:column` when `config` is a config import.\n * @throws TypeError when the schema validates asynchronously; use {@link loadConfigAsync} for those.\n */\nexport const loadConfig = <S extends ConfigSchema>(config: LooseInput<SchemaInput<S>>, schema: S): SchemaOutput<S> =>\n\tEffect.runSync(decodeConfig(config, schema, { async: false }));\n\n/**\n * {@link loadConfig} for schemas that may validate asynchronously.\n */\nexport const loadConfigAsync = async <S extends ConfigSchema>(\n\tconfig: LooseInput<SchemaInput<S>>,\n\tschema: S,\n): Promise<SchemaOutput<S>> => Effect.runPromise(decodeConfig(config, schema, { async: true }));\n\n/**\n * Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.\n * Never writes `process.env`.\n *\n * @throws {@link ConfigValidationError} listing every issue and every `.env` file that could not be read.\n * @throws TypeError when the schema validates asynchronously; use {@link loadEnvAsync} for those.\n */\nexport const loadEnv = <S extends ConfigSchema>(schema: S, options: LoadEnvOptions = {}): SchemaOutput<S> =>\n\tEffect.runSync(decodeEnv(schema, { ...options, async: false }));\n\n/**\n * {@link loadEnv} for schemas that may validate asynchronously.\n */\nexport const loadEnvAsync = async <S extends ConfigSchema>(\n\tschema: S,\n\toptions: LoadEnvOptions = {},\n): Promise<SchemaOutput<S>> => Effect.runPromise(decodeEnv(schema, { ...options, async: true }));\n"],"mappings":";;;;;;;;;AAiCA,MAAa,cAAsC,QAAoC,WACtF,OAAO,QAAQ,aAAa,QAAQ,QAAQ,EAAE,OAAO,MAAM,CAAC,CAAC;;;;AAK9D,MAAa,kBAAkB,OAC9B,QACA,WAC8B,OAAO,WAAW,aAAa,QAAQ,QAAQ,EAAE,OAAO,KAAK,CAAC,CAAC;;;;;;;;AAS9F,MAAa,WAAmC,QAAW,UAA0B,CAAC,MACrF,OAAO,QAAQ,UAAU,QAAQ;CAAE,GAAG;CAAS,OAAO;AAAM,CAAC,CAAC;;;;AAK/D,MAAa,eAAe,OAC3B,QACA,UAA0B,CAAC,MACG,OAAO,WAAW,UAAU,QAAQ;CAAE,GAAG;CAAS,OAAO;AAAK,CAAC,CAAC"}
@@ -0,0 +1,124 @@
1
+ import { c as searchDirectories, i as processEnv, o as findProjectRoot, r as envFileNames, s as isFile, t as resolveConfig } from "./resolve-B7e2uGDa.mjs";
2
+ import { a as parseConfigFileName, c as pathKey, d as problemAt, i as configSuffixes, l as fileProblem, n as isPlainObject, o as SourceIndex, s as formatPath, t as hasPlaceholder, u as formatProblems } from "./placeholders-MqW2KO9Z.mjs";
3
+ import { Effect } from "effect";
4
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
5
+ // src/runtime/load.ts
6
+ const browserLayers = ["base", "environment"];
7
+ const watchFilesOf = Effect.fn("watchFilesOf")(function* (file, environment, target) {
8
+ const name = parseConfigFileName(basename(file));
9
+ if (!name) return [file];
10
+ const files = [file];
11
+ if (environment) files.push(join(dirname(file), `${name.stem}.${environment}${name.suffix}`));
12
+ if (target === "browser") return files;
13
+ const directories = yield* searchDirectories(file);
14
+ const locals = environment ? [`${name.stem}.local${name.suffix}`, `${name.stem}.${environment}.local${name.suffix}`] : [`${name.stem}.local${name.suffix}`];
15
+ for (const fileName of [...locals, ...envFileNames(environment)]) for (const directory of directories) files.push(join(directory, fileName));
16
+ return files;
17
+ });
18
+ /**
19
+ * A JavaScript expression that evaluates to a deep-frozen copy of a config value.
20
+ */
21
+ const valueLiteral = (value, indent = "") => {
22
+ if (value === void 0) return "undefined";
23
+ if (typeof value === "number") return Object.is(value, -0) ? "-0" : String(value);
24
+ if (typeof value === "bigint") return `${value}n`;
25
+ if (value instanceof Date) return `new Date(${valueLiteral(value.getTime(), indent)})`;
26
+ const inner = `${indent}\t`;
27
+ if (Array.isArray(value)) {
28
+ if (value.length === 0) return "Object.freeze([])";
29
+ return `Object.freeze([\n${value.map((item) => `${inner}${valueLiteral(item, inner)},\n`).join("")}${indent}])`;
30
+ }
31
+ if (isPlainObject(value)) {
32
+ const entries = Object.entries(value);
33
+ if (entries.length === 0) return "Object.freeze({})";
34
+ return `Object.freeze({\n${entries.map(([key, item]) => {
35
+ const name = key === "__proto__" ? `[${JSON.stringify(key)}]` : JSON.stringify(key);
36
+ return `${inner}${name}: ${valueLiteral(item, inner)},\n`;
37
+ }).join("")}${indent}})`;
38
+ }
39
+ return JSON.stringify(value);
40
+ };
41
+ const hasUnescapedPlaceholder = (text) => hasPlaceholder(text.replaceAll("$${", ""));
42
+ const placeholderProblems = (file, text, value, positions) => {
43
+ const source = new SourceIndex(text);
44
+ const problems = [];
45
+ const visit = (item, path) => {
46
+ if (typeof item === "string") {
47
+ if (!hasUnescapedPlaceholder(item)) return;
48
+ const span = positions.get(pathKey(path));
49
+ problems.push(problemAt(source, file, span?.valueStart ?? 0, span?.valueLength ?? 0, "browser-placeholder", `'${formatPath(path) || "(root)"}' uses an environment placeholder, which browser builds cannot read`));
50
+ } else if (Array.isArray(item)) for (const [index, child] of item.entries()) visit(child, [...path, index]);
51
+ else if (isPlainObject(item)) for (const [key, child] of Object.entries(item)) visit(child, [...path, key]);
52
+ };
53
+ visit(value, []);
54
+ return problems;
55
+ };
56
+ const failingCode = (file, problems) => `throw new Error(${JSON.stringify(`Invalid config ${file}\n${formatProblems(problems)}`)});\n\nexport default undefined;\n`;
57
+ const serverModule = Effect.fn("serverModule")(function* (file, options) {
58
+ const problems = [];
59
+ if (!parseConfigFileName(basename(file))) problems.push(fileProblem(file, "unsupported-file", `${basename(file)} is not a config file; expected a name ending in ${configSuffixes.join(", ")}`));
60
+ else if (!(yield* isFile(file))) problems.push(fileProblem(file, "file-not-found", `Cannot find ${file}`));
61
+ const settings = options.environment === void 0 ? "" : `, { environment: ${JSON.stringify(options.environment)} }`;
62
+ const code = `import { resolveConfigModule } from ${JSON.stringify(options.runtime ?? "tyfigo/runtime")};\n\nexport default resolveConfigModule(${JSON.stringify(file)}${settings});\n`;
63
+ const environment = options.environment ?? (processEnv().NODE_ENV || void 0);
64
+ return {
65
+ code,
66
+ watchFiles: yield* watchFilesOf(file, environment, "server"),
67
+ problems
68
+ };
69
+ });
70
+ const browserModule = Effect.fn("browserModule")(function* (file, options) {
71
+ const environment = options.environment ?? (processEnv().NODE_ENV || void 0);
72
+ const result = yield* resolveConfig(file, {
73
+ environment,
74
+ env: {},
75
+ layers: browserLayers
76
+ });
77
+ const files = /* @__PURE__ */ new Set([result.file, ...result.layers.map((layer) => layer.file)]);
78
+ const problems = result.problems.filter((problem) => files.has(problem.file) && problem.code !== "missing-variable" && problem.code !== "invalid-placeholder");
79
+ for (const layer of result.layers) {
80
+ const parsed = result.format?.parse(layer.text, layer.file);
81
+ if (parsed?.value !== void 0) problems.push(...placeholderProblems(layer.file, layer.text, parsed.value, layer.positions));
82
+ }
83
+ return {
84
+ code: problems.length > 0 ? failingCode(result.file, problems) : `export default ${valueLiteral(result.value, "")};\n`,
85
+ watchFiles: yield* watchFilesOf(result.file, environment, "browser"),
86
+ problems
87
+ };
88
+ });
89
+ const bundledServerModule = Effect.fn("bundledServerModule")(function* (file, options) {
90
+ const environment = options.environment ?? (processEnv().NODE_ENV || void 0);
91
+ const result = yield* resolveConfig(file, { environment });
92
+ const problems = result.problems.filter((problem) => problem.code !== "missing-variable");
93
+ const root = (yield* findProjectRoot(dirname(result.file))) ?? dirname(result.file);
94
+ const name = (path) => relative(root, path).split(sep).join("/");
95
+ const bundle = {
96
+ file: name(result.file),
97
+ environment,
98
+ layers: result.layers.map((layer) => ({
99
+ file: name(layer.file),
100
+ kind: layer.kind,
101
+ text: layer.text
102
+ }))
103
+ };
104
+ return {
105
+ code: problems.length > 0 ? failingCode(result.file, problems) : `import { resolveBundledConfigModule } from ${JSON.stringify(options.runtime ?? "tyfigo/runtime")};\n\nexport default resolveBundledConfigModule(${JSON.stringify(bundle)});\n`,
106
+ watchFiles: yield* watchFilesOf(result.file, environment, "server"),
107
+ problems
108
+ };
109
+ });
110
+ /**
111
+ * Generates the JavaScript module for a config file import. Loaders fail the import or build when `problems` is not empty.
112
+ */
113
+ const loadModule = Effect.fn("loadModule")(function* (path, options) {
114
+ const file = resolve(path);
115
+ if (options.target === "browser") return yield* browserModule(file, options);
116
+ return options.bundle ? yield* bundledServerModule(file, options) : yield* serverModule(file, options);
117
+ });
118
+ /**
119
+ * {@link loadModule} for synchronous callers such as Node `registerHooks` load hooks.
120
+ */
121
+ const load = (path, options) => Effect.runSync(loadModule(path, options));
122
+ export { loadModule as n, valueLiteral as r, load as t };
123
+
124
+ //# sourceMappingURL=load-BqGJgpfo.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-BqGJgpfo.mjs","names":[],"sources":["../src/runtime/load.ts"],"sourcesContent":["import { basename, dirname, join, relative, resolve, sep } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport { findProjectRoot, isFile, searchDirectories } from '../core/files.ts';\nimport {\n\tconfigSuffixes,\n\tenvFileNames,\n\tfileProblem,\n\tformatPath,\n\tformatProblems,\n\thasPlaceholder,\n\tisPlainObject,\n\tparseConfigFileName,\n\tpathKey,\n\tproblemAt,\n\tprocessEnv,\n\tresolveConfig,\n\tSourceIndex,\n} from '../core/index.ts';\n\nimport type { Path, PositionMap, Problem } from '../core/index.ts';\nimport type { BundledConfig } from './resolve.ts';\n\nexport type LoadTarget = 'server' | 'browser';\n\nexport interface LoadOptions {\n\t/**\n\t * `server` resolves the config when the module runs. `browser` inlines the base and environment files at build time and\n\t * reports every placeholder as a problem.\n\t */\n\treadonly target: LoadTarget;\n\t/**\n\t * The environment name, such as the bundler's mode. Defaults to `NODE_ENV`, read when the server module runs and when\n\t * `load` runs for the browser.\n\t */\n\treadonly environment?: string | undefined;\n\t/**\n\t * The specifier the server module imports the runtime from. Defaults to `tyfigo/runtime`.\n\t */\n\treadonly runtime?: string | undefined;\n\t/**\n\t * For `server`: inline the text of the layer files that exist at build time, and read `.env` files and environment variables\n\t * from `process.cwd()` when the module runs, so a bundle does not depend on the paths of the machine that built it.\n\t */\n\treadonly bundle?: boolean | undefined;\n}\n\nexport interface LoadResult {\n\t/**\n\t * The JavaScript module, with the config as its default export.\n\t */\n\treadonly code: string;\n\t/**\n\t * Every file whose creation, change or removal changes the module, whether or not it exists now.\n\t */\n\treadonly watchFiles: readonly string[];\n\treadonly problems: readonly Problem[];\n}\n\nconst browserLayers = ['base', 'environment'] as const;\n\nconst watchFilesOf = Effect.fn('watchFilesOf')(function* (\n\tfile: string,\n\tenvironment: string | undefined,\n\ttarget: LoadTarget,\n) {\n\tconst name = parseConfigFileName(basename(file));\n\tif (!name) return [file];\n\n\tconst files = [file];\n\tif (environment) files.push(join(dirname(file), `${name.stem}.${environment}${name.suffix}`));\n\tif (target === 'browser') return files;\n\n\tconst directories = yield* searchDirectories(file);\n\tconst locals = environment\n\t\t? [`${name.stem}.local${name.suffix}`, `${name.stem}.${environment}.local${name.suffix}`]\n\t\t: [`${name.stem}.local${name.suffix}`];\n\tfor (const fileName of [...locals, ...envFileNames(environment)]) {\n\t\tfor (const directory of directories) files.push(join(directory, fileName));\n\t}\n\treturn files;\n});\n\n/**\n * A JavaScript expression that evaluates to a deep-frozen copy of a config value.\n */\nexport const valueLiteral = (value: unknown, indent = ''): string => {\n\tif (value === undefined) return 'undefined';\n\tif (typeof value === 'number') return Object.is(value, -0) ? '-0' : String(value);\n\tif (typeof value === 'bigint') return `${value}n`;\n\tif (value instanceof Date) return `new Date(${valueLiteral(value.getTime(), indent)})`;\n\tconst inner = `${indent}\\t`;\n\tif (Array.isArray(value)) {\n\t\tif (value.length === 0) return 'Object.freeze([])';\n\t\tconst items = value.map((item) => `${inner}${valueLiteral(item, inner)},\\n`);\n\t\treturn `Object.freeze([\\n${items.join('')}${indent}])`;\n\t}\n\tif (isPlainObject(value)) {\n\t\tconst entries = Object.entries(value);\n\t\tif (entries.length === 0) return 'Object.freeze({})';\n\t\tconst properties = entries.map(([key, item]) => {\n\t\t\tconst name = key === '__proto__' ? `[${JSON.stringify(key)}]` : JSON.stringify(key);\n\t\t\treturn `${inner}${name}: ${valueLiteral(item, inner)},\\n`;\n\t\t});\n\t\treturn `Object.freeze({\\n${properties.join('')}${indent}})`;\n\t}\n\treturn JSON.stringify(value);\n};\n\nconst hasUnescapedPlaceholder = (text: string) => hasPlaceholder(text.replaceAll('$${', ''));\n\nconst placeholderProblems = (file: string, text: string, value: unknown, positions: PositionMap) => {\n\tconst source = new SourceIndex(text);\n\tconst problems: Problem[] = [];\n\tconst visit = (item: unknown, path: Path) => {\n\t\tif (typeof item === 'string') {\n\t\t\tif (!hasUnescapedPlaceholder(item)) return;\n\t\t\tconst span = positions.get(pathKey(path));\n\t\t\tproblems.push(\n\t\t\t\tproblemAt(\n\t\t\t\t\tsource,\n\t\t\t\t\tfile,\n\t\t\t\t\tspan?.valueStart ?? 0,\n\t\t\t\t\tspan?.valueLength ?? 0,\n\t\t\t\t\t'browser-placeholder',\n\t\t\t\t\t`'${formatPath(path) || '(root)'}' uses an environment placeholder, which browser builds cannot read`,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (Array.isArray(item)) for (const [index, child] of item.entries()) visit(child, [...path, index]);\n\t\telse if (isPlainObject(item)) for (const [key, child] of Object.entries(item)) visit(child, [...path, key]);\n\t};\n\tvisit(value, []);\n\treturn problems;\n};\n\nconst failingCode = (file: string, problems: readonly Problem[]) =>\n\t`throw new Error(${JSON.stringify(`Invalid config ${file}\\n${formatProblems(problems)}`)});\\n\\nexport default undefined;\\n`;\n\nconst serverModule = Effect.fn('serverModule')(function* (file: string, options: LoadOptions) {\n\tconst problems: Problem[] = [];\n\tif (!parseConfigFileName(basename(file))) {\n\t\tproblems.push(\n\t\t\tfileProblem(\n\t\t\t\tfile,\n\t\t\t\t'unsupported-file',\n\t\t\t\t`${basename(file)} is not a config file; expected a name ending in ${configSuffixes.join(', ')}`,\n\t\t\t),\n\t\t);\n\t} else if (!(yield* isFile(file))) problems.push(fileProblem(file, 'file-not-found', `Cannot find ${file}`));\n\n\tconst settings =\n\t\toptions.environment === undefined ? '' : `, { environment: ${JSON.stringify(options.environment)} }`;\n\tconst code = `import { resolveConfigModule } from ${JSON.stringify(options.runtime ?? 'tyfigo/runtime')};\\n\\nexport default resolveConfigModule(${JSON.stringify(file)}${settings});\\n`;\n\tconst environment = options.environment ?? (processEnv().NODE_ENV || undefined);\n\treturn { code, watchFiles: yield* watchFilesOf(file, environment, 'server'), problems } satisfies LoadResult;\n});\n\nconst browserModule = Effect.fn('browserModule')(function* (file: string, options: LoadOptions) {\n\tconst environment = options.environment ?? (processEnv().NODE_ENV || undefined);\n\tconst result = yield* resolveConfig(file, { environment, env: {}, layers: browserLayers });\n\tconst files = new Set([result.file, ...result.layers.map((layer) => layer.file)]);\n\tconst problems = result.problems.filter(\n\t\t(problem) =>\n\t\t\tfiles.has(problem.file) && problem.code !== 'missing-variable' && problem.code !== 'invalid-placeholder',\n\t);\n\tfor (const layer of result.layers) {\n\t\tconst parsed = result.format?.parse(layer.text, layer.file);\n\t\tif (parsed?.value !== undefined) {\n\t\t\tproblems.push(...placeholderProblems(layer.file, layer.text, parsed.value, layer.positions));\n\t\t}\n\t}\n\n\tconst code =\n\t\tproblems.length > 0\n\t\t\t? failingCode(result.file, problems)\n\t\t\t: `export default ${valueLiteral(result.value, '')};\\n`;\n\treturn {\n\t\tcode,\n\t\twatchFiles: yield* watchFilesOf(result.file, environment, 'browser'),\n\t\tproblems,\n\t} satisfies LoadResult;\n});\n\nconst bundledServerModule = Effect.fn('bundledServerModule')(function* (file: string, options: LoadOptions) {\n\tconst environment = options.environment ?? (processEnv().NODE_ENV || undefined);\n\tconst result = yield* resolveConfig(file, { environment });\n\tconst problems = result.problems.filter((problem) => problem.code !== 'missing-variable');\n\tconst root = (yield* findProjectRoot(dirname(result.file))) ?? dirname(result.file);\n\tconst name = (path: string) => relative(root, path).split(sep).join('/');\n\tconst bundle: BundledConfig = {\n\t\tfile: name(result.file),\n\t\tenvironment,\n\t\tlayers: result.layers.map((layer) => ({ file: name(layer.file), kind: layer.kind, text: layer.text })),\n\t};\n\n\tconst code =\n\t\tproblems.length > 0\n\t\t\t? failingCode(result.file, problems)\n\t\t\t: `import { resolveBundledConfigModule } from ${JSON.stringify(options.runtime ?? 'tyfigo/runtime')};\\n\\nexport default resolveBundledConfigModule(${JSON.stringify(bundle)});\\n`;\n\treturn {\n\t\tcode,\n\t\twatchFiles: yield* watchFilesOf(result.file, environment, 'server'),\n\t\tproblems,\n\t} satisfies LoadResult;\n});\n\n/**\n * Generates the JavaScript module for a config file import. Loaders fail the import or build when `problems` is not empty.\n */\nexport const loadModule = Effect.fn('loadModule')(function* (path: string, options: LoadOptions) {\n\tconst file = resolve(path);\n\tif (options.target === 'browser') return yield* browserModule(file, options);\n\treturn options.bundle ? yield* bundledServerModule(file, options) : yield* serverModule(file, options);\n});\n\n/**\n * {@link loadModule} for synchronous callers such as Node `registerHooks` load hooks.\n */\nexport const load = (path: string, options: LoadOptions): LoadResult => Effect.runSync(loadModule(path, options));\n"],"mappings":"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"}