Skip to content

Commit

Permalink
feat: env vars should be readonly (#111)
Browse files Browse the repository at this point in the history
Co-authored-by: Julius Marminge <julius0216@outlook.com>
  • Loading branch information
Zamiell and juliusmarminge committed Aug 17, 2023
1 parent 32222dc commit 6890765
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-houses-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@t3-oss/env-core": patch
---

fix: mark type as readonly
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ dist
.vercel
.DS_Store

env.d.ts
next-env.d.ts
**/.vscode
20 changes: 16 additions & 4 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,12 @@ export function createEnv<
TShared extends Record<string, ZodType> = NonNullable<unknown>
>(
opts: EnvOptions<TPrefix, TServer, TClient, TShared>
): Simplify<
z.infer<ZodObject<TServer>> &
z.infer<ZodObject<TClient>> &
z.infer<ZodObject<TShared>>
): Readonly<
Simplify<
z.infer<ZodObject<TServer>> &
z.infer<ZodObject<TClient>> &
z.infer<ZodObject<TShared>>
>
> {
const runtimeEnv = opts.runtimeEnvStrict ?? opts.runtimeEnv ?? process.env;

Expand Down Expand Up @@ -209,6 +211,16 @@ export function createEnv<
}
return target[prop as keyof typeof target];
},
// Maybe reconsider this in the future:
// https://github.com/t3-oss/t3-env/pull/111#issuecomment-1682931526
// set(_target, prop) {
// // Readonly - this is the error message you get from assigning to a frozen object
// throw new Error(
// typeof prop === "string"
// ? `Cannot assign to read only property ${prop} of object #<Object>`
// : `Cannot assign to read only property of object #<Object>`
// );
// },
});

// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
Expand Down
79 changes: 57 additions & 22 deletions packages/core/test/smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ describe("return type is correctly inferred", () => {
},
});

expectTypeOf(env).toEqualTypeOf<{
BAR: string;
FOO_BAR: string;
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
BAR: string;
FOO_BAR: string;
}>
>();

expect(env).toMatchObject({
BAR: "bar",
Expand All @@ -128,10 +130,12 @@ describe("return type is correctly inferred", () => {
},
});

expectTypeOf(env).toEqualTypeOf<{
BAR: number;
FOO_BAR: string;
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
BAR: number;
FOO_BAR: string;
}>
>();

expect(env).toMatchObject({
BAR: 123,
Expand All @@ -149,9 +153,11 @@ describe("return type is correctly inferred", () => {
},
});

expectTypeOf(env).toEqualTypeOf<{
BAR: string;
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
BAR: string;
}>
>();

expect(env).toMatchObject({
BAR: "bar",
Expand All @@ -173,10 +179,12 @@ test("can pass number and booleans", () => {
},
});

expectTypeOf(env).toEqualTypeOf<{
PORT: number;
IS_DEV: boolean;
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
PORT: number;
IS_DEV: boolean;
}>
>();

expect(env).toMatchObject({
PORT: 123,
Expand Down Expand Up @@ -280,7 +288,7 @@ describe("client/server only mode", () => {
runtimeEnv: { FOO_BAR: "foo" },
});

expectTypeOf(env).toEqualTypeOf<{ FOO_BAR: string }>();
expectTypeOf(env).toEqualTypeOf<Readonly<{ FOO_BAR: string }>>();
expect(env).toMatchObject({ FOO_BAR: "foo" });
});

Expand All @@ -292,7 +300,7 @@ describe("client/server only mode", () => {
runtimeEnv: { BAR: "bar" },
});

expectTypeOf(env).toEqualTypeOf<{ BAR: string }>();
expectTypeOf(env).toEqualTypeOf<Readonly<{ BAR: string }>>();
expect(env).toMatchObject({ BAR: "bar" });
});

Expand Down Expand Up @@ -336,11 +344,13 @@ describe("shared can be accessed on both server and client", () => {
runtimeEnv: process.env,
});

expectTypeOf(env).toEqualTypeOf<{
NODE_ENV: "development" | "production" | "test";
BAR: string;
FOO_BAR: string;
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
NODE_ENV: "development" | "production" | "test";
BAR: string;
FOO_BAR: string;
}>
>();

test("server", () => {
const { window } = globalThis;
Expand Down Expand Up @@ -369,3 +379,28 @@ describe("shared can be accessed on both server and client", () => {
globalThis.window = window;
});
});

test("envs are readonly", () => {
const env = createEnv({
server: { BAR: z.string() },
runtimeEnv: { BAR: "bar" },
});

/**
* We currently don't enforce readonly during runtime:
* https://github.com/t3-oss/t3-env/pull/111#issuecomment-1682931526
*/

// expect(() => {
// // @ts-expect-error - envs are readonly
// env.BAR = "foo";
// }).toThrowErrorMatchingInlineSnapshot(
// '"Cannot assign to read only property BAR of object #<Object>"'
// );

// expect(env).toMatchObject({ BAR: "bar" });

// @ts-expect-error - envs are readonly
env.BAR = "foo";
expect(env).toMatchObject({ BAR: "foo" });
});
32 changes: 19 additions & 13 deletions packages/nextjs/test/smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,13 @@ test("new experimental runtime option only requires client vars", () => {
},
});

expectTypeOf(env).toEqualTypeOf<{
BAR: string;
NEXT_PUBLIC_BAR: string;
NODE_ENV: "development" | "production";
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
BAR: string;
NEXT_PUBLIC_BAR: string;
NODE_ENV: "development" | "production";
}>
>();

expect(env).toMatchObject({
BAR: "bar",
Expand All @@ -138,10 +140,12 @@ describe("return type is correctly inferred", () => {
},
});

expectTypeOf(env).toEqualTypeOf<{
BAR: string;
NEXT_PUBLIC_BAR: string;
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
BAR: string;
NEXT_PUBLIC_BAR: string;
}>
>();

expect(env).toMatchObject({
BAR: "bar",
Expand All @@ -159,10 +163,12 @@ describe("return type is correctly inferred", () => {
},
});

expectTypeOf(env).toEqualTypeOf<{
BAR: number;
NEXT_PUBLIC_BAR: string;
}>();
expectTypeOf(env).toEqualTypeOf<
Readonly<{
BAR: number;
NEXT_PUBLIC_BAR: string;
}>
>();

expect(env).toMatchObject({
BAR: 123,
Expand Down

2 comments on commit 6890765

@vercel
Copy link

@vercel vercel bot commented on 6890765 Aug 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

t3-env-nextjs – ./examples/nextjs

t3-env-nextjs-t3-oss.vercel.app
t3-env-nextjs-git-main-t3-oss.vercel.app
t3-env-nextjs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 6890765 Aug 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

t3-env – ./docs

t3-env-t3-oss.vercel.app
t3-env-git-main-t3-oss.vercel.app
t3-env.vercel.app
env.t3.gg
env.t3.wtf

Please sign in to comment.