@adamhl8/configs 0.24.13 → 0.26.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.
@@ -0,0 +1,9 @@
1
+ import { requireWhen } from "./utils.js";
2
+ import arkenv from "arkenv";
3
+
4
+ //#region src/env/index.d.ts
5
+ type ArkEnv = typeof arkenv;
6
+ declare const parseEnv: ArkEnv;
7
+ //#endregion
8
+ export { parseEnv, requireWhen };
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/env/index.ts"],"mappings":";;;;KAKK,MAAA,UAAgB,MAAM;AAAA,cAEd,QAAA,EAOP,MAAM"}
@@ -0,0 +1,17 @@
1
+ import { requireWhen } from "./utils.js";
2
+ import process from "node:process";
3
+ import arkenv from "arkenv";
4
+ import { attempt, isErr } from "ts-explicit-errors";
5
+ //#region src/env/index.ts
6
+ const parseEnv = ((...args) => {
7
+ const env = attempt(() => arkenv(...args));
8
+ if (isErr(env)) {
9
+ console.error(env.message);
10
+ process.exit(1);
11
+ }
12
+ return env;
13
+ });
14
+ //#endregion
15
+ export { parseEnv, requireWhen };
16
+
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/env/index.ts"],"sourcesContent":["import process from \"node:process\"\n\nimport arkenv from \"arkenv\"\nimport { attempt, isErr } from \"ts-explicit-errors\"\n\ntype ArkEnv = typeof arkenv\n\nexport const parseEnv = ((...args: Parameters<ArkEnv>) => {\n const env = attempt(() => arkenv(...args))\n if (isErr(env)) {\n console.error(env.message)\n process.exit(1)\n }\n return env\n}) as ArkEnv\n\nexport { requireWhen } from \"#/env/utils.ts\"\n"],"mappings":";;;;;AAOA,MAAa,aAAa,GAAG,SAA6B;CACxD,MAAM,MAAM,cAAc,OAAO,GAAG,IAAI,CAAC;CACzC,IAAI,MAAM,GAAG,GAAG;EACd,QAAQ,MAAM,IAAI,OAAO;EACzB,QAAQ,KAAK,CAAC;CAChB;CACA,OAAO;AACT"}
@@ -0,0 +1,14 @@
1
+ import { type } from "arktype";
2
+
3
+ //#region src/env/utils.d.ts
4
+ /**
5
+ * Makes an env var required while `when` is true, otherwise optional.
6
+ *
7
+ * A set value must always satisfy `def`. An unset var takes `fallback` (exempt from `def` validation), or the key is
8
+ * omitted entirely if no fallback is given.
9
+ */
10
+ declare function requireWhen<const type$1>(when: boolean, type: type.validate<type$1>): ReturnType<type.instantiate<type$1>["optional"]>;
11
+ declare function requireWhen<const type$2>(when: boolean, type: type.validate<type$2>, fallback: type.infer<type$2>): type.instantiate<type$2>;
12
+ //#endregion
13
+ export { requireWhen };
14
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","names":[],"sources":["../../src/env/utils.ts"],"mappings":";;;;;AASA;;;;iBAAgB,WAAA,eACd,IAAA,WACA,IAAA,EAAM,IAAA,CAAQ,QAAA,CAAS,MAAA,IACtB,UAAA,CAAW,IAAA,CAAQ,WAAA,CAAY,MAAA;AAAA,iBAClB,WAAA,eACd,IAAA,WACA,IAAA,EAAM,IAAA,CAAQ,QAAA,CAAS,MAAA,GACvB,QAAA,EAAU,IAAA,CAAQ,KAAA,CAAM,MAAA,IACvB,IAAA,CAAQ,WAAA,CAAY,MAAA"}
@@ -0,0 +1,14 @@
1
+ import { type } from "arkenv";
2
+ //#region src/env/utils.ts
3
+ function requireWhen(when, type$1, ...rest) {
4
+ const t = type.raw(type$1);
5
+ if (when) return t;
6
+ if (rest.length === 0) return t.optional();
7
+ const [fallback] = rest;
8
+ const unset = Symbol("unset");
9
+ return t.or(type.unit(unset)).pipe((v) => v === unset ? fallback : v).default(unset);
10
+ }
11
+ //#endregion
12
+ export { requireWhen };
13
+
14
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":["arkenvType","type"],"sources":["../../src/env/utils.ts"],"sourcesContent":["import { type as arkenvType } from \"arkenv\"\nimport type { type as arktype } from \"arktype\"\n\n/**\n * Makes an env var required while `when` is true, otherwise optional.\n *\n * A set value must always satisfy `def`. An unset var takes `fallback` (exempt from `def` validation), or the key is\n * omitted entirely if no fallback is given.\n */\nexport function requireWhen<const type>(\n when: boolean,\n type: arktype.validate<type>,\n): ReturnType<arktype.instantiate<type>[\"optional\"]>\nexport function requireWhen<const type>(\n when: boolean,\n type: arktype.validate<type>,\n fallback: arktype.infer<type>,\n): arktype.instantiate<type>\n/* oxlint-disable typescript/no-unsafe-type-assertion */\nexport function requireWhen(when: boolean, type: unknown, ...rest: [unknown?]): never {\n const t = arkenvType.raw(type)\n if (when) return t as never\n if (rest.length === 0) return t.optional() as never\n const [fallback] = rest\n // defaulting to a sentinel keeps the fallback exempt from def validation without\n // letting an explicitly set value bypass it (env values can never be a symbol)\n const unset = Symbol(\"unset\")\n return t\n .or(arkenvType.unit(unset))\n .pipe((v: unknown) => (v === unset ? fallback : v))\n .default(unset as never) as never\n}\n"],"mappings":";;AAmBA,SAAgB,YAAY,MAAe,QAAe,GAAG,MAAyB;CACpF,MAAM,IAAIA,KAAW,IAAIC,MAAI;CAC7B,IAAI,MAAM,OAAO;CACjB,IAAI,KAAK,WAAW,GAAG,OAAO,EAAE,SAAS;CACzC,MAAM,CAAC,YAAY;CAGnB,MAAM,QAAQ,OAAO,OAAO;CAC5B,OAAO,EACJ,GAAGD,KAAW,KAAK,KAAK,CAAC,CAAC,CAC1B,MAAM,MAAgB,MAAM,QAAQ,WAAW,CAAE,CAAC,CAClD,QAAQ,KAAc;AAC3B"}
package/dist/utils.d.ts CHANGED
@@ -4,12 +4,22 @@ import { MergeDeep, ReadonlyDeep, SimplifyDeep, WritableDeep } from "type-fest";
4
4
  /**
5
5
  * The merge config function, where the `UserConfig` passed in is merged with `BaseConfig`.
6
6
  *
7
+ * Each `arrays` mode gets its own overload so the return type's `arrayMergeMode` matches the runtime behavior. The
8
+ * `"replace"` overload comes first so it wins overload resolution when `{ arrays: "replace" }` is passed.
9
+ *
7
10
  * The `const` type parameter keeps literal types without requiring `as const` at the call site. The constraint is
8
11
  * `ReadonlyDeep` so readonly arrays/objects (e.g. from `as const`) are accepted, and the result is `WritableDeep` so it
9
12
  * stays assignable to the tools' own (mutable) config types.
10
13
  */
11
14
  interface MergeConfigFn<UserConfig, BaseConfig> {
12
- <const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(userConfig: UserConfigToMerge): MergeDeep<SimplifyDeep<WritableDeep<BaseConfig>>, SimplifyDeep<WritableDeep<UserConfigToMerge>>, {
15
+ <const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(userConfig: UserConfigToMerge, options: {
16
+ arrays: "replace";
17
+ }): MergeDeep<SimplifyDeep<WritableDeep<BaseConfig>>, SimplifyDeep<WritableDeep<UserConfigToMerge>>, {
18
+ arrayMergeMode: "replace";
19
+ }>;
20
+ <const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(userConfig: UserConfigToMerge, options?: {
21
+ arrays?: "merge";
22
+ }): MergeDeep<SimplifyDeep<WritableDeep<BaseConfig>>, SimplifyDeep<WritableDeep<UserConfigToMerge>>, {
13
23
  arrayMergeMode: "spread";
14
24
  }>;
15
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","names":[],"sources":["../src/utils.ts"],"mappings":";;;;AAmBA;;;;;;UAAiB,aAAA;EAAA,iCAEkB,YAAA,CAAa,UAAA,GAC5C,UAAA,EAAY,iBAAA,GACX,SAAA,CACD,YAAA,CAAa,YAAA,CAAa,UAAA,IAC1B,YAAA,CAAa,YAAA,CAAa,iBAAA;IACxB,cAAA;EAAA;AAAA;;;;;;;;UAYW,qBAAA,iCAAsD,aAAA,CAAc,UAAA,EAAY,UAAA;EAAA,IAC3F,YAAA,CAAa,YAAA,CAAa,UAAA;AAAA"}
1
+ {"version":3,"file":"utils.d.ts","names":[],"sources":["../src/utils.ts"],"mappings":";;;;AA6BA;;;;;;;;;UAAiB,aAAA;EAAA,iCACkB,YAAA,CAAa,UAAA,GAC5C,UAAA,EAAY,iBAAA,EACZ,OAAA;IAAW,MAAA;EAAA,IACV,SAAA,CACD,YAAA,CAAa,YAAA,CAAa,UAAA,IAC1B,YAAA,CAAa,YAAA,CAAa,iBAAA;IACxB,cAAA;EAAA;EAAA,iCAE6B,YAAA,CAAa,UAAA,GAC5C,UAAA,EAAY,iBAAA,EACZ,OAAA;IAAY,MAAA;EAAA,IACX,SAAA,CACD,YAAA,CAAa,YAAA,CAAa,UAAA,IAC1B,YAAA,CAAa,YAAA,CAAa,iBAAA;IACxB,cAAA;EAAA;AAAA;;;;;;;;UAYW,qBAAA,iCAAsD,aAAA,CAAc,UAAA,EAAY,UAAA;EAAA,IAC3F,YAAA,CAAa,YAAA,CAAa,UAAA;AAAA"}
package/dist/utils.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import { cloneDeep, mergeWith } from "es-toolkit";
2
2
  //#region src/utils.ts
3
- /** A wrapper around es-toolkit's `mergeWith` with a custom merge function that concatenates arrays. */
4
- const merge = (target, source) => mergeWith(target, source, (objValue, srcValue) => Array.isArray(objValue) ? objValue.concat(srcValue) : void 0);
3
+ /** A wrapper around es-toolkit's `mergeWith` with a custom merge function that concatenates or replaces arrays. */
4
+ const merge = (target, source, arrays) => mergeWith(target, source, (objValue, srcValue) => {
5
+ if (!Array.isArray(objValue)) return;
6
+ return arrays === "merge" ? objValue.concat(srcValue) : srcValue;
7
+ });
5
8
  /**
6
9
  * Creates a config merge function with proper type overloads for merging with a base config.
7
10
  *
@@ -11,9 +14,9 @@ const merge = (target, source) => mergeWith(target, source, (objValue, srcValue)
11
14
  * limit on large configs like tsdown's.
12
15
  */
13
16
  const createMergeConfigFn = (baseConfig) => {
14
- const mergeConfigFn = (userConfig) => {
17
+ const mergeConfigFn = (userConfig, options) => {
15
18
  if (userConfig === void 0) return cloneDeep(baseConfig);
16
- return merge(cloneDeep(baseConfig), cloneDeep(userConfig));
19
+ return merge(cloneDeep(baseConfig), cloneDeep(userConfig), options?.arrays ?? "merge");
17
20
  };
18
21
  return mergeConfigFn;
19
22
  };
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { cloneDeep, mergeWith } from \"es-toolkit\"\nimport type { MergeDeep, ReadonlyDeep, WritableDeep, SimplifyDeep } from \"type-fest\"\n\ntype AnyObj = object\n\n/** A wrapper around es-toolkit's `mergeWith` with a custom merge function that concatenates arrays. */\nconst merge = <T extends AnyObj, S extends AnyObj>(target: T, source: S): T & S =>\n mergeWith(target, source, (objValue: unknown, srcValue: unknown) =>\n // oxlint-disable-next-line unicorn/prefer-spread\n Array.isArray(objValue) ? objValue.concat(srcValue) : undefined,\n )\n\n/**\n * The merge config function, where the `UserConfig` passed in is merged with `BaseConfig`.\n *\n * The `const` type parameter keeps literal types without requiring `as const` at the call site. The constraint is\n * `ReadonlyDeep` so readonly arrays/objects (e.g. from `as const`) are accepted, and the result is `WritableDeep` so it\n * stays assignable to the tools' own (mutable) config types.\n */\nexport interface MergeConfigFn<UserConfig, BaseConfig> {\n // oxlint-disable-next-line typescript/prefer-function-type - need to use call signature type\n <const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(\n userConfig: UserConfigToMerge,\n ): MergeDeep<\n SimplifyDeep<WritableDeep<BaseConfig>>,\n SimplifyDeep<WritableDeep<UserConfigToMerge>>,\n { arrayMergeMode: \"spread\" }\n >\n // Instead of returning `BaseConfig & UserConfig` (from `merge`), return a more friendly type using `MergeDeep`\n}\n\n/**\n * The optional merge config function, where the `userConfig` argument is optional. - if `UserConfig` is not provided,\n * the return type is `BaseConfig` - if `UserConfig` is provided, the return type is the merged type of `BaseConfig` and\n * `UserConfig`.\n *\n * Note that this extends `MergeConfigFn`, so this type has both function signatures on it.\n */\nexport interface OptionalMergeConfigFn<UserConfig, BaseConfig> extends MergeConfigFn<UserConfig, BaseConfig> {\n (): SimplifyDeep<WritableDeep<BaseConfig>>\n}\n\n/**\n * Creates a config merge function with proper type overloads for merging with a base config.\n *\n * `UserConfig` is not inferrable, so both type arguments must be passed explicitly. Returning the exact\n * `OptionalMergeConfigFn` instantiation (instead of relating a looser type to an annotation at the export site) keeps\n * tsc from eagerly evaluating the deep-mapped types over the full config type, which blows the instantiation depth\n * limit on large configs like tsdown's.\n */\nexport const createMergeConfigFn = <UserConfig extends AnyObj, BaseConfig extends ReadonlyDeep<UserConfig>>(\n baseConfig: BaseConfig,\n): OptionalMergeConfigFn<UserConfig, BaseConfig> => {\n // We don't care about the specific type of userConfig here because we assert mergeConfigFn as the correct type below\n const mergeConfigFn = (userConfig?: AnyObj) => {\n // Return a clone so consumers can't mutate the shared base config\n // oxlint-disable-next-line unicorn/prefer-structured-clone - structuredClone does not properly clone functions\n if (userConfig === undefined) return cloneDeep(baseConfig)\n // Clone both target and source so we never mutate the original objects\n // oxlint-disable-next-line unicorn/prefer-structured-clone - structuredClone does not properly clone functions\n return merge(cloneDeep(baseConfig), cloneDeep(userConfig))\n }\n\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion\n return mergeConfigFn as OptionalMergeConfigFn<UserConfig, BaseConfig>\n}\n"],"mappings":";;;AAMA,MAAM,SAA6C,QAAW,WAC5D,UAAU,QAAQ,SAAS,UAAmB,aAE5C,MAAM,QAAQ,QAAQ,IAAI,SAAS,OAAO,QAAQ,IAAI,KAAA,CACxD;;;;;;;;;AAwCF,MAAa,uBACX,eACkD;CAElD,MAAM,iBAAiB,eAAwB;EAG7C,IAAI,eAAe,KAAA,GAAW,OAAO,UAAU,UAAU;EAGzD,OAAO,MAAM,UAAU,UAAU,GAAG,UAAU,UAAU,CAAC;CAC3D;CAGA,OAAO;AACT"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { cloneDeep, mergeWith } from \"es-toolkit\"\nimport type { MergeDeep, ReadonlyDeep, WritableDeep, SimplifyDeep } from \"type-fest\"\n\ntype AnyObj = object\n\n/**\n * How arrays in the user config are combined with arrays in the base config. - `\"merge\"` (default): the user array is\n * appended to the base array - `\"replace\"`: the user array is used as-is\n */\ntype ArrayMergeMode = \"merge\" | \"replace\"\n\n/** A wrapper around es-toolkit's `mergeWith` with a custom merge function that concatenates or replaces arrays. */\nconst merge = <T extends AnyObj, S extends AnyObj>(target: T, source: S, arrays: ArrayMergeMode): T & S =>\n mergeWith(target, source, (objValue: unknown, srcValue: unknown) => {\n if (!Array.isArray(objValue)) return\n // oxlint-disable-next-line unicorn/prefer-spread\n return arrays === \"merge\" ? objValue.concat(srcValue) : srcValue\n })\n\n/**\n * The merge config function, where the `UserConfig` passed in is merged with `BaseConfig`.\n *\n * Each `arrays` mode gets its own overload so the return type's `arrayMergeMode` matches the runtime behavior. The\n * `\"replace\"` overload comes first so it wins overload resolution when `{ arrays: \"replace\" }` is passed.\n *\n * The `const` type parameter keeps literal types without requiring `as const` at the call site. The constraint is\n * `ReadonlyDeep` so readonly arrays/objects (e.g. from `as const`) are accepted, and the result is `WritableDeep` so it\n * stays assignable to the tools' own (mutable) config types.\n */\nexport interface MergeConfigFn<UserConfig, BaseConfig> {\n <const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(\n userConfig: UserConfigToMerge,\n options: { arrays: \"replace\" },\n ): MergeDeep<\n SimplifyDeep<WritableDeep<BaseConfig>>,\n SimplifyDeep<WritableDeep<UserConfigToMerge>>,\n { arrayMergeMode: \"replace\" }\n >\n <const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(\n userConfig: UserConfigToMerge,\n options?: { arrays?: \"merge\" },\n ): MergeDeep<\n SimplifyDeep<WritableDeep<BaseConfig>>,\n SimplifyDeep<WritableDeep<UserConfigToMerge>>,\n { arrayMergeMode: \"spread\" }\n >\n // Instead of returning `BaseConfig & UserConfig` (from `merge`), return a more friendly type using `MergeDeep`\n}\n\n/**\n * The optional merge config function, where the `userConfig` argument is optional. - if `UserConfig` is not provided,\n * the return type is `BaseConfig` - if `UserConfig` is provided, the return type is the merged type of `BaseConfig` and\n * `UserConfig`.\n *\n * Note that this extends `MergeConfigFn`, so this type has both function signatures on it.\n */\nexport interface OptionalMergeConfigFn<UserConfig, BaseConfig> extends MergeConfigFn<UserConfig, BaseConfig> {\n (): SimplifyDeep<WritableDeep<BaseConfig>>\n}\n\n/**\n * Creates a config merge function with proper type overloads for merging with a base config.\n *\n * `UserConfig` is not inferrable, so both type arguments must be passed explicitly. Returning the exact\n * `OptionalMergeConfigFn` instantiation (instead of relating a looser type to an annotation at the export site) keeps\n * tsc from eagerly evaluating the deep-mapped types over the full config type, which blows the instantiation depth\n * limit on large configs like tsdown's.\n */\nexport const createMergeConfigFn = <UserConfig extends AnyObj, BaseConfig extends ReadonlyDeep<UserConfig>>(\n baseConfig: BaseConfig,\n): OptionalMergeConfigFn<UserConfig, BaseConfig> => {\n // We don't care about the specific type of userConfig here because we assert mergeConfigFn as the correct type below\n const mergeConfigFn = (userConfig?: AnyObj, options?: { arrays?: ArrayMergeMode }) => {\n // Return a clone so consumers can't mutate the shared base config\n // oxlint-disable-next-line unicorn/prefer-structured-clone - structuredClone does not properly clone functions\n if (userConfig === undefined) return cloneDeep(baseConfig)\n // Clone both target and source so we never mutate the original objects\n // oxlint-disable-next-line unicorn/prefer-structured-clone - structuredClone does not properly clone functions\n return merge(cloneDeep(baseConfig), cloneDeep(userConfig), options?.arrays ?? \"merge\")\n }\n\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion\n return mergeConfigFn as OptionalMergeConfigFn<UserConfig, BaseConfig>\n}\n"],"mappings":";;;AAYA,MAAM,SAA6C,QAAW,QAAW,WACvE,UAAU,QAAQ,SAAS,UAAmB,aAAsB;CAClE,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;CAE9B,OAAO,WAAW,UAAU,SAAS,OAAO,QAAQ,IAAI;AAC1D,CAAC;;;;;;;;;AAmDH,MAAa,uBACX,eACkD;CAElD,MAAM,iBAAiB,YAAqB,YAA0C;EAGpF,IAAI,eAAe,KAAA,GAAW,OAAO,UAAU,UAAU;EAGzD,OAAO,MAAM,UAAU,UAAU,GAAG,UAAU,UAAU,GAAG,SAAS,UAAU,OAAO;CACvF;CAGA,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adamhl8/configs",
3
- "version": "0.24.13",
3
+ "version": "0.26.0",
4
4
  "homepage": "https://github.com/adamhl8/configs",
5
5
  "bugs": {
6
6
  "url": "https://github.com/adamhl8/configs/issues"
@@ -31,6 +31,10 @@
31
31
  ".": {
32
32
  "types": "./dist/index.d.ts",
33
33
  "import": "./dist/index.js"
34
+ },
35
+ "./env": {
36
+ "types": "./dist/env/index.d.ts",
37
+ "import": "./dist/env/index.js"
34
38
  }
35
39
  },
36
40
  "publishConfig": {
@@ -48,7 +52,10 @@
48
52
  "@adamhl8/eslint-plugin-clean-modules": "^0.1.3",
49
53
  "@commitlint/config-conventional": "^21.2.0",
50
54
  "@commitlint/types": "^21.2.0",
55
+ "arkenv": "^0.12.2",
56
+ "arktype": "^2.2.2",
51
57
  "es-toolkit": "^1.49.0",
58
+ "ts-explicit-errors": "^4.1.2",
52
59
  "type-fest": "^5.7.0"
53
60
  },
54
61
  "devDependencies": {