From 9be501e89bb1bbaafbf01e74a57f5a55f1a3d903 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:05:55 +0000 Subject: [PATCH 01/11] Initial plan From d27b14ed0f0e5ef5c2860cea3cf01a7a196db2b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:11:21 +0000 Subject: [PATCH 02/11] Initial analysis - identify issue with type constraints in createEnv Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com> --- packages/arkenv/src/default-arrays.test.ts | 33 ++++++++++++++++++++ packages/arkenv/src/typescript-error-test.ts | 6 ++++ 2 files changed, 39 insertions(+) create mode 100644 packages/arkenv/src/default-arrays.test.ts create mode 100644 packages/arkenv/src/typescript-error-test.ts diff --git a/packages/arkenv/src/default-arrays.test.ts b/packages/arkenv/src/default-arrays.test.ts new file mode 100644 index 000000000..11593758c --- /dev/null +++ b/packages/arkenv/src/default-arrays.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "vitest"; +import arkenv, { type } from "../src/index"; + +describe("default values with arrays", () => { + it("should fail with current implementation - array default", () => { + // This should fail with current type constraints + expect(() => { + const env = arkenv({ + // @ts-expect-error - This should not work with current types + ARRAY: type("number[]").default(() => []), + }); + }).toThrow(); + }); + + it("should work with string-based default arrays", () => { + // This should work + const env = arkenv({ + ARRAY: "number[] = []", + }, {}); + + expect(env.ARRAY).toEqual([]); + }); + + it("should work with the type function directly", () => { + // This should work as mentioned in the issue + const envType = type({ + ARRAY: type("number[]").default(() => []), + }); + + const result = envType.assert({}); + expect(result.ARRAY).toEqual([]); + }); +}); \ No newline at end of file diff --git a/packages/arkenv/src/typescript-error-test.ts b/packages/arkenv/src/typescript-error-test.ts new file mode 100644 index 000000000..bfe003905 --- /dev/null +++ b/packages/arkenv/src/typescript-error-test.ts @@ -0,0 +1,6 @@ +import arkenv, { type } from "../src/index"; + +// This should demonstrate the TypeScript error mentioned in the issue +const Thing = arkenv({ + array: type("number.integer[]").default(() => []), +}); \ No newline at end of file From a9a4e176348af7889f209ae9e923fc3679dce329 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:17:21 +0000 Subject: [PATCH 03/11] Fix support for array defaults with type().default() syntax Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com> --- apps/www/content/docs/quickstart.mdx | 6 +++- .../src/array-defaults-integration.test.ts | 35 +++++++++++++++++++ packages/arkenv/src/create-env.test.ts | 20 +++++++++++ packages/arkenv/src/create-env.ts | 2 +- packages/arkenv/src/default-arrays.test.ts | 33 ----------------- packages/arkenv/src/type.test.ts | 10 ++++++ packages/arkenv/src/typescript-error-test.ts | 6 ---- 7 files changed, 71 insertions(+), 41 deletions(-) create mode 100644 packages/arkenv/src/array-defaults-integration.test.ts delete mode 100644 packages/arkenv/src/default-arrays.test.ts delete mode 100644 packages/arkenv/src/typescript-error-test.ts diff --git a/apps/www/content/docs/quickstart.mdx b/apps/www/content/docs/quickstart.mdx index 79df20e91..059d02aee 100644 --- a/apps/www/content/docs/quickstart.mdx +++ b/apps/www/content/docs/quickstart.mdx @@ -35,7 +35,7 @@ description: Let's get you started with a few simple steps. Add a schema to make your environment variables **validated** and **typesafe**: ```ts title="config/env.ts" twoslash - import arkenv from 'arkenv'; + import arkenv, { type } from 'arkenv'; export const env = arkenv({ // Built-in validators @@ -48,6 +48,10 @@ description: Let's get you started with a few simple steps. // Optional variables with defaults LOG_LEVEL: "'debug' | 'info' | 'warn' | 'error' = 'info'", + // Array defaults using type function + ALLOWED_ORIGINS: type("string[]").default(() => ["localhost"]), + FEATURE_FLAGS: type("string[]").default(() => []), + // Optional environment variable "API_KEY?": 'string' }); diff --git a/packages/arkenv/src/array-defaults-integration.test.ts b/packages/arkenv/src/array-defaults-integration.test.ts new file mode 100644 index 000000000..bb6e4e18b --- /dev/null +++ b/packages/arkenv/src/array-defaults-integration.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from "vitest"; +import arkenv, { type } from "./index"; + +describe("arkenv array defaults", () => { + it("should work with the exact syntax from the GitHub issue", () => { + // This is the exact code from the GitHub issue that should now work + const Thing = arkenv({ + array: type("number.integer[]").default(() => []), + }); + + expect(Thing.array).toEqual([]); + }); + + it("should work with complex array defaults", () => { + const env = arkenv({ + ALLOWED_HOSTS: type("string[]").default(() => ["localhost", "127.0.0.1"]), + FEATURE_FLAGS: type("string[]").default(() => []), + PORTS: type("number[]").default(() => [3000, 8080]), + }, {}); + + expect(env.ALLOWED_HOSTS).toEqual(["localhost", "127.0.0.1"]); + expect(env.FEATURE_FLAGS).toEqual([]); + expect(env.PORTS).toEqual([3000, 8080]); + }); + + it("should support arrays with defaults and environment overrides", () => { + const env = arkenv({ + NUMBERS: type("number[]").default(() => [1, 2, 3]), + }, { + NUMBERS: [4, 5, 6], // Non-string environment for testing + }); + + expect(env.NUMBERS).toEqual([4, 5, 6]); + }); +}); \ No newline at end of file diff --git a/packages/arkenv/src/create-env.test.ts b/packages/arkenv/src/create-env.test.ts index 25a656a45..9a2f9c751 100644 --- a/packages/arkenv/src/create-env.test.ts +++ b/packages/arkenv/src/create-env.test.ts @@ -1,6 +1,7 @@ import { styleText } from "node:util"; import { describe, expect, it } from "vitest"; import { createEnv } from "./create-env"; +import { type } from "./type"; import { indent } from "./utils"; /** @@ -83,4 +84,23 @@ describe("env", () => { expect(TEST_STRING).toBe("hello"); }); + + it("should support array types with default values", () => { + const env = createEnv({ + NUMBERS: type("number[]").default(() => [1, 2, 3]), + STRINGS: type("string[]").default(() => ["a", "b"]), + }, {}); + + expect(env.NUMBERS).toEqual([1, 2, 3]); + expect(env.STRINGS).toEqual(["a", "b"]); + }); + + it("should support array types with defaults when no environment value provided", () => { + // Test default value usage when environment variable is not set + const env = createEnv({ + NUMBERS: type("number[]").default(() => [1, 2, 3]), + }, {}); + + expect(env.NUMBERS).toEqual([1, 2, 3]); + }); }); diff --git a/packages/arkenv/src/create-env.ts b/packages/arkenv/src/create-env.ts index 431962967..b656dcbac 100644 --- a/packages/arkenv/src/create-env.ts +++ b/packages/arkenv/src/create-env.ts @@ -13,7 +13,7 @@ export type EnvSchema = type.validate; * @returns The validated environment variable schema * @throws An {@link ArkEnvError | error} if the environment variables are invalid. */ -export function createEnv>( +export function createEnv( def: EnvSchema, env: RuntimeEnvironment = process.env, ): distill.Out> { diff --git a/packages/arkenv/src/default-arrays.test.ts b/packages/arkenv/src/default-arrays.test.ts deleted file mode 100644 index 11593758c..000000000 --- a/packages/arkenv/src/default-arrays.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it } from "vitest"; -import arkenv, { type } from "../src/index"; - -describe("default values with arrays", () => { - it("should fail with current implementation - array default", () => { - // This should fail with current type constraints - expect(() => { - const env = arkenv({ - // @ts-expect-error - This should not work with current types - ARRAY: type("number[]").default(() => []), - }); - }).toThrow(); - }); - - it("should work with string-based default arrays", () => { - // This should work - const env = arkenv({ - ARRAY: "number[] = []", - }, {}); - - expect(env.ARRAY).toEqual([]); - }); - - it("should work with the type function directly", () => { - // This should work as mentioned in the issue - const envType = type({ - ARRAY: type("number[]").default(() => []), - }); - - const result = envType.assert({}); - expect(result.ARRAY).toEqual([]); - }); -}); \ No newline at end of file diff --git a/packages/arkenv/src/type.test.ts b/packages/arkenv/src/type.test.ts index 9bece9280..9aff9e222 100644 --- a/packages/arkenv/src/type.test.ts +++ b/packages/arkenv/src/type.test.ts @@ -182,4 +182,14 @@ describe("type", () => { expect(result.NUMBER_VALUE).toBe(42); expect(result.BOOLEAN_VALUE).toBe(true); }); + + it("should work with array defaults using type function", () => { + // This demonstrates the specific issue mentioned in the GitHub issue + const envType = type({ + array: type("number.integer[]").default(() => []), + }); + + const result = envType.assert({}); + expect(result.array).toEqual([]); + }); }); diff --git a/packages/arkenv/src/typescript-error-test.ts b/packages/arkenv/src/typescript-error-test.ts deleted file mode 100644 index bfe003905..000000000 --- a/packages/arkenv/src/typescript-error-test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import arkenv, { type } from "../src/index"; - -// This should demonstrate the TypeScript error mentioned in the issue -const Thing = arkenv({ - array: type("number.integer[]").default(() => []), -}); \ No newline at end of file From 80a0b2e40fddec55f8b054dffb37a41d62ad9207 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:47:24 +0000 Subject: [PATCH 04/11] Update type constraint to Record and add changeset Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com> --- .changeset/array-defaults-support.md | 23 +++++++++++++++++++++++ packages/arkenv/src/create-env.ts | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/array-defaults-support.md diff --git a/.changeset/array-defaults-support.md b/.changeset/array-defaults-support.md new file mode 100644 index 000000000..7057a0e98 --- /dev/null +++ b/.changeset/array-defaults-support.md @@ -0,0 +1,23 @@ +--- +"arkenv": minor +--- + +Support array defaults using type().default() syntax + +Fixes an issue where `type("array[]").default(() => [...])` syntax was not accepted by `createEnv` due to overly restrictive type constraints. The function now accepts any string-keyed record while still maintaining type safety through ArkType's validation system. + +**Breaking Changes:** None - this is a pure addition of functionality. + +**New Features:** +- Array defaults using `type("string[]").default(() => ["localhost"])` syntax +- Support for complex array types with defaults +- Mixed schemas combining string-based and type-based defaults + +**Examples:** +```typescript +const env = arkenv({ + ALLOWED_ORIGINS: type("string[]").default(() => ["localhost"]), + FEATURE_FLAGS: type("string[]").default(() => []), + PORTS: type("number[]").default(() => [3000, 8080]), +}); +``` \ No newline at end of file diff --git a/packages/arkenv/src/create-env.ts b/packages/arkenv/src/create-env.ts index b656dcbac..6d6a2127b 100644 --- a/packages/arkenv/src/create-env.ts +++ b/packages/arkenv/src/create-env.ts @@ -13,7 +13,7 @@ export type EnvSchema = type.validate; * @returns The validated environment variable schema * @throws An {@link ArkEnvError | error} if the environment variables are invalid. */ -export function createEnv( +export function createEnv>( def: EnvSchema, env: RuntimeEnvironment = process.env, ): distill.Out> { From a6dac3fe3715d57c17560a9b402714726ff0cf03 Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Tue, 30 Sep 2025 01:49:37 +0500 Subject: [PATCH 05/11] Apply suggestion from @github-actions[bot] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../src/array-defaults-integration.test.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/arkenv/src/array-defaults-integration.test.ts b/packages/arkenv/src/array-defaults-integration.test.ts index bb6e4e18b..1579732fd 100644 --- a/packages/arkenv/src/array-defaults-integration.test.ts +++ b/packages/arkenv/src/array-defaults-integration.test.ts @@ -12,11 +12,17 @@ describe("arkenv array defaults", () => { }); it("should work with complex array defaults", () => { - const env = arkenv({ - ALLOWED_HOSTS: type("string[]").default(() => ["localhost", "127.0.0.1"]), - FEATURE_FLAGS: type("string[]").default(() => []), - PORTS: type("number[]").default(() => [3000, 8080]), - }, {}); + const env = arkenv( + { + ALLOWED_HOSTS: type("string[]").default(() => [ + "localhost", + "127.0.0.1", + ]), + FEATURE_FLAGS: type("string[]").default(() => []), + PORTS: type("number[]").default(() => [3000, 8080]), + }, + {}, + ); expect(env.ALLOWED_HOSTS).toEqual(["localhost", "127.0.0.1"]); expect(env.FEATURE_FLAGS).toEqual([]); From 48358941759e3e47c720bb6b507f3bf2e0ea4b4a Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Tue, 30 Sep 2025 01:49:52 +0500 Subject: [PATCH 06/11] Apply suggestion from @github-actions[bot] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../arkenv/src/array-defaults-integration.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/arkenv/src/array-defaults-integration.test.ts b/packages/arkenv/src/array-defaults-integration.test.ts index 1579732fd..fa70a8f37 100644 --- a/packages/arkenv/src/array-defaults-integration.test.ts +++ b/packages/arkenv/src/array-defaults-integration.test.ts @@ -30,11 +30,14 @@ describe("arkenv array defaults", () => { }); it("should support arrays with defaults and environment overrides", () => { - const env = arkenv({ - NUMBERS: type("number[]").default(() => [1, 2, 3]), - }, { - NUMBERS: [4, 5, 6], // Non-string environment for testing - }); + const env = arkenv( + { + NUMBERS: type("number[]").default(() => [1, 2, 3]), + }, + { + NUMBERS: [4, 5, 6], // Non-string environment for testing + }, + ); expect(env.NUMBERS).toEqual([4, 5, 6]); }); From bcaa75b0152ea823803b2fe6f8387d5a07d5df25 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:50:36 +0000 Subject: [PATCH 07/11] [autofix.ci] apply automated fixes --- .../src/array-defaults-integration.test.ts | 2 +- packages/arkenv/src/create-env.test.ts | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/arkenv/src/array-defaults-integration.test.ts b/packages/arkenv/src/array-defaults-integration.test.ts index fa70a8f37..ea823bf09 100644 --- a/packages/arkenv/src/array-defaults-integration.test.ts +++ b/packages/arkenv/src/array-defaults-integration.test.ts @@ -41,4 +41,4 @@ describe("arkenv array defaults", () => { expect(env.NUMBERS).toEqual([4, 5, 6]); }); -}); \ No newline at end of file +}); diff --git a/packages/arkenv/src/create-env.test.ts b/packages/arkenv/src/create-env.test.ts index 9a2f9c751..5b0a917b7 100644 --- a/packages/arkenv/src/create-env.test.ts +++ b/packages/arkenv/src/create-env.test.ts @@ -86,10 +86,13 @@ describe("env", () => { }); it("should support array types with default values", () => { - const env = createEnv({ - NUMBERS: type("number[]").default(() => [1, 2, 3]), - STRINGS: type("string[]").default(() => ["a", "b"]), - }, {}); + const env = createEnv( + { + NUMBERS: type("number[]").default(() => [1, 2, 3]), + STRINGS: type("string[]").default(() => ["a", "b"]), + }, + {}, + ); expect(env.NUMBERS).toEqual([1, 2, 3]); expect(env.STRINGS).toEqual(["a", "b"]); @@ -97,9 +100,12 @@ describe("env", () => { it("should support array types with defaults when no environment value provided", () => { // Test default value usage when environment variable is not set - const env = createEnv({ - NUMBERS: type("number[]").default(() => [1, 2, 3]), - }, {}); + const env = createEnv( + { + NUMBERS: type("number[]").default(() => [1, 2, 3]), + }, + {}, + ); expect(env.NUMBERS).toEqual([1, 2, 3]); }); From 77d51fb9bbb0537575e2ea65cf6c329be4cb62a5 Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Tue, 30 Sep 2025 01:55:41 +0500 Subject: [PATCH 08/11] Enhance array defaults support in type() syntax Fix to an issue where `type("array[]").default(() => [...])` syntax was not accepted by `createEnv` due to overly restrictive type constraints. The function now accepts any string-keyed record while still maintaining type safety through ArkType's validation system. Added support for array defaults using `type("string[]").default(() => [])` syntax and complex array types. --- .changeset/array-defaults-support.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.changeset/array-defaults-support.md b/.changeset/array-defaults-support.md index 7057a0e98..f48cd1ccc 100644 --- a/.changeset/array-defaults-support.md +++ b/.changeset/array-defaults-support.md @@ -2,14 +2,12 @@ "arkenv": minor --- -Support array defaults using type().default() syntax +#### Support array defaults using type().default() syntax -Fixes an issue where `type("array[]").default(() => [...])` syntax was not accepted by `createEnv` due to overly restrictive type constraints. The function now accepts any string-keyed record while still maintaining type safety through ArkType's validation system. - -**Breaking Changes:** None - this is a pure addition of functionality. +Fix to an issue where `type("array[]").default(() => [...])` syntax was not accepted by `createEnv` due to overly restrictive type constraints. The function now accepts any string-keyed record while still maintaining type safety through ArkType's validation system. **New Features:** -- Array defaults using `type("string[]").default(() => ["localhost"])` syntax +- Array defaults to empty using `type("string[]").default(() => [])` syntax - Support for complex array types with defaults - Mixed schemas combining string-based and type-based defaults @@ -18,6 +16,6 @@ Fixes an issue where `type("array[]").default(() => [...])` syntax was not accep const env = arkenv({ ALLOWED_ORIGINS: type("string[]").default(() => ["localhost"]), FEATURE_FLAGS: type("string[]").default(() => []), - PORTS: type("number[]").default(() => [3000, 8080]), + PORT: "number.port", }); -``` \ No newline at end of file +``` From 43526ef0800e49a5b15a8fe080c9d5c244dbf186 Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Mon, 6 Oct 2025 17:11:06 +0300 Subject: [PATCH 09/11] Update packages/arkenv/src/create-env.ts --- packages/arkenv/src/create-env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arkenv/src/create-env.ts b/packages/arkenv/src/create-env.ts index 6d6a2127b..3b5fe9f58 100644 --- a/packages/arkenv/src/create-env.ts +++ b/packages/arkenv/src/create-env.ts @@ -13,7 +13,7 @@ export type EnvSchema = type.validate; * @returns The validated environment variable schema * @throws An {@link ArkEnvError | error} if the environment variables are invalid. */ -export function createEnv>( +export function createEnv>( def: EnvSchema, env: RuntimeEnvironment = process.env, ): distill.Out> { From 8db3377a0e6578336759bf57e140934739c8284e Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Mon, 6 Oct 2025 19:24:52 +0500 Subject: [PATCH 10/11] feat(arkenv): add array type and example env - Added ALLOWED_ORIGINS array type - Added .env.example file --- apps/playgrounds/node/.env.example | 3 +++ apps/playgrounds/node/index.ts | 6 ++++-- packages/arkenv/src/create-env.ts | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 apps/playgrounds/node/.env.example diff --git a/apps/playgrounds/node/.env.example b/apps/playgrounds/node/.env.example new file mode 100644 index 000000000..4a191eaba --- /dev/null +++ b/apps/playgrounds/node/.env.example @@ -0,0 +1,3 @@ +HOST=localhost +PORT=3000 +NODE_ENV=development diff --git a/apps/playgrounds/node/index.ts b/apps/playgrounds/node/index.ts index 1199ff0c4..a09dfa791 100644 --- a/apps/playgrounds/node/index.ts +++ b/apps/playgrounds/node/index.ts @@ -1,9 +1,10 @@ -import arkenv from "arkenv"; +import arkenv, { type } from "arkenv"; const env = arkenv({ HOST: "string.host", PORT: "number.port", NODE_ENV: "'development' | 'production' | 'test' = 'development'", + ALLOWED_ORIGINS: type("string[]").default(() => []), }); // Automatically validate and parse process.env @@ -11,7 +12,8 @@ const env = arkenv({ const host = env.HOST; const port = env.PORT; const nodeEnv = env.NODE_ENV; +const allowedOrigins = env.ALLOWED_ORIGINS; -console.log({ host, port, nodeEnv }); +console.log({ host, port, nodeEnv, allowedOrigins }); export default env; diff --git a/packages/arkenv/src/create-env.ts b/packages/arkenv/src/create-env.ts index 3b5fe9f58..d2d0487a9 100644 --- a/packages/arkenv/src/create-env.ts +++ b/packages/arkenv/src/create-env.ts @@ -6,6 +6,11 @@ type RuntimeEnvironment = Record; export type EnvSchema = type.validate; +/** + * TODO: If possible, find a better type than "const T extends Record", + * and be as close as possible to the type accepted by ArkType's `type`. + */ + /** * Create an environment variables object from a schema and an environment * @param def - The environment variable schema From 3ff8f4cdb0e99a76a2a900e37d10270c5f4264a0 Mon Sep 17 00:00:00 2001 From: Yam C Borodetsky Date: Mon, 6 Oct 2025 19:26:55 +0500 Subject: [PATCH 11/11] feat: Add array defaults support - Array defaults to empty - Support complex array types - Mixed schemas supported --- .changeset/array-defaults-support.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.changeset/array-defaults-support.md b/.changeset/array-defaults-support.md index f48cd1ccc..2f01bae01 100644 --- a/.changeset/array-defaults-support.md +++ b/.changeset/array-defaults-support.md @@ -6,12 +6,13 @@ Fix to an issue where `type("array[]").default(() => [...])` syntax was not accepted by `createEnv` due to overly restrictive type constraints. The function now accepts any string-keyed record while still maintaining type safety through ArkType's validation system. -**New Features:** +### New Features - Array defaults to empty using `type("string[]").default(() => [])` syntax - Support for complex array types with defaults - Mixed schemas combining string-based and type-based defaults -**Examples:** +### Example + ```typescript const env = arkenv({ ALLOWED_ORIGINS: type("string[]").default(() => ["localhost"]),