Skip to content

Commit

Permalink
add precise types
Browse files Browse the repository at this point in the history
  • Loading branch information
dferber90 committed Jan 24, 2025
1 parent 1775370 commit 5bca4c4
Show file tree
Hide file tree
Showing 10 changed files with 63 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { flag } from '@vercel/flags/next';

export const firstPrecomputedFlag = flag({
export const firstPrecomputedFlag = flag<boolean>({
key: 'first-precomputed-flag',
decide: () => Math.random() > 0.5,
});

export const secondPrecomputedFlag = flag({
export const secondPrecomputedFlag = flag<boolean>({
key: 'second-precomputed-flag',
decide: () => Date.now() % 2 === 0,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { flag } from '@vercel/flags/next';

export const basicEdgeMiddlewareFlag = flag({
export const basicEdgeMiddlewareFlag = flag<boolean>({
key: 'basic-edge-middleware-flag',
decide({ cookies }) {
return cookies.get('basic-edge-middleware-flag')?.value === '1';
Expand Down
2 changes: 1 addition & 1 deletion examples/snippets/app/getting-started/overview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DemoFlag } from '@/components/demo-flag';
import { ReloadButton } from './reload-button';

// declare a feature flag
const randomFlag = flag({
const randomFlag = flag<boolean>({
key: 'random-flag',
decide() {
// this flag will be on for 50% of visitors
Expand Down
2 changes: 1 addition & 1 deletion examples/snippets/flags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { flag } from '@vercel/flags/next';

export const exampleFlag = flag({
export const exampleFlag = flag<boolean>({
key: 'example-flag',
decide() {
return true;
Expand Down
2 changes: 1 addition & 1 deletion examples/snippets/lib/pages-router-precomputed/flags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { flag } from '@vercel/flags/next';

export const exampleFlag = flag({
export const exampleFlag = flag<boolean>({
key: 'pages-router-precomputed-example-flag',
decide() {
return true;
Expand Down
2 changes: 1 addition & 1 deletion examples/summer-sale/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const showFreeDeliveryBannerFlag = flag<boolean>({
],
});

export const countryFlag = flag({
export const countryFlag = flag<boolean>({
key: 'country',
async decide({ headers }) {
return headers.get('accept-language') || 'no accept-lanugage header';
Expand Down
2 changes: 1 addition & 1 deletion packages/flags/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Create a file called flags.ts in your project and declare your first feature fla
// app/flags.tsx
import { flag } from '@vercel/flags/next';

export const exampleFlag = flag({
export const exampleFlag = flag<boolean>({
key: 'example-flag',
decide() {
return true;
Expand Down
61 changes: 38 additions & 23 deletions packages/flags/src/next/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('flag on app router', () => {
it('allows declaring a flag', async () => {
mocks.headers.mockReturnValueOnce(new Headers());

const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: () => false,
});
Expand All @@ -62,7 +62,7 @@ describe('flag on app router', () => {
it('caches for the duration of a request', async () => {
let i = 0;
const decide = vi.fn(() => i++);
const f = flag({ key: 'first-flag', decide });
const f = flag<number>({ key: 'first-flag', decide });

// first request using the flag twice
const headersOfFirstRequest = new Headers();
Expand Down Expand Up @@ -94,7 +94,7 @@ describe('flag on app router', () => {

const mockDecide = vi.fn(() => promise);

const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: mockDecide,
});
Expand All @@ -119,7 +119,7 @@ describe('flag on app router', () => {

it('respects overrides', async () => {
const decide = vi.fn(() => false);
const f = flag({ key: 'first-flag', decide });
const f = flag<boolean>({ key: 'first-flag', decide });

// first request using the flag twice
const headersOfFirstRequest = new Headers();
Expand All @@ -139,7 +139,11 @@ describe('flag on app router', () => {

it('uses precomputed values', async () => {
const decide = vi.fn(() => true);
const f = flag({ key: 'first-flag', decide, options: [false, true] });
const f = flag<boolean>({
key: 'first-flag',
decide,
options: [false, true],
});
const flagGroup = [f];
const code = await precompute(flagGroup);
expect(decide).toHaveBeenCalledTimes(1);
Expand All @@ -149,7 +153,7 @@ describe('flag on app router', () => {

it('uses precomputed values even when options are inferred', async () => {
const decide = vi.fn(() => true);
const f = flag({ key: 'first-flag', decide });
const f = flag<boolean>({ key: 'first-flag', decide });
const flagGroup = [f];
const code = await precompute(flagGroup);
expect(decide).toHaveBeenCalledTimes(1);
Expand All @@ -166,7 +170,7 @@ describe('flag on app router', () => {
const mockDecide = vi.fn(() => promise);
const catchFn = vi.fn();

const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: mockDecide,
defaultValue: false,
Expand All @@ -187,16 +191,18 @@ describe('flag on app router', () => {
});

it('falls back to the defaultValue when a decide function returns undefined', async () => {
const syncFlag = flag({
const syncFlag = flag<boolean>({
key: 'sync-flag',
// @ts-expect-error this is the case we are testing
decide: () => undefined,
defaultValue: true,
});

await expect(syncFlag()).resolves.toEqual(true);

const asyncFlag = flag({
const asyncFlag = flag<boolean>({
key: 'async-flag',
// @ts-expect-error this is the case we are testing
decide: async () => undefined,
defaultValue: true,
});
Expand All @@ -205,8 +211,9 @@ describe('flag on app router', () => {
});

it('throws an error when the decide function returns undefined and no defaultValue is provided', async () => {
const syncFlag = flag({
const syncFlag = flag<boolean>({
key: 'sync-flag',
// @ts-expect-error this is the case we are testing
decide: () => undefined,
});

Expand Down Expand Up @@ -235,7 +242,7 @@ describe('flag on pages router', () => {
it('allows declaring a flag', async () => {
mocks.headers.mockReturnValueOnce(new Headers());

const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: () => false,
});
Expand All @@ -253,7 +260,7 @@ describe('flag on pages router', () => {
it('caches for the duration of a request', async () => {
let i = 0;
const decide = vi.fn(() => i++);
const f = flag({ key: 'first-flag', decide });
const f = flag<number>({ key: 'first-flag', decide });

const [firstRequest, socket1] = createRequest();
const [secondRequest, socket2] = createRequest();
Expand Down Expand Up @@ -285,7 +292,7 @@ describe('flag on pages router', () => {

const mockDecide = vi.fn(() => promise);

const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: mockDecide,
});
Expand All @@ -311,7 +318,7 @@ describe('flag on pages router', () => {
const mockDecide = vi.fn(() => {
throw new Error('custom error');
});
const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: mockDecide,
});
Expand All @@ -325,16 +332,18 @@ describe('flag on pages router', () => {

it('falls back to the defaultValue when a decide function returns undefined', async () => {
const [firstRequest, socket1] = createRequest();
const syncFlag = flag({
const syncFlag = flag<boolean>({
key: 'sync-flag',
// @ts-expect-error this is the case we are testing
decide: () => undefined,
defaultValue: true,
});

await expect(syncFlag(firstRequest)).resolves.toEqual(true);

const asyncFlag = flag({
const asyncFlag = flag<boolean>({
key: 'async-flag',
// @ts-expect-error this is the case we are testing
decide: async () => undefined,
defaultValue: true,
});
Expand All @@ -346,8 +355,9 @@ describe('flag on pages router', () => {

it('throws an error when the decide function returns undefined and no defaultValue is provided', async () => {
const [firstRequest, socket1] = createRequest();
const syncFlag = flag({
const syncFlag = flag<boolean>({
key: 'sync-flag',
// @ts-expect-error this is the case we are testing
decide: () => undefined,
});

Expand All @@ -370,7 +380,7 @@ describe('flag on pages router', () => {

it('respects overrides', async () => {
const decide = vi.fn(() => false);
const f = flag({ key: 'first-flag', decide });
const f = flag<boolean>({ key: 'first-flag', decide });
const override = await encrypt({ 'first-flag': true });

const [firstRequest, socket1] = createRequest({
Expand All @@ -383,7 +393,11 @@ describe('flag on pages router', () => {

it('uses precomputed values', async () => {
const decide = vi.fn(() => true);
const f = flag({ key: 'first-flag', decide, options: [false, true] });
const f = flag<boolean>({
key: 'first-flag',
decide,
options: [false, true],
});
const flagGroup = [f];
const code = await precompute(flagGroup);
expect(decide).toHaveBeenCalledTimes(1);
Expand All @@ -400,7 +414,7 @@ describe('flag on pages router', () => {
const mockDecide = vi.fn(() => promise);
const catchFn = vi.fn();

const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: mockDecide,
defaultValue: false,
Expand Down Expand Up @@ -428,7 +442,7 @@ describe('dynamic io', () => {
(error as any).digest = 'DYNAMIC_SERVER_USAGE;dynamic usage error';
throw error;
});
const f = flag({
const f = flag<boolean>({
key: 'first-flag',
decide: mockDecide,
defaultValue: false,
Expand Down Expand Up @@ -456,7 +470,7 @@ describe('adapters', () => {

mocks.headers.mockReturnValueOnce(new Headers());

const f = flag({
const f = flag<number>({
key: 'adapter-flag',
adapter: testAdapter(5),
});
Expand All @@ -471,8 +485,9 @@ describe('adapters', () => {

mocks.headers.mockReturnValueOnce(new Headers());

const f = flag({
const f = flag<boolean>({
key: 'adapter-flag',
// @ts-expect-error this is the case we are testing
adapter: testAdapter(undefined),
});

Expand Down
28 changes: 16 additions & 12 deletions packages/flags/src/next/precompute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('generatePermutations', () => {
it('should infer boolean options', async () => {
process.env.FLAGS_SECRET = crypto.randomBytes(32).toString('base64url');

const flagA = flag({ key: 'a', decide: () => false });
const flagA = flag<boolean>({ key: 'a', decide: () => false });
await expectPermutations([flagA], [{ a: false }, { a: true }]);
});
});
Expand All @@ -44,7 +44,11 @@ describe('generatePermutations', () => {
it('should not infer any options', async () => {
process.env.FLAGS_SECRET = crypto.randomBytes(32).toString('base64url');

const flagA = flag({ key: 'a', decide: () => false, options: [] });
const flagA = flag<boolean>({
key: 'a',
decide: () => false,
options: [],
});
await expectPermutations([flagA], []);
});
});
Expand Down Expand Up @@ -89,12 +93,12 @@ describe('generatePermutations', () => {
it('should generate permutations', async () => {
process.env.FLAGS_SECRET = crypto.randomBytes(32).toString('base64url');

const flagA = flag({
const flagA = flag<boolean>({
key: 'a',
decide: () => false,
});

const flagB = flag({
const flagB = flag<boolean>({
key: 'b',
decide: () => false,
});
Expand All @@ -115,17 +119,17 @@ describe('generatePermutations', () => {
it('should generate permutations', async () => {
process.env.FLAGS_SECRET = crypto.randomBytes(32).toString('base64url');

const flagA = flag({
const flagA = flag<boolean>({
key: 'a',
decide: () => false,
});

const flagB = flag({
const flagB = flag<boolean>({
key: 'b',
decide: () => false,
});

const flagC = flag({
const flagC = flag<string>({
key: 'c',
decide: () => 'two',
options: ['one', 'two', 'three'],
Expand Down Expand Up @@ -157,17 +161,17 @@ describe('generatePermutations', () => {
it('should generate permutations', async () => {
process.env.FLAGS_SECRET = crypto.randomBytes(32).toString('base64url');

const flagA = flag({
const flagA = flag<boolean>({
key: 'a',
decide: () => false,
});

const flagB = flag({
const flagB = flag<boolean>({
key: 'b',
decide: () => false,
});

const flagC = flag({
const flagC = flag<string>({
key: 'c',
decide: () => 'two',
options: ['one', 'two', 'three'],
Expand All @@ -189,8 +193,8 @@ describe('getPrecomputed', () => {
it('should return the precomputed value', async () => {
process.env.FLAGS_SECRET = crypto.randomBytes(32).toString('base64url');

const flagA = flag({ key: 'a', decide: () => true });
const flagB = flag({ key: 'b', decide: () => false });
const flagA = flag<boolean>({ key: 'a', decide: () => true });
const flagB = flag<boolean>({ key: 'b', decide: () => false });

const group = [flagA, flagB];
const code = await serialize(group, [true, false]);
Expand Down
2 changes: 1 addition & 1 deletion packages/flags/src/sveltekit/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('getProviderData', () => {

describe('flag', () => {
it('defines a key', async () => {
const f = flag({ key: 'first-flag', decide: () => false });
const f = flag<boolean>({ key: 'first-flag', decide: () => false });
expect(f).toHaveProperty('key', 'first-flag');
});
});

0 comments on commit 5bca4c4

Please sign in to comment.