ronantakizawa/codereview-bench huggingface dataset:
Repository: directus/directus
File: api/src/auth/drivers/openid.ts (TypeScript)

Code before:

	override async login(user: User): Promise<void> {
		return this.refresh(user);
	}

	override async refresh(user: User): Promise<void> {
		const logger = useLogger();

		let authData = user.auth_data as AuthData;

		if (typeof authData === 'string') {
			try {
				authData = parseJSON(authData);
			} catch {
				logger.warn(`[OpenID] Session data isn't valid JSON: ${authData}`);
			}
		}

		if (authData?.['refreshToken']) {
			try {
				const client = await this.getClient();
				const tokenSet = await client.refresh(authData['refreshToken']);

				// Update user refreshToken if provided
				if (tokenSet.refresh_token) {
					const usersService = await this.getUsersService();

					await usersService.updateOne(user.id, {
						auth_data: JSON.stringify({ refreshToken: tokenSet.refresh_token }),
					});
				}
			} catch (e) {
				throw handleError(e);
			}
		}
	}
}

const handleError = (e: any) => {
	const logger = useLogger();

	if (e instanceof errors.OPError) {
		if (e.error === 'invalid_grant') {
			// Invalid token
			logger.warn(e, `[OpenID] Invalid grant`);
			return new InvalidTokenError();
		}

		// Server response error
		logger.warn(e, `[OpenID] Unknown OP error`);
		return new ServiceUnavailableError({

Review comment (suggestion):
```suggestion
					const usersService = this.getUsersService(schema);
```

Code after:

	override async login(user: User): Promise<void> {
		return this.refresh(user);
	}

	override async refresh(user: User): Promise<void> {
		const logger = useLogger();

		let authData = user.auth_data as AuthData;

		if (typeof authData === 'string') {
			try {
				authData = parseJSON(authData);
			} catch {
				logger.warn(`[OpenID] Session data isn't valid JSON: ${authData}`);
			}
		}

		if (authData?.['refreshToken']) {
			try {
				const client = await this.getClient();
				const tokenSet = await client.refresh(authData['refreshToken']);

				// Update user refreshToken if provided
				if (tokenSet.refresh_token) {
					const usersService = this.getUsersService(await getSchema());

					await usersService.updateOne(user.id, {
						auth_data: JSON.stringify({ refreshToken: tokenSet.refresh_token }),
					});
				}
			} catch (e) {
				throw handleError(e);
			}
		}
	}
}

const handleError = (e: any) => {
	const logger = useLogger();

	if (e instanceof errors.OPError) {
		if (e.error === 'invalid_grant') {
			// Invalid token
			logger.warn(e, `[OpenID] Invalid grant`);
			return new InvalidTokenError();
		}

		// Server response error
		logger.warn(e, `[OpenID] Unknown OP error`);
		return new ServiceUnavailableError({
Repository: directus/directus
File: api/src/auth/auth.ts (TypeScript)

Code before:
import type { SchemaOverview } from '@directus/types';
import type { Knex } from 'knex';
import { UsersService } from '../services/users.js';
import type { AuthDriverOptions, User } from '../types/index.js';
import { getSchema } from '../utils/get-schema.js';

export abstract class AuthDriver {
	knex: Knex;

	constructor(options: AuthDriverOptions, _config: Record<string, any>) {
		this.knex = options.knex;
	}

	protected async getUsersService(schema?: SchemaOverview): Promise<UsersService> {
		return new UsersService({
			knex: this.knex,
			schema: schema ?? (await getSchema()),
		});
	}

	/**
	 * Get user id for a given provider payload
	 *
	 * @param payload Any data that the user might've provided
	 * @throws InvalidCredentialsError
	 * @return User id of the identifier
	 */
	abstract getUserID(payload: Record<string, any>): Promise<string>;

	/**
	 * Verify user password
	 *
	 * @param user User information
	 * @param password User password
	 * @throws InvalidCredentialsError
	 */
	abstract verify(user: User, password?: string): Promise<void>;

	/**
	 * Check with the (external) provider if the user is allowed entry to Directus
	 *
	 * @param _user User information
	 * @param _payload Any data that the user might've provided

Review comment (suggestion):
I dont think we need this function to be `async`, all usages (Except 1 that has schema available) get the schema passed in. This way we do not have to `await` this function everywhere for a fallback that isnt used 😄 
```suggestion
	protected getUsersService(schema: SchemaOverview): UsersService {
		return new UsersService({
			knex: this.knex,
			schema,
		});
```

Code after:
import type { SchemaOverview } from '@directus/types';
import type { Knex } from 'knex';
import { UsersService } from '../services/users.js';
import type { AuthDriverOptions, User } from '../types/index.js';

export abstract class AuthDriver {
	knex: Knex;

	constructor(options: AuthDriverOptions, _config: Record<string, any>) {
		this.knex = options.knex;
	}

	protected getUsersService(schema: SchemaOverview): UsersService {
		return new UsersService({
			knex: this.knex,
			schema,
		});
	}

	/**
	 * Get user id for a given provider payload
	 *
	 * @param payload Any data that the user might've provided
	 * @throws InvalidCredentialsError
	 * @return User id of the identifier
	 */
	abstract getUserID(payload: Record<string, any>): Promise<string>;

	/**
	 * Verify user password
	 *
	 * @param user User information
	 * @param password User password
	 * @throws InvalidCredentialsError
	 */
	abstract verify(user: User, password?: string): Promise<void>;

	/**
	 * Check with the (external) provider if the user is allowed entry to Directus
	 *
	 * @param _user User information
	 * @param _payload Any data that the user might've provided
	 * @throws InvalidCredentialsError
Repository: directus/directus
File: sdk/src/rest/utils/query-to-params.ts (TypeScript)

Code before:

		// backwards JS compatibility for string pages
		if (typeof query.page === 'string' && Number(query.page) >= 1) {
			params['page'] = query.page;
		}
	}

	if (query.deep && typeof query.deep === 'object' && Object.keys(query.deep).length > 0) {
		params['deep'] = JSON.stringify(query.deep);
	}

	if (query.alias && typeof query.alias === 'object' && Object.keys(query.alias).length > 0) {
		params['alias'] = JSON.stringify(query.alias);
	}

	if (query.aggregate && typeof query.aggregate === 'object' && Object.keys(query.aggregate).length > 0) {
		params['aggregate'] = JSON.stringify(query.aggregate);
	}

	if (query.groupBy) {
		if (Array.isArray(query.groupBy) && query.groupBy.length > 0) {
			params['groupBy'] = query.groupBy.join(',');
		}

		// backwards JS compatibility for string pages
		if (typeof query.groupBy === 'string' && String(query.groupBy).length > 0) {
			params['groupBy'] = query.groupBy;
		}
	}

	for (const [key, value] of Object.entries(query)) {
		if (key in params || knownQueryKeys.includes(key)) continue;

		if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
			params[key] = String(value);
		} else {
			params[key] = JSON.stringify(value);
		}
	}

	return params;
};


Review comment (suggestion):
An empty string is falsey, this check should be unnecessary.
```suggestion
		if (typeof query.groupBy === 'string') {
```


Code after:
		if (isArray(query.groupBy)) params['groupBy'] = query.groupBy.join(',');
		// backwards JS compatibility for `groupBy: "id,name"`
		if (isString(query.groupBy)) params['groupBy'] = query.groupBy;
	}

	if (isString(query.version)) params['version'] = query.version;

	if (query.versionRaw) {
		if (isBoolean(query.versionRaw)) params['versionRaw'] = String(query.versionRaw);
		// backwards JS compatibility for `versionRaw: "true"`
		if (isString(query.versionRaw)) params['versionRaw'] = query.versionRaw;
	}

	// parse custom parameters
	for (const [key, value] of Object.entries(query)) {
		if (knownQueryKeys.includes(key)) continue;
		let stringValue: string | undefined;

		if (typeof value === 'string') {
			stringValue = value;
		} else {
			// `JSON.stringify` can return `undefined` for types it cannot serialize
			// Example: JSON.stringify(() => {}) -> undefined
			stringValue = JSON.stringify(value) as string | undefined;
		}

		if (stringValue) {
			params[key] = stringValue;
		}
	}

	return params;
};
Repository: directus/directus
File: sdk/src/rest/utils/query-to-params.ts (TypeScript)

Code before:

	if (query.deep && typeof query.deep === 'object' && Object.keys(query.deep).length > 0) {
		params['deep'] = JSON.stringify(query.deep);
	}

	if (query.alias && typeof query.alias === 'object' && Object.keys(query.alias).length > 0) {
		params['alias'] = JSON.stringify(query.alias);
	}

	if (query.aggregate && typeof query.aggregate === 'object' && Object.keys(query.aggregate).length > 0) {
		params['aggregate'] = JSON.stringify(query.aggregate);
	}

	if (query.groupBy) {
		if (Array.isArray(query.groupBy) && query.groupBy.length > 0) {
			params['groupBy'] = query.groupBy.join(',');
		}

		// backwards JS compatibility for string pages
		if (typeof query.groupBy === 'string' && String(query.groupBy).length > 0) {
			params['groupBy'] = query.groupBy;
		}
	}

	for (const [key, value] of Object.entries(query)) {
		if (key in params || knownQueryKeys.includes(key)) continue;

		if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
			params[key] = String(value);
		} else {
			params[key] = JSON.stringify(value);
		}
	}

	return params;
};


Review comment (suggestion):
As params will only ever contain knownQueryKeys we can remove the `key in params` check
 ```suggestion
		if (knownQueryKeys.includes(key)) continue;
```

Code after:

	if (query.versionRaw) {
		if (isBoolean(query.versionRaw)) params['versionRaw'] = String(query.versionRaw);
		// backwards JS compatibility for `versionRaw: "true"`
		if (isString(query.versionRaw)) params['versionRaw'] = query.versionRaw;
	}

	// parse custom parameters
	for (const [key, value] of Object.entries(query)) {
		if (knownQueryKeys.includes(key)) continue;
		let stringValue: string | undefined;

		if (typeof value === 'string') {
			stringValue = value;
		} else {
			// `JSON.stringify` can return `undefined` for types it cannot serialize
			// Example: JSON.stringify(() => {}) -> undefined
			stringValue = JSON.stringify(value) as string | undefined;
		}

		if (stringValue) {
			params[key] = stringValue;
		}
	}

	return params;
};
Repository: directus/directus
File: sdk/src/rest/utils/query-to-params.ts (TypeScript)

Code before:
		}

		// backwards JS compatibility for string pages
		if (typeof query.page === 'string' && Number(query.page) >= 1) {
			params['page'] = query.page;
		}
	}

	if (query.deep && typeof query.deep === 'object' && Object.keys(query.deep).length > 0) {
		params['deep'] = JSON.stringify(query.deep);
	}

	if (query.alias && typeof query.alias === 'object' && Object.keys(query.alias).length > 0) {
		params['alias'] = JSON.stringify(query.alias);
	}

	if (query.aggregate && typeof query.aggregate === 'object' && Object.keys(query.aggregate).length > 0) {
		params['aggregate'] = JSON.stringify(query.aggregate);
	}

	if (query.groupBy) {
		if (Array.isArray(query.groupBy) && query.groupBy.length > 0) {
			params['groupBy'] = query.groupBy.join(',');
		}

		// backwards JS compatibility for string pages
		if (typeof query.groupBy === 'string') {
			params['groupBy'] = query.groupBy;
		}
	}

	for (const [key, value] of Object.entries(query)) {
		if (knownQueryKeys.includes(key)) continue;

		if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
			params[key] = String(value);
		} else {
			params[key] = JSON.stringify(value);
		}
	}

	return params;
};


Review comment (suggestion):
Small nitpick 😅 
```suggestion
		// backwards JS compatibility for `groupBy: "id,name"`
```

Code after:
	if (query.groupBy) {
		if (isArray(query.groupBy)) params['groupBy'] = query.groupBy.join(',');
		// backwards JS compatibility for `groupBy: "id,name"`
		if (isString(query.groupBy)) params['groupBy'] = query.groupBy;
	}

	if (isString(query.version)) params['version'] = query.version;

	if (query.versionRaw) {
		if (isBoolean(query.versionRaw)) params['versionRaw'] = String(query.versionRaw);
		// backwards JS compatibility for `versionRaw: "true"`
		if (isString(query.versionRaw)) params['versionRaw'] = query.versionRaw;
	}

	// parse custom parameters
	for (const [key, value] of Object.entries(query)) {
		if (knownQueryKeys.includes(key)) continue;
		let stringValue: string | undefined;

		if (typeof value === 'string') {
			stringValue = value;
		} else {
			// `JSON.stringify` can return `undefined` for types it cannot serialize
			// Example: JSON.stringify(() => {}) -> undefined
			stringValue = JSON.stringify(value) as string | undefined;
		}

		if (stringValue) {
			params[key] = stringValue;
		}
	}

	return params;
};
Repository: directus/directus
File: sdk/src/rest/utils/query-to-params.test.ts (TypeScript)

Code before:
		expect(result).toEqual({
			aggregate: '{"count":"*","avg":"price"}',
		});
	});

	test('should format groupBy parameter', () => {
		const result = queryToParams({
			groupBy: ['category', 'status'],
		});

		expect(result).toEqual({
			groupBy: 'category,status',
		});
	});

	test('should format version parameter', () => {
		const result = queryToParams({
			version: 'draft',
		});

		expect(result).toEqual({
			version: 'draft',
		});
	});

	test('should format version parameter', () => {
		const result = queryToParams({
			versionRaw: true,
		});

		expect(result).toEqual({
			versionRaw: 'true',
		});
	});

	test('should handle various custom unknown parameters', () => {
		const result = queryToParams({
			customString: 'value',
			customNumber: 42,
			customBool: true,
			customObject: { key: 'value' },
		});

		expect(result).toEqual({
			customString: 'value',
			customNumber: '42',
			customBool: 'true',
			customObject: '{"key":"value"}',
		});
	});
});

Review comment (suggestion):
```suggestion
	test('should format versionRaw parameter', () => {
```

Code after:
			},
		});

		expect(result).toEqual({
			aggregate: '{"count":"*","avg":"price"}',
		});
	});

	test('should format groupBy parameter', () => {
		const result = queryToParams({
			groupBy: ['category', 'status'],
		});

		expect(result).toEqual({
			groupBy: 'category,status',
		});
	});

	test('should format version parameter', () => {
		const result = queryToParams({
			version: 'draft',
		});

		expect(result).toEqual({
			version: 'draft',
		});
	});

	test('should format versionRaw parameter', () => {
		const result = queryToParams({
			versionRaw: true,
		});

		expect(result).toEqual({
			versionRaw: 'true',
		});
	});

	test('should handle various custom unknown parameters', () => {
		const result = queryToParams({
			customString: 'value',
			customNumber: 42,
			customBool: true,
			customObject: { key: 'value' },
		});

		expect(result).toEqual({
			customString: 'value',
			customNumber: '42',
			customBool: 'true',
			customObject: '{"key":"value"}',
Repository: directus/directus
File: app/src/components/v-form/types.ts (TypeScript)

Code before:
import { DeepPartial, Field } from '@directus/types';

export type FormField = DeepPartial<Field> & {
	field: string;
	name: string;
	hideLabel?: boolean;
	hideLoader?: boolean;
};

export interface ComparisonContext {
	side: 'base' | 'incoming';
	fields: Set<string>;
	selectedFields: string[];
	onToggleField: (field: string) => void;
	comparisonType?: 'version' | 'revision';
}


Review comment (suggestion):
Let’s just call it type and remove the optional modifier.

```suggestion
	type: 'version' | 'revision';
```

Code after:
import { DeepPartial, Field } from '@directus/types';

export type FormField = DeepPartial<Field> & {
	field: string;
	name: string;
	hideLabel?: boolean;
	hideLoader?: boolean;
};

export interface ComparisonContext {
	side: 'base' | 'incoming';
	fields: Set<string>;
	selectedFields: string[];
	onToggleField: (field: string) => void;
}
Repository: directus/directus
File: app/src/components/v-form/v-form.vue (Vue)

Code before:
		if (props.fields) {
			return props.fields;
		}

		throw new Error('[v-form]: You need to pass either the collection or fields prop.');
	}

	function setPrimaryKeyReadonly(field: Field) {
		if (
			field.schema?.is_primary_key === true &&
			props.primaryKey !== '+' &&
			(field.schema?.has_auto_increment === true || field.meta?.special?.includes('uuid'))
		) {
			const fieldClone = cloneDeep(field) as any;
			if (!fieldClone.meta) fieldClone.meta = {};
			fieldClone.meta.readonly = true;
			return fieldClone;
		}

		return field;
	}

	function isFieldVisible(field: Field | TFormField) {
		// In comparison mode, show hidden fields but exclude read-only fields
		if (props.comparison?.fields) {
			// revision comparisons do not support related item fields, so we need to hide them
			if (props.comparison.comparisonType === 'revision') {
				const localType = field.meta?.special?.[0];

				if (isRelationalField(localType)) {
					return false;
				}
			}

			return field.meta?.readonly !== true;
		}

		return field.meta?.hidden !== true;
	}
}

function setValue(fieldKey: string, value: any, opts?: { force?: boolean }) {
	const field = fieldsMap.value[fieldKey];

	if (opts?.force !== true && (!field || isDisabled(field))) return;

	const edits = props.modelValue ? cloneDeep(props.modelValue) : {};
	edits[fieldKey] = value;
	emit('update:modelValue', edits);
}


Review comment (suggestion):
Now, all hidden fields appear, even if they haven’t changed. Please also see the other [comment](https://github.com/directus/directus/pull/25954#discussion_r2416136652).

Code after:
			return props.fields;
		}

		throw new Error('[v-form]: You need to pass either the collection or fields prop.');
	}

	function setPrimaryKeyReadonly(field: Field) {
		if (
			field.schema?.is_primary_key === true &&
			props.primaryKey !== '+' &&
			(field.schema?.has_auto_increment === true || field.meta?.special?.includes('uuid'))
		) {
			const fieldClone = cloneDeep(field) as any;
			if (!fieldClone.meta) fieldClone.meta = {};
			fieldClone.meta.readonly = true;
			return fieldClone;
		}

		return field;
	}

	function isFieldVisible(field: Field | TFormField) {
		// In comparison mode, show hidden fields but exclude read-only fields
		if (props.comparison?.fields?.has(field.field)) {
			return field.meta?.readonly !== true;
		}

		return field.meta?.hidden !== true;
	}
}

function setValue(fieldKey: string, value: any, opts?: { force?: boolean }) {
	const field = fieldsMap.value[fieldKey];

	if (opts?.force !== true && (!field || isDisabled(field))) return;

	const edits = props.modelValue ? cloneDeep(props.modelValue) : {};
	edits[fieldKey] = value;
	emit('update:modelValue', edits);
}

function apply(updates: { [field: string]: any }) {
	const updatableKeys = props.batchMode
		? Object.keys(updates)
		: Object.keys(updates).filter((key) => {
				const field = fieldsMap.value[key];
				if (!field) return false;
				return (
					(updates.$type === 'created' && field.meta?.readonly) || field.schema?.is_primary_key || !isDisabled(field)
				);
			});
Repository: directus/directus
File: app/src/utils/merge-item-data.test.ts (TypeScript)

Code before:
import { describe, it, expect } from 'vitest';
import { mergeItemData } from './merge-item-data';

describe('mergeItemData', () => {
	it('should use existing if edits is undefined', () => {
		const defaults = { x: 'default' };
		const existing = { x: 'existing' };
		const edits = {};
		const result = mergeItemData(defaults, existing, edits);
		expect(result.x).toMatchInlineSnapshot(`"existing"`);
	});

	it('should use default if both existing and edits are undefined', () => {
		const defaults = { x: 'default' };
		const existing = {};
		const edits = {};
		const result = mergeItemData(defaults, existing, edits);
		expect(result.x).toMatchInlineSnapshot(`"default"`);
	});

	it('should use edits over existing and default', () => {
		const defaults = { x: 'default' };
		const existing = { x: 'existing' };
		const edits = { x: 'edited' };
		const result = mergeItemData(defaults, existing, edits);
		expect(result.x).toMatchInlineSnapshot(`"edited"`);
	});

	it('should merge default, existing, and edits with correct precedence', () => {
		const defaults = { b: 'default b', c: 'default c' };

Review comment (suggestion):
The test states edits is undefined, but is using an empty object instead.

Code after:
import { describe, it, expect } from 'vitest';
import { mergeItemData } from './merge-item-data';

describe('mergeItemData', () => {
	it('should use existing if field is undefined in edits', () => {
		const defaults = { x: 'default' };
		const existing = { x: 'existing' };
		const edits = {};
		const result = mergeItemData(defaults, existing, edits);
		expect(result.x).toMatchInlineSnapshot(`"existing"`);
	});

	it('should use default if field is undefined in both existing and edits', () => {
		const defaults = { x: 'default' };
		const existing = {};
		const edits = {};
		const result = mergeItemData(defaults, existing, edits);
		expect(result.x).toMatchInlineSnapshot(`"default"`);
	});

	it('should use edits over existing and default', () => {
		const defaults = { x: 'default' };
		const existing = { x: 'existing' };
		const edits = { x: 'edited' };
		const result = mergeItemData(defaults, existing, edits);
		expect(result.x).toMatchInlineSnapshot(`"edited"`);
	});

	it('should merge default, existing, and edits with correct precedence', () => {
		const defaults = { b: 'default b', c: 'default c' };
Repository: directus/directus
File: packages/env/src/constants/defaults.ts (TypeScript)

Code before:
	QUERY_LIMIT_DEFAULT: 100,
	MAX_BATCH_MUTATION: Infinity,
	ROBOTS_TXT: 'User-agent: *\nDisallow: /',

	TEMP_PATH: './node_modules/.directus',

	DB_EXCLUDE_TABLES: 'spatial_ref_sys,sysdiagrams',

	STORAGE_LOCATIONS: 'local',
	STORAGE_LOCAL_DRIVER: 'local',
	STORAGE_LOCAL_ROOT: './uploads',

	RATE_LIMITER_ENABLED: false,
	RATE_LIMITER_POINTS: 50,
	RATE_LIMITER_DURATION: 1,
	RATE_LIMITER_STORE: 'memory',

	RATE_LIMITER_GLOBAL_ENABLED: false,
	RATE_LIMITER_GLOBAL_POINTS: 1000,
	RATE_LIMITER_GLOBAL_DURATION: 1,

	RATE_LIMITER_REGISTRATION_ENABLED: true,
	RATE_LIMITER_REGISTRATION_POINTS: 5,
	RATE_LIMITER_REGISTRATION_DURATION: 60,

	EMAIL_LIMITERENABLED: false,
	EMAIL_LIMITER_POINTS: 60,
	EMAIL_LIMITER_DURATION: 60,
	EMAIL_LIMITER_QUEUE_SIZE: 1_000_000,

	EMAIL_FLOWS_LIMITER_ENABLED: false,
	EMAIL_FLOWS_LIMITER_POINTS: 1,
	EMAIL_FLOWS_LIMITER_DURATION: 60,

	ACCESS_TOKEN_TTL: '15m',
	EMAIL_VERIFICATION_TOKEN_TTL: '7d',

	REFRESH_TOKEN_TTL: '7d',
	REFRESH_TOKEN_COOKIE_NAME: 'directus_refresh_token',
	REFRESH_TOKEN_COOKIE_SECURE: false,
	REFRESH_TOKEN_COOKIE_SAME_SITE: 'lax',

	SESSION_COOKIE_TTL: '1d',
	SESSION_COOKIE_NAME: 'directus_session_token',
	SESSION_COOKIE_SECURE: false,
	SESSION_COOKIE_SAME_SITE: 'lax',
	SESSION_REFRESH_GRACE_PERIOD: '10s',

	USER_INVITE_TOKEN_TTL: '7d',

	LOGIN_STALL_TIME: 500,

Review comment (suggestion):
```suggestion
	EMAIL_LIMITER_ENABLED: false,
```

Code after:
	QUERY_LIMIT_DEFAULT: 100,
	MAX_BATCH_MUTATION: Infinity,
	ROBOTS_TXT: 'User-agent: *\nDisallow: /',

	TEMP_PATH: './node_modules/.directus',

	DB_EXCLUDE_TABLES: 'spatial_ref_sys,sysdiagrams',

	STORAGE_LOCATIONS: 'local',
	STORAGE_LOCAL_DRIVER: 'local',
	STORAGE_LOCAL_ROOT: './uploads',

	RATE_LIMITER_ENABLED: false,
	RATE_LIMITER_POINTS: 50,
	RATE_LIMITER_DURATION: 1,
	RATE_LIMITER_STORE: 'memory',

	RATE_LIMITER_GLOBAL_ENABLED: false,
	RATE_LIMITER_GLOBAL_POINTS: 1000,
	RATE_LIMITER_GLOBAL_DURATION: 1,

	RATE_LIMITER_REGISTRATION_ENABLED: true,
	RATE_LIMITER_REGISTRATION_POINTS: 5,
	RATE_LIMITER_REGISTRATION_DURATION: 60,

	RATE_LIMITER_EMAIL_ENABLED: false,
	RATE_LIMITER_EMAIL_POINTS: 60,
	RATE_LIMITER_EMAIL_DURATION: 60,
	RATE_LIMITER_EMAIL_QUEUE_SIZE: 1_000_000,

	RATE_LIMITER_EMAIL_FLOWS_ENABLED: false,
	RATE_LIMITER_EMAIL_FLOWS_POINTS: 1,
	RATE_LIMITER_EMAIL_FLOWS_DURATION: 60,

	ACCESS_TOKEN_TTL: '15m',
	EMAIL_VERIFICATION_TOKEN_TTL: '7d',

	REFRESH_TOKEN_TTL: '7d',
	REFRESH_TOKEN_COOKIE_NAME: 'directus_refresh_token',
	REFRESH_TOKEN_COOKIE_SECURE: false,
	REFRESH_TOKEN_COOKIE_SAME_SITE: 'lax',

	SESSION_COOKIE_TTL: '1d',
	SESSION_COOKIE_NAME: 'directus_session_token',
	SESSION_COOKIE_SECURE: false,
	SESSION_COOKIE_SAME_SITE: 'lax',
	SESSION_REFRESH_GRACE_PERIOD: '10s',

	USER_INVITE_TOKEN_TTL: '7d',

	LOGIN_STALL_TIME: 500,