From dc15f8d8b01f8af258c366750671e0c68dca9d70 Mon Sep 17 00:00:00 2001 From: Zeyu Zhang <39144422+zeyu2001@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:04:47 +0800 Subject: [PATCH] refactor: provide zod schema --- ...rter-kitty-validators.createemailschema.md | 59 ++++++ ...tors.emailvalidationerror._constructor_.md | 47 ----- ...r-kitty-validators.emailvalidationerror.md | 48 ----- ...validators.emailvalidator._constructor_.md | 49 ----- ...starter-kitty-validators.emailvalidator.md | 81 ------- ...r-kitty-validators.emailvalidator.parse.md | 58 ----- ...s.emailvalidatoroptions.allowsubdomains.md | 18 -- ...alidators.emailvalidatoroptions.domains.md | 13 +- ...-kitty-validators.emailvalidatoroptions.md | 23 +- apps/docs/api/starter-kitty-validators.md | 32 +-- etc/starter-kitty-validators.api.md | 17 +- packages/validators/README.md | 5 +- .../validators/src/__tests__/email.test.ts | 58 ++--- packages/validators/src/email/index.ts | 50 ++--- packages/validators/src/email/options.ts | 27 ++- packages/validators/src/email/schema.ts | 10 +- packages/validators/src/email/utils.ts | 10 +- packages/validators/src/index.ts | 1 - .../temp/starter-kitty-validators.api.json | 198 +++--------------- .../temp/starter-kitty-validators.api.md | 17 +- 20 files changed, 190 insertions(+), 631 deletions(-) create mode 100644 apps/docs/api/starter-kitty-validators.createemailschema.md delete mode 100644 apps/docs/api/starter-kitty-validators.emailvalidationerror._constructor_.md delete mode 100644 apps/docs/api/starter-kitty-validators.emailvalidationerror.md delete mode 100644 apps/docs/api/starter-kitty-validators.emailvalidator._constructor_.md delete mode 100644 apps/docs/api/starter-kitty-validators.emailvalidator.md delete mode 100644 apps/docs/api/starter-kitty-validators.emailvalidator.parse.md delete mode 100644 apps/docs/api/starter-kitty-validators.emailvalidatoroptions.allowsubdomains.md diff --git a/apps/docs/api/starter-kitty-validators.createemailschema.md b/apps/docs/api/starter-kitty-validators.createemailschema.md new file mode 100644 index 0000000..1fa822a --- /dev/null +++ b/apps/docs/api/starter-kitty-validators.createemailschema.md @@ -0,0 +1,59 @@ + + +[Home](./index.md) > [@opengovsg/starter-kitty-validators](./starter-kitty-validators.md) > [createEmailSchema](./starter-kitty-validators.createemailschema.md) + +## createEmailSchema() function + +Create a schema that validates emails against RFC 5322 and a whitelist of domains. + +**Signature:** + +```typescript +createEmailSchema: (options?: EmailValidatorOptions) => ZodSchema +``` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +options + + + + +[EmailValidatorOptions](./starter-kitty-validators.emailvalidatoroptions.md) + + + + +_(Optional)_ The options to use for validation + + +
+**Returns:** + +ZodSchema<string> + +A Zod schema that validates emails according to the provided options + +## Exceptions + +[OptionsError](./starter-kitty-validators.optionserror.md) If the options are invalid + diff --git a/apps/docs/api/starter-kitty-validators.emailvalidationerror._constructor_.md b/apps/docs/api/starter-kitty-validators.emailvalidationerror._constructor_.md deleted file mode 100644 index 2de82d9..0000000 --- a/apps/docs/api/starter-kitty-validators.emailvalidationerror._constructor_.md +++ /dev/null @@ -1,47 +0,0 @@ - - -[Home](./index.md) > [@opengovsg/starter-kitty-validators](./starter-kitty-validators.md) > [EmailValidationError](./starter-kitty-validators.emailvalidationerror.md) > [(constructor)](./starter-kitty-validators.emailvalidationerror._constructor_.md) - -## EmailValidationError.(constructor) - -Constructs a new instance of the `EmailValidationError` class - -**Signature:** - -```typescript -constructor(message: string); -``` - -## Parameters - - - -
- -Parameter - - - - -Type - - - - -Description - - -
- -message - - - - -string - - - - - -
diff --git a/apps/docs/api/starter-kitty-validators.emailvalidationerror.md b/apps/docs/api/starter-kitty-validators.emailvalidationerror.md deleted file mode 100644 index 3f5a96b..0000000 --- a/apps/docs/api/starter-kitty-validators.emailvalidationerror.md +++ /dev/null @@ -1,48 +0,0 @@ - - -[Home](./index.md) > [@opengovsg/starter-kitty-validators](./starter-kitty-validators.md) > [EmailValidationError](./starter-kitty-validators.emailvalidationerror.md) - -## EmailValidationError class - -Invalid email error. - -**Signature:** - -```typescript -export declare class EmailValidationError extends Error -``` -**Extends:** Error - -## Constructors - - - -
- -Constructor - - - - -Modifiers - - - - -Description - - -
- -[(constructor)(message)](./starter-kitty-validators.emailvalidationerror._constructor_.md) - - - - - - - -Constructs a new instance of the `EmailValidationError` class - - -
diff --git a/apps/docs/api/starter-kitty-validators.emailvalidator._constructor_.md b/apps/docs/api/starter-kitty-validators.emailvalidator._constructor_.md deleted file mode 100644 index b851393..0000000 --- a/apps/docs/api/starter-kitty-validators.emailvalidator._constructor_.md +++ /dev/null @@ -1,49 +0,0 @@ - - -[Home](./index.md) > [@opengovsg/starter-kitty-validators](./starter-kitty-validators.md) > [EmailValidator](./starter-kitty-validators.emailvalidator.md) > [(constructor)](./starter-kitty-validators.emailvalidator._constructor_.md) - -## EmailValidator.(constructor) - -Constructs a new instance of the `EmailValidator` class - -**Signature:** - -```typescript -constructor(options?: EmailValidatorOptions); -``` - -## Parameters - - - -
- -Parameter - - - - -Type - - - - -Description - - -
- -options - - - - -[EmailValidatorOptions](./starter-kitty-validators.emailvalidatoroptions.md) - - - - -_(Optional)_ - - -
diff --git a/apps/docs/api/starter-kitty-validators.emailvalidator.md b/apps/docs/api/starter-kitty-validators.emailvalidator.md deleted file mode 100644 index aae977f..0000000 --- a/apps/docs/api/starter-kitty-validators.emailvalidator.md +++ /dev/null @@ -1,81 +0,0 @@ - - -[Home](./index.md) > [@opengovsg/starter-kitty-validators](./starter-kitty-validators.md) > [EmailValidator](./starter-kitty-validators.emailvalidator.md) - -## EmailValidator class - -Validates emails against RFC 5322 and a whitelist of domains. - -**Signature:** - -```typescript -export declare class EmailValidator -``` - -## Constructors - - - -
- -Constructor - - - - -Modifiers - - - - -Description - - -
- -[(constructor)(options)](./starter-kitty-validators.emailvalidator._constructor_.md) - - - - - - - -Constructs a new instance of the `EmailValidator` class - - -
- -## Methods - - - -
- -Method - - - - -Modifiers - - - - -Description - - -
- -[parse(email)](./starter-kitty-validators.emailvalidator.parse.md) - - - - - - - -Parses an email address string. - - -
diff --git a/apps/docs/api/starter-kitty-validators.emailvalidator.parse.md b/apps/docs/api/starter-kitty-validators.emailvalidator.parse.md deleted file mode 100644 index fa8efb0..0000000 --- a/apps/docs/api/starter-kitty-validators.emailvalidator.parse.md +++ /dev/null @@ -1,58 +0,0 @@ - - -[Home](./index.md) > [@opengovsg/starter-kitty-validators](./starter-kitty-validators.md) > [EmailValidator](./starter-kitty-validators.emailvalidator.md) > [parse](./starter-kitty-validators.emailvalidator.parse.md) - -## EmailValidator.parse() method - -Parses an email address string. - -**Signature:** - -```typescript -parse(email: string): string; -``` - -## Parameters - - - -
- -Parameter - - - - -Type - - - - -Description - - -
- -email - - - - -string - - - - -The email to validate - - -
-**Returns:** - -string - - -## Exceptions - -[EmailValidationError](./starter-kitty-validators.emailvalidationerror.md) If the email is invalid - diff --git a/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.allowsubdomains.md b/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.allowsubdomains.md deleted file mode 100644 index c64c53c..0000000 --- a/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.allowsubdomains.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [@opengovsg/starter-kitty-validators](./starter-kitty-validators.md) > [EmailValidatorOptions](./starter-kitty-validators.emailvalidatoroptions.md) > [allowSubdomains](./starter-kitty-validators.emailvalidatoroptions.allowsubdomains.md) - -## EmailValidatorOptions.allowSubdomains property - -Whether subdomains are allowed. Defaults to `true`. - -**Signature:** - -```typescript -allowSubdomains?: boolean; -``` - -## Example - -If `false`, `open.gov.sg` is not allowed, even if `gov.sg` is in the whitelist. - diff --git a/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.domains.md b/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.domains.md index 1815e99..5d41e1f 100644 --- a/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.domains.md +++ b/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.domains.md @@ -6,13 +6,22 @@ The list of allowed domains for the domain part of the email address. If not provided, all domains are allowed. +Each whitelisted domain must be specified as an object with the `domain` and `includeSubdomains` properties. If `includeSubdomains` is `true`, all subdomains of the domain are also allowed. + **Signature:** ```typescript -domains?: string[]; +domains?: { + domain: string; + includeSubdomains: boolean; + }[]; ``` ## Example -`[ 'gov.sg', 'example.com' ]` + +```javascript +[ { domain: 'gov.sg', includeSubdomains: true } ] +``` +This will allow `gov.sg` and all subdomains of `gov.sg`, such as `open.gov.sg`. diff --git a/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.md b/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.md index 9566430..c9c7cce 100644 --- a/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.md +++ b/apps/docs/api/starter-kitty-validators.emailvalidatoroptions.md @@ -37,25 +37,6 @@ Description -[allowSubdomains?](./starter-kitty-validators.emailvalidatoroptions.allowsubdomains.md) - - - - - - - -boolean - - - - -_(Optional)_ Whether subdomains are allowed. Defaults to `true`. - - - - - [domains?](./starter-kitty-validators.emailvalidatoroptions.domains.md) @@ -64,13 +45,15 @@ _(Optional)_ Whether subdomains are allowed. Defaults to `true`. -string\[\] +{ domain: string; includeSubdomains: boolean; }\[\] _(Optional)_ The list of allowed domains for the domain part of the email address. If not provided, all domains are allowed. +Each whitelisted domain must be specified as an object with the `domain` and `includeSubdomains` properties. If `includeSubdomains` is `true`, all subdomains of the domain are also allowed. + diff --git a/apps/docs/api/starter-kitty-validators.md b/apps/docs/api/starter-kitty-validators.md index 578137f..421bf45 100644 --- a/apps/docs/api/starter-kitty-validators.md +++ b/apps/docs/api/starter-kitty-validators.md @@ -21,56 +21,60 @@ Description -[EmailValidationError](./starter-kitty-validators.emailvalidationerror.md) +[OptionsError](./starter-kitty-validators.optionserror.md) -Invalid email error. +Invalid options error. -[EmailValidator](./starter-kitty-validators.emailvalidator.md) +[UrlValidationError](./starter-kitty-validators.urlvalidationerror.md) -Validates emails against RFC 5322 and a whitelist of domains. +Invalid URL error. -[OptionsError](./starter-kitty-validators.optionserror.md) +[UrlValidator](./starter-kitty-validators.urlvalidator.md) -Invalid options error. +Parses URLs according to WHATWG standards and validates against a whitelist of allowed protocols and hostnames, preventing open redirects, XSS, SSRF, and other security vulnerabilities. - + -[UrlValidationError](./starter-kitty-validators.urlvalidationerror.md) +## Functions + + - + diff --git a/etc/starter-kitty-validators.api.md b/etc/starter-kitty-validators.api.md index 4a3cac6..1bdb399 100644 --- a/etc/starter-kitty-validators.api.md +++ b/etc/starter-kitty-validators.api.md @@ -7,22 +7,17 @@ /// import { z } from 'zod'; +import { ZodSchema } from 'zod'; // @public -export class EmailValidationError extends Error { - constructor(message: string); -} - -// @public -export class EmailValidator { - constructor(options?: EmailValidatorOptions); - parse(email: string): string; -} +export const createEmailSchema: (options?: EmailValidatorOptions) => ZodSchema; // @public export interface EmailValidatorOptions { - allowSubdomains?: boolean; - domains?: string[]; + domains?: { + domain: string; + includeSubdomains: boolean; + }[]; } // @public diff --git a/packages/validators/README.md b/packages/validators/README.md index 38d8214..5498db4 100644 --- a/packages/validators/README.md +++ b/packages/validators/README.md @@ -48,8 +48,9 @@ try { `options?`: `` -- `domains?`: `` - A list of allowed email domains. If no domains are provided, the validator will allow any domain. -- `allowSubdomains?`: `` - Whether to allow subdomains of the allowed domains. If `false`, only an exact match of the allowed domains will be allowed. Defaults to `true`. +- `domains?`: `[]` + - `domain`: `` - The domain to allow. + - `allowSubdomains?`: `` - Whether to allow subdomains of the domain. Defaults to `false`. ### EmailValidator.parse(email) diff --git a/packages/validators/src/__tests__/email.test.ts b/packages/validators/src/__tests__/email.test.ts index 7805f42..aba71c7 100644 --- a/packages/validators/src/__tests__/email.test.ts +++ b/packages/validators/src/__tests__/email.test.ts @@ -1,90 +1,68 @@ import { describe, expect, it } from 'vitest' +import { ZodError } from 'zod' import { OptionsError } from '@/common/errors' -import { EmailValidationError } from '@/email/errors' -import { EmailValidator } from '@/index' +import { createEmailSchema } from '@/index' describe('EmailValidator with default options', () => { - const validator = new EmailValidator() + const schema = createEmailSchema() it('should parse a valid email', () => { - const email = validator.parse('zeyu@open.gov.sg') + const email = schema.parse('zeyu@open.gov.sg') expect(email).toBe('zeyu@open.gov.sg') }) it('should throw an error for an invalid email', () => { - expect(() => validator.parse('zeyu@open')).toThrowError( - EmailValidationError, - ) + expect(() => schema.parse('zeyu@open')).toThrowError(ZodError) }) it('should clean up unnecessary whitespace', () => { - const email = validator.parse(' zeyu@open.gov.sg ') + const email = schema.parse(' zeyu@open.gov.sg ') expect(email).toBe('zeyu@open.gov.sg') }) }) describe('EmailValidator that allows subdomains', () => { - const validator = new EmailValidator({ - domains: ['gov.sg'], - allowSubdomains: true, + const schema = createEmailSchema({ + domains: [{ domain: 'gov.sg', includeSubdomains: true }], }) it('should allow a valid subdomain', () => { - expect(() => validator.parse('zeyu@open.gov.sg')).not.toThrow() + expect(() => schema.parse('zeyu@open.gov.sg')).not.toThrow() }) it('should allow a valid subdomain with multiple levels', () => { - expect(() => validator.parse('zeyu@a.b.c.d.gov.sg')).not.toThrow() + expect(() => schema.parse('zeyu@a.b.c.d.gov.sg')).not.toThrow() }) it('should throw an error for an invalid subdomain', () => { - expect(() => validator.parse('zeyu@edu.sg')).toThrowError( - EmailValidationError, - ) + expect(() => schema.parse('zeyu@edu.sg')).toThrowError(ZodError) }) }) describe('EmailValidator that disallows subdomains', () => { - const validator = new EmailValidator({ - domains: ['gov.sg'], - allowSubdomains: false, + const schema = createEmailSchema({ + domains: [{ domain: 'gov.sg', includeSubdomains: false }], }) it('should allow a valid domain', () => { - expect(() => validator.parse('zeyu@gov.sg')).not.toThrow() + expect(() => schema.parse('zeyu@gov.sg')).not.toThrow() }) it('should throw an error for a subdomain', () => { - expect(() => validator.parse('zeyu@open.gov.sg')).toThrowError( - EmailValidationError, - ) + expect(() => schema.parse('zeyu@open.gov.sg')).toThrowError(ZodError) }) }) describe('EmailValidator with invalid options', () => { it('should throw an error for invalid options', () => { // @ts-expect-error Testing invalid options - expect(() => new EmailValidator({ domains: 'gov.sg' })).toThrowError( + expect(() => createEmailSchema({ domains: ['gov.sg'] })).toThrowError( OptionsError, ) }) - it('should not throw an error when missing domains', () => { - expect( - () => - new EmailValidator({ - allowSubdomains: true, - }), - ).not.toThrow() - }) - - it('should not throw an error when missing allowSubdomains', () => { - expect( - () => - new EmailValidator({ - domains: ['gov.sg'], - }), - ).not.toThrow() + it('should not throw an error when missing options', () => { + expect(() => createEmailSchema()).not.toThrow() }) }) diff --git a/packages/validators/src/email/index.ts b/packages/validators/src/email/index.ts index f7ada35..4c4cbd1 100644 --- a/packages/validators/src/email/index.ts +++ b/packages/validators/src/email/index.ts @@ -1,47 +1,25 @@ -import { ZodError } from 'zod' +import { ZodSchema } from 'zod' import { fromError } from 'zod-validation-error' import { OptionsError } from '@/common/errors' import { EmailValidatorOptions, optionsSchema } from '@/email/options' - -import { EmailValidationError } from './errors' -import { createEmailSchema } from './schema' +import { toSchema } from '@/email/schema' /** - * Validates emails against RFC 5322 and a whitelist of domains. + * Create a schema that validates emails against RFC 5322 and a whitelist of domains. + * + * @param options - The options to use for validation + * @throws {@link OptionsError} If the options are invalid + * @returns A Zod schema that validates emails according to the provided options * * @public */ -export class EmailValidator { - private schema - - constructor(options: EmailValidatorOptions = {}) { - const result = optionsSchema.safeParse(options) - if (result.success) { - this.schema = createEmailSchema(result.data) - return - } - throw new OptionsError(fromError(result.error).toString()) - } - - /** - * Parses an email address string. - * - * @param email - The email to validate - * @throws {@link EmailValidationError} If the email is invalid - * @returns - * - * @public - */ - parse(email: string) { - const result = this.schema.safeParse(email) - if (result.success) { - return result.data - } - if (result.error instanceof ZodError) { - throw new EmailValidationError(fromError(result.error).toString()) - } else { - throw result.error - } +export const createEmailSchema = ( + options: EmailValidatorOptions = {}, +): ZodSchema => { + const result = optionsSchema.safeParse(options) + if (result.success) { + return toSchema(result.data) } + throw new OptionsError(fromError(result.error).toString()) } diff --git a/packages/validators/src/email/options.ts b/packages/validators/src/email/options.ts index 041877a..0b3d7fb 100644 --- a/packages/validators/src/email/options.ts +++ b/packages/validators/src/email/options.ts @@ -10,22 +10,29 @@ export interface EmailValidatorOptions { * The list of allowed domains for the domain part of the email address. * If not provided, all domains are allowed. * - * @example - * `[ 'gov.sg', 'example.com' ]` - */ - domains?: string[] - /** - * Whether subdomains are allowed. Defaults to `true`. + * Each whitelisted domain must be specified as an object with the `domain` + * and `includeSubdomains` properties. If `includeSubdomains` is `true`, all + * subdomains of the domain are also allowed. * * @example - * If `false`, `open.gov.sg` is not allowed, even if `gov.sg` is in the whitelist. + * ```javascript + * [ { domain: 'gov.sg', includeSubdomains: true } ] + * ``` + * + * This will allow `gov.sg` and all subdomains of `gov.sg`, such as `open.gov.sg`. */ - allowSubdomains?: boolean + domains?: { domain: string; includeSubdomains: boolean }[] } export const optionsSchema = z.object({ - domains: z.array(z.string()).optional(), - allowSubdomains: z.boolean().default(true), + domains: z + .array( + z.object({ + domain: z.string(), + includeSubdomains: z.boolean(), + }), + ) + .default([]), }) export type ParsedEmailValidatorOptions = z.infer diff --git a/packages/validators/src/email/schema.ts b/packages/validators/src/email/schema.ts index c282dbd..9054389 100644 --- a/packages/validators/src/email/schema.ts +++ b/packages/validators/src/email/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' import { ParsedEmailValidatorOptions } from '@/email/options' import { isWhitelistedDomain, parseEmail } from '@/email/utils' -export const createEmailSchema = (options: ParsedEmailValidatorOptions) => { +export const toSchema = (options: ParsedEmailValidatorOptions) => { return z .string() .trim() @@ -11,15 +11,11 @@ export const createEmailSchema = (options: ParsedEmailValidatorOptions) => { .transform((email) => parseEmail(email)) .refine( (parsed) => { - if (!options.domains) { + if (options.domains.length === 0) { return true } const domain = parsed.domain - return isWhitelistedDomain( - domain, - options.domains, - options.allowSubdomains, - ) + return isWhitelistedDomain(domain, options.domains) }, { message: 'Domain not allowed', diff --git a/packages/validators/src/email/utils.ts b/packages/validators/src/email/utils.ts index 77ebeea..d0d3809 100644 --- a/packages/validators/src/email/utils.ts +++ b/packages/validators/src/email/utils.ts @@ -12,12 +12,12 @@ export const parseEmail = (email: string) => { export const isWhitelistedDomain = ( domain: string, - whitelistedDomains: string[], - allowSubdomains: boolean, + whitelistedDomains: { domain: string; includeSubdomains: boolean }[], ) => { return whitelistedDomains.some( - (whitelistedDomain) => - domain === whitelistedDomain || - (allowSubdomains && domain.endsWith(`.${whitelistedDomain}`)), + (whitelisted) => + domain === whitelisted.domain || + (whitelisted.includeSubdomains && + domain.endsWith(`.${whitelisted.domain}`)), ) } diff --git a/packages/validators/src/index.ts b/packages/validators/src/index.ts index f90303b..e04e6cb 100644 --- a/packages/validators/src/index.ts +++ b/packages/validators/src/index.ts @@ -6,7 +6,6 @@ export type * from '@/common/errors' export * from '@/email' -export type * from '@/email/errors' export type { EmailValidatorOptions } from '@/email/options' export * from '@/url' export type * from '@/url/errors' diff --git a/packages/validators/temp/starter-kitty-validators.api.json b/packages/validators/temp/starter-kitty-validators.api.json index 47cfcc5..ee83dd6 100644 --- a/packages/validators/temp/starter-kitty-validators.api.json +++ b/packages/validators/temp/starter-kitty-validators.api.json @@ -173,168 +173,51 @@ "preserveMemberOrder": false, "members": [ { - "kind": "Class", - "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidationError:class", - "docComment": "/**\n * Invalid email error.\n *\n * @public\n */\n", + "kind": "Function", + "canonicalReference": "@opengovsg/starter-kitty-validators!createEmailSchema:function(1)", + "docComment": "/**\n * Create a schema that validates emails against RFC 5322 and a whitelist of domains.\n *\n * @param options - The options to use for validation\n *\n * @returns A Zod schema that validates emails according to the provided options\n *\n * @throws\n *\n * {@link OptionsError} If the options are invalid\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export declare class EmailValidationError extends " + "text": "createEmailSchema: (options?: " }, { "kind": "Reference", - "text": "Error", - "canonicalReference": "!Error:interface" + "text": "EmailValidatorOptions", + "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidatorOptions:interface" }, { "kind": "Content", - "text": " " - } - ], - "fileUrlPath": "dist/email/errors.d.ts", - "releaseTag": "Public", - "isAbstract": false, - "name": "EmailValidationError", - "preserveMemberOrder": false, - "members": [ + "text": ") => " + }, { - "kind": "Constructor", - "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidationError:constructor(1)", - "docComment": "/**\n * Constructs a new instance of the `EmailValidationError` class\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "constructor(message: " - }, - { - "kind": "Content", - "text": "string" - }, - { - "kind": "Content", - "text": ");" - } - ], - "releaseTag": "Public", - "isProtected": false, - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "message", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ] - } - ], - "extendsTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "implementsTokenRanges": [] - }, - { - "kind": "Class", - "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidator:class", - "docComment": "/**\n * Validates emails against RFC 5322 and a whitelist of domains.\n *\n * @public\n */\n", - "excerptTokens": [ + "kind": "Reference", + "text": "ZodSchema", + "canonicalReference": "zod!ZodType:class" + }, { "kind": "Content", - "text": "export declare class EmailValidator " + "text": "" } ], "fileUrlPath": "dist/email/index.d.ts", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, "releaseTag": "Public", - "isAbstract": false, - "name": "EmailValidator", - "preserveMemberOrder": false, - "members": [ + "overloadIndex": 1, + "parameters": [ { - "kind": "Constructor", - "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidator:constructor(1)", - "docComment": "/**\n * Constructs a new instance of the `EmailValidator` class\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "constructor(options?: " - }, - { - "kind": "Reference", - "text": "EmailValidatorOptions", - "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidatorOptions:interface" - }, - { - "kind": "Content", - "text": ");" - } - ], - "releaseTag": "Public", - "isProtected": false, - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "options", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": true - } - ] - }, - { - "kind": "Method", - "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidator#parse:member(1)", - "docComment": "/**\n * Parses an email address string.\n *\n * @param email - The email to validate\n *\n * @returns \n *\n * @throws\n *\n * {@link EmailValidationError} If the email is invalid\n *\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "parse(email: " - }, - { - "kind": "Content", - "text": "string" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "string" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isStatic": false, - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 4 + "parameterName": "options", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 }, - "releaseTag": "Public", - "isProtected": false, - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "email", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "isOptional": false, - "isAbstract": false, - "name": "parse" + "isOptional": true } ], - "implementsTokenRanges": [] + "name": "createEmailSchema" }, { "kind": "Interface", @@ -351,37 +234,10 @@ "name": "EmailValidatorOptions", "preserveMemberOrder": false, "members": [ - { - "kind": "PropertySignature", - "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidatorOptions#allowSubdomains:member", - "docComment": "/**\n * Whether subdomains are allowed. Defaults to `true`.\n *\n * @example\n *\n * If `false`, `open.gov.sg` is not allowed, even if `gov.sg` is in the whitelist.\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "allowSubdomains?: " - }, - { - "kind": "Content", - "text": "boolean" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": true, - "releaseTag": "Public", - "name": "allowSubdomains", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, { "kind": "PropertySignature", "canonicalReference": "@opengovsg/starter-kitty-validators!EmailValidatorOptions#domains:member", - "docComment": "/**\n * The list of allowed domains for the domain part of the email address. If not provided, all domains are allowed.\n *\n * @example\n *\n * `[ 'gov.sg', 'example.com' ]`\n */\n", + "docComment": "/**\n * The list of allowed domains for the domain part of the email address. If not provided, all domains are allowed.\n *\n * Each whitelisted domain must be specified as an object with the `domain` and `includeSubdomains` properties. If `includeSubdomains` is `true`, all subdomains of the domain are also allowed.\n *\n * @example\n * ```javascript\n * [ { domain: 'gov.sg', includeSubdomains: true } ]\n * ```\n *\n * This will allow `gov.sg` and all subdomains of `gov.sg`, such as `open.gov.sg`.\n */\n", "excerptTokens": [ { "kind": "Content", @@ -389,7 +245,7 @@ }, { "kind": "Content", - "text": "string[]" + "text": "{\n domain: string;\n includeSubdomains: boolean;\n }[]" }, { "kind": "Content", diff --git a/packages/validators/temp/starter-kitty-validators.api.md b/packages/validators/temp/starter-kitty-validators.api.md index 4a3cac6..1bdb399 100644 --- a/packages/validators/temp/starter-kitty-validators.api.md +++ b/packages/validators/temp/starter-kitty-validators.api.md @@ -7,22 +7,17 @@ /// import { z } from 'zod'; +import { ZodSchema } from 'zod'; // @public -export class EmailValidationError extends Error { - constructor(message: string); -} - -// @public -export class EmailValidator { - constructor(options?: EmailValidatorOptions); - parse(email: string): string; -} +export const createEmailSchema: (options?: EmailValidatorOptions) => ZodSchema; // @public export interface EmailValidatorOptions { - allowSubdomains?: boolean; - domains?: string[]; + domains?: { + domain: string; + includeSubdomains: boolean; + }[]; } // @public
+Function - -Invalid URL error. + +Description -
-[UrlValidator](./starter-kitty-validators.urlvalidator.md) +
+ +[createEmailSchema(options)](./starter-kitty-validators.createemailschema.md) -Parses URLs according to WHATWG standards and validates against a whitelist of allowed protocols and hostnames, preventing open redirects, XSS, SSRF, and other security vulnerabilities. +Create a schema that validates emails against RFC 5322 and a whitelist of domains.