diff --git a/.changeset/swift-squids-invite.md b/.changeset/swift-squids-invite.md new file mode 100644 index 0000000000..5c490b5da8 --- /dev/null +++ b/.changeset/swift-squids-invite.md @@ -0,0 +1,5 @@ +--- +"@directus/env": patch +--- + +Fixed the treatment of environment variables when set to an empty string diff --git a/packages/env/src/lib/cast.test.ts b/packages/env/src/lib/cast.test.ts index 5a954de98a..a23dff56d7 100644 --- a/packages/env/src/lib/cast.test.ts +++ b/packages/env/src/lib/cast.test.ts @@ -77,26 +77,26 @@ describe('Casting', () => { vi.mocked(getCastFlag).mockReturnValue('string'); vi.mocked(toString).mockReturnValue('cast-value'); - expect(cast('value')).toBe('cast-value', 'key'); + expect(cast('value')).toBe('cast-value'); }); test('Uses toNumber for number types', () => { vi.mocked(getCastFlag).mockReturnValue('number'); vi.mocked(toNumber).mockReturnValue(123); - expect(cast('value')).toBe(123, 'key'); + expect(cast('value')).toBe(123); }); test('Uses toBoolean for number types', () => { vi.mocked(getCastFlag).mockReturnValue('boolean'); vi.mocked(toBoolean).mockReturnValue(false); - expect(cast('value')).toBe(false, 'key'); + expect(cast('value')).toBe(false); }); test('Uses RegExp for regex types', () => { vi.mocked(getCastFlag).mockReturnValue('regex'); - expect(cast('value')).toBeInstanceOf(RegExp, 'key'); + expect(cast('value')).toBeInstanceOf(RegExp); }); test('Uses toArray for array types', () => { @@ -109,13 +109,24 @@ describe('Casting', () => { vi.mocked(toNumber).mockImplementation((v) => v); vi.mocked(toArray).mockReturnValue([1, 2, 3]); - expect(cast('array:value')).toEqual([1, 2, 3], 'key'); + expect(cast('array:value')).toEqual([1, 2, 3]); + }); + + test('Filters empty strings values out of the array', () => { + vi.mocked(getCastFlag).mockImplementation((v) => { + if (String(v).startsWith('array')) return 'array'; + return null; + }); + + vi.mocked(toArray).mockReturnValue(['', '']); + + expect(cast('array:,')).toEqual([]); }); test('Uses tryJson for json types', () => { vi.mocked(getCastFlag).mockReturnValue('json'); vi.mocked(tryJson).mockReturnValue('cast-value'); - expect(cast('value')).toBe('cast-value', 'key'); + expect(cast('value')).toBe('cast-value'); }); }); diff --git a/packages/env/src/lib/cast.ts b/packages/env/src/lib/cast.ts index 8a8b624942..985bd7d491 100644 --- a/packages/env/src/lib/cast.ts +++ b/packages/env/src/lib/cast.ts @@ -23,7 +23,9 @@ export const cast = (value: unknown, key?: string): unknown => { case 'regex': return new RegExp(String(value)); case 'array': - return toArray(value).map((v) => cast(v)); + return toArray(value) + .map((v) => cast(v)) + .filter((v) => v !== ''); case 'json': return tryJson(value); } diff --git a/packages/env/src/utils/guess-type.test.ts b/packages/env/src/utils/guess-type.test.ts index 878d7ea50d..f72eab9356 100644 --- a/packages/env/src/utils/guess-type.test.ts +++ b/packages/env/src/utils/guess-type.test.ts @@ -43,4 +43,8 @@ describe('json', () => { test('Defaults to json for object type values', () => { expect(guessType({ hello: 'world' })).toBe('json'); }); + + test('Defaults to json for empty string', () => { + expect(guessType('')).toBe('json'); + }); }); diff --git a/packages/env/src/utils/guess-type.ts b/packages/env/src/utils/guess-type.ts index a669a3f1f1..4c4a8c443e 100644 --- a/packages/env/src/utils/guess-type.ts +++ b/packages/env/src/utils/guess-type.ts @@ -8,6 +8,7 @@ export const guessType = (value: unknown): EnvType => { if ( String(value).startsWith('0') === false && isNaN(Number(value)) === false && + String(value).length > 0 && Number(value) <= Number.MAX_SAFE_INTEGER ) { return 'number';