/
opt
/
canhelp
/
node_modules
/
zod
/
src
/
v3
/
tests
/
/opt/canhelp/node_modules/zod/src/v3/tests
mkdir
upload
Name
Size
Mode
Actions
all-errors.test.ts
4753
0644
edit
dl
rm
anyunknown.test.ts
673
0644
edit
dl
rm
array.test.ts
1939
0644
edit
dl
rm
async-parsing.test.ts
12639
0644
edit
dl
rm
async-refinements.test.ts
1603
0644
edit
dl
rm
base.test.ts
782
0644
edit
dl
rm
bigint.test.ts
1977
0644
edit
dl
rm
branded.test.ts
1734
0644
edit
dl
rm
catch.test.ts
6550
0644
edit
dl
rm
coerce.test.ts
6650
0644
edit
dl
rm
complex.test.ts
1914
0644
edit
dl
rm
custom.test.ts
950
0644
edit
dl
rm
date.test.ts
983
0644
edit
dl
rm
deepmasking.test.ts
5215
0644
edit
dl
rm
default.test.ts
3527
0644
edit
dl
rm
description.test.ts
1323
0644
edit
dl
rm
discriminated-unions.test.ts
8537
0644
edit
dl
rm
enum.test.ts
2737
0644
edit
dl
rm
error.test.ts
16812
0644
edit
dl
rm
firstparty.test.ts
2391
0644
edit
dl
rm
firstpartyschematypes.test.ts
788
0644
edit
dl
rm
function.test.ts
6578
0644
edit
dl
rm
generics.test.ts
1523
0644
edit
dl
rm
instanceof.test.ts
1154
0644
edit
dl
rm
intersection.test.ts
3091
0644
edit
dl
rm
language-server.source.ts
1275
0644
edit
dl
rm
language-server.test.ts
8318
0644
edit
dl
rm
literal.test.ts
1063
0644
edit
dl
rm
map.test.ts
3545
0644
edit
dl
rm
masking.test.ts
85
0644
edit
dl
rm
mocker.test.ts
389
0644
edit
dl
rm
Mocker.ts
1341
0644
edit
dl
rm
nan.test.ts
587
0644
edit
dl
rm
nativeEnum.test.ts
2344
0644
edit
dl
rm
nullable.test.ts
1172
0644
edit
dl
rm
number.test.ts
6373
0644
edit
dl
rm
object-augmentation.test.ts
569
0644
edit
dl
rm
object-in-es5-env.test.ts
778
0644
edit
dl
rm
object.test.ts
11772
0644
edit
dl
rm
optional.test.ts
1222
0644
edit
dl
rm
parser.test.ts
1125
0644
edit
dl
rm
parseUtil.test.ts
869
0644
edit
dl
rm
partials.test.ts
6996
0644
edit
dl
rm
pickomit.test.ts
3622
0644
edit
dl
rm
pipeline.test.ts
823
0644
edit
dl
rm
preprocess.test.ts
4530
0644
edit
dl
rm
primitive.test.ts
12215
0644
edit
dl
rm
promise.test.ts
2490
0644
edit
dl
rm
readonly.test.ts
7339
0644
edit
dl
rm
record.test.ts
3735
0644
edit
dl
rm
recursive.test.ts
3946
0644
edit
dl
rm
refine.test.ts
8172
0644
edit
dl
rm
safeparse.test.ts
628
0644
edit
dl
rm
set.test.ts
4788
0644
edit
dl
rm
standard-schema.test.ts
2587
0644
edit
dl
rm
string.test.ts
65623
0644
edit
dl
rm
transformer.test.ts
5687
0644
edit
dl
rm
tuple.test.ts
2691
0644
edit
dl
rm
unions.test.ts
1540
0644
edit
dl
rm
validations.test.ts
3826
0644
edit
dl
rm
void.test.ts
349
0644
edit
dl
rm
Edit:
/opt/canhelp/node_modules/zod/src/v3/tests/string.test.ts
(65623B)
import { Buffer } from "node:buffer"; // @ts-ignore TS6133 import { expect, test } from "vitest"; import * as z from "zod/v3"; const minFive = z.string().min(5, "min5"); const maxFive = z.string().max(5, "max5"); const justFive = z.string().length(5); const nonempty = z.string().nonempty("nonempty"); const includes = z.string().includes("includes"); const includesFromIndex2 = z.string().includes("includes", { position: 2 }); const startsWith = z.string().startsWith("startsWith"); const endsWith = z.string().endsWith("endsWith"); test("passing validations", () => { minFive.parse("12345"); minFive.parse("123456"); maxFive.parse("12345"); maxFive.parse("1234"); nonempty.parse("1"); justFive.parse("12345"); includes.parse("XincludesXX"); includesFromIndex2.parse("XXXincludesXX"); startsWith.parse("startsWithX"); endsWith.parse("XendsWith"); }); test("failing validations", () => { expect(() => minFive.parse("1234")).toThrow(); expect(() => maxFive.parse("123456")).toThrow(); expect(() => nonempty.parse("")).toThrow(); expect(() => justFive.parse("1234")).toThrow(); expect(() => justFive.parse("123456")).toThrow(); expect(() => includes.parse("XincludeXX")).toThrow(); expect(() => includesFromIndex2.parse("XincludesXX")).toThrow(); expect(() => startsWith.parse("x")).toThrow(); expect(() => endsWith.parse("x")).toThrow(); }); test("email validations", () => { const validEmails = [ `email@domain.com`, `firstname.lastname@domain.com`, `email@subdomain.domain.com`, `firstname+lastname@domain.com`, `1234567890@domain.com`, `email@domain-one.com`, `_______@domain.com`, `email@domain.name`, `email@domain.co.jp`, `firstname-lastname@domain.com`, `very.common@example.com`, `disposable.style.email.with+symbol@example.com`, `other.email-with-hyphen@example.com`, `fully-qualified-domain@example.com`, `user.name+tag+sorting@example.com`, `x@example.com`, `mojojojo@asdf.example.com`, `example-indeed@strange-example.com`, `example@s.example`, `user-@example.org`, `user@my-example.com`, `a@b.cd`, `work+user@mail.com`, `tom@test.te-st.com`, `something@subdomain.domain-with-hyphens.tld`, `common'name@domain.com`, `francois@etu.inp-n7.fr`, ]; const invalidEmails = [ // no "printable characters" // `user%example.com@example.org`, // `mailhost!username@example.org`, // `test/test@test.com`, // double @ `francois@@etu.inp-n7.fr`, // do not support quotes `"email"@domain.com`, `"e asdf sadf ?<>ail"@domain.com`, `" "@example.org`, `"john..doe"@example.org`, `"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com`, // do not support comma `a,b@domain.com`, // do not support IPv4 `email@123.123.123.123`, `email@[123.123.123.123]`, `postmaster@123.123.123.123`, `user@[68.185.127.196]`, `ipv4@[85.129.96.247]`, `valid@[79.208.229.53]`, `valid@[255.255.255.255]`, `valid@[255.0.55.2]`, `valid@[255.0.55.2]`, // do not support ipv6 `hgrebert0@[IPv6:4dc8:ac7:ce79:8878:1290:6098:5c50:1f25]`, `bshapiro4@[IPv6:3669:c709:e981:4884:59a3:75d1:166b:9ae]`, `jsmith@[IPv6:2001:db8::1]`, `postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]`, `postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:192.168.1.1]`, // microsoft test cases `plainaddress`, `#@%^%#$@#$@#.com`, `@domain.com`, `Joe Smith <email@domain.com>`, `email.domain.com`, `email@domain@domain.com`, `.email@domain.com`, `email.@domain.com`, `email..email@domain.com`, `あいうえお@domain.com`, `email@domain.com (Joe Smith)`, `email@domain`, `email@-domain.com`, `email@111.222.333.44444`, `email@domain..com`, `Abc.example.com`, `A@b@c@example.com`, `colin..hacks@domain.com`, `a"b(c)d,e:f;g<h>i[j\k]l@example.com`, `just"not"right@example.com`, `this is"not\allowed@example.com`, `this\ still\"not\\allowed@example.com`, // random `i_like_underscore@but_its_not_allowed_in_this_part.example.com`, `QA[icon]CHOCOLATE[icon]@test.com`, `invalid@-start.com`, `invalid@end.com-`, `a.b@c.d`, `invalid@[1.1.1.-1]`, `invalid@[68.185.127.196.55]`, `temp@[192.168.1]`, `temp@[9.18.122.]`, `double..point@test.com`, `asdad@test..com`, `asdad@hghg...sd...au`, `asdad@hghg........au`, `invalid@[256.2.2.48]`, `invalid@[256.2.2.48]`, `invalid@[999.465.265.1]`, `jkibbey4@[IPv6:82c4:19a8::70a9:2aac:557::ea69:d985:28d]`, `mlivesay3@[9952:143f:b4df:2179:49a1:5e82:b92e:6b6]`, `gbacher0@[IPv6:bc37:4d3f:5048:2e26:37cc:248e:df8e:2f7f:af]`, `invalid@[IPv6:5348:4ed3:5d38:67fb:e9b:acd2:c13:192.168.256.1]`, `test@.com`, `aaaaaaaaaaaaaaalongemailthatcausesregexDoSvulnerability@test.c`, ]; const emailSchema = z.string().email(); expect( validEmails.every((email) => { return emailSchema.safeParse(email).success; }) ).toBe(true); expect( invalidEmails.every((email) => { return emailSchema.safeParse(email).success === false; }) ).toBe(true); }); const validBase64Strings = [ "SGVsbG8gV29ybGQ=", // "Hello World" "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==", // "This is an encoded string" "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcms=", // "Many hands make light work" "UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // "Patience is the key to success" "QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // "Base64 encoding is fun" "MTIzNDU2Nzg5MA==", // "1234567890" "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", // "abcdefghijklmnopqrstuvwxyz" "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", // "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ISIkJSMmJyonKCk=", // "!\"#$%&'()*" "", // Empty string is technically valid base64 "w7/Dv8O+w74K", // ÿÿþþ ]; for (const str of validBase64Strings) { test(`base64 should accept ${str}`, () => { expect(z.string().base64().safeParse(str).success).toBe(true); }); } const invalidBase64Strings = [ "12345", // Not padded correctly, not a multiple of 4 characters "12345===", // Not padded correctly "SGVsbG8gV29ybGQ", // Missing padding "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw", // Missing padding "!UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // Invalid character '!' "?QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // Invalid character '?' ".MTIzND2Nzg5MC4=", // Invalid character '.' "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", // Missing padding "w7_Dv8O-w74K", // Has - and _ characters (is base64url) ]; for (const str of invalidBase64Strings) { test(`base64 should reject ${str}`, () => { expect(z.string().base64().safeParse(str).success).toBe(false); }); } const validBase64URLStrings = [ "SGVsbG8gV29ybGQ", // "Hello World" "SGVsbG8gV29ybGQ=", // "Hello World" with padding "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw", // "This is an encoded string" "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==", // "This is an encoded string" with padding "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcms", // "Many hands make light work" "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcms=", // "Many hands make light work" with padding "UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // "Patience is the key to success" "QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg", // "Base64 encoding is fun" "QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // "Base64 encoding is fun" with padding "MTIzNDU2Nzg5MA", // "1234567890" "MTIzNDU2Nzg5MA==", // "1234567890" with padding "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo", // "abcdefghijklmnopqrstuvwxyz" "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", // "abcdefghijklmnopqrstuvwxyz with padding" "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", // "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", // "ABCDEFGHIJKLMNOPQRSTUVWXYZ" with padding "ISIkJSMmJyonKCk", // "!\"#$%&'()*" "ISIkJSMmJyonKCk=", // "!\"#$%&'()*" with padding "", // Empty string is technically valid base64url "w7_Dv8O-w74K", // ÿÿþþ "123456", ]; for (const str of validBase64URLStrings) { test(`base64url should accept ${str}`, () => { expect(z.string().base64url().safeParse(str).success).toBe(true); }); } const invalidBase64URLStrings = [ "w7/Dv8O+w74K", // Has + and / characters (is base64) "12345", // Invalid length (not a multiple of 4 characters when adding allowed number of padding characters) "12345===", // Not padded correctly "!UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // Invalid character '!' "?QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // Invalid character '?' ".MTIzND2Nzg5MC4=", // Invalid character '.' ]; for (const str of invalidBase64URLStrings) { test(`base64url should reject ${str}`, () => { expect(z.string().base64url().safeParse(str).success).toBe(false); }); } function makeJwt(header: object, payload: object) { const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64url"); const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString("base64url"); const signature = "signature"; // Placeholder for the signature return `${headerBase64}.${payloadBase64}.${signature}`; } test("jwt validations", () => { const jwt = z.string().jwt(); const jwtWithAlg = z.string().jwt({ alg: "HS256" }); expect(() => jwt.parse("invalid")).toThrow(); expect(() => jwt.parse("invalid.invalid")).toThrow(); expect(() => jwt.parse("invalid.invalid.invalid")).toThrow(); // Valid JWTs const d1 = makeJwt({ typ: "JWT", alg: "HS256" }, {}); expect(() => jwt.parse(d1)).not.toThrow(); expect(() => jwtWithAlg.parse(d1)).not.toThrow(); // Invalid header const d2 = makeJwt({}, {}); expect(() => jwt.parse(d2)).toThrow(); // Wrong algorithm const d3 = makeJwt({ typ: "JWT", alg: "RS256" }, {}); expect(() => jwtWithAlg.parse(d3)).toThrow(); // missing typ is fine const d4 = makeJwt({ alg: "HS256" }, {}); jwt.parse(d4); // type isn't JWT const d5 = makeJwt({ typ: "SUP", alg: "HS256" }, { foo: "bar" }); expect(() => jwt.parse(d5)).toThrow(); // Custom error message const customMsg = "Invalid JWT token"; const jwtWithMsg = z.string().jwt({ message: customMsg }); try { jwtWithMsg.parse("invalid"); } catch (error) { expect((error as z.ZodError).issues[0].message).toBe(customMsg); } }); test("url validations", () => { const url = z.string().url(); url.parse("http://google.com"); url.parse("https://google.com/asdf?asdf=ljk3lk4&asdf=234#asdf"); expect(() => url.parse("asdf")).toThrow(); expect(() => url.parse("https:/")).toThrow(); expect(() => url.parse("asdfj@lkjsdf.com")).toThrow(); }); test("url error overrides", () => { try { z.string().url().parse("https"); } catch (err) { expect((err as z.ZodError).issues[0].message).toEqual("Invalid url"); } try { z.string().url("badurl").parse("https"); } catch (err) { expect((err as z.ZodError).issues[0].message).toEqual("badurl"); } try { z.string().url({ message: "badurl" }).parse("https"); } catch (err) { expect((err as z.ZodError).issues[0].message).toEqual("badurl"); } }); test("emoji validations", () => { const emoji = z.string().emoji(); emoji.parse("👋👋👋👋"); emoji.parse("🍺👩🚀🫡"); emoji.parse("💚💙💜💛❤️"); emoji.parse("🐛🗝🐏🍡🎦🚢🏨💫🎌☘🗡😹🔒🎬➡️🍹🗂🚨⚜🕑〽️🚦🌊🍴💍🍌💰😳🌺🍃"); emoji.parse("🇹🇷🤽🏿♂️"); emoji.parse( "😀😁😂🤣😃😄😅😆😉😊😋😎😍😘🥰😗😙😚☺️☺🙂🤗🤩🤔🤨😐😑😶🙄😏😣😥😮🤐😯😪😫😴😌😛😜😝🤤😒😓😔😕🙃🤑😲☹️☹🙁😖😞😟😤😢😭😦😧😨😩🤯😬😰😱🥵🥶😳🤪😵😡😠🤬😷🤒🤕🤢🤮🤧😇🤠🥳🥴🥺🤥🤫🤭🧐🤓😈👿🤡👹👺💀☠️☠👻👽👾🤖💩😺😸😹😻😼😽🙀😿😾🙈🙉🙊🏻🏼🏽🏾🏿👶👶🏻👶🏼👶🏽👶🏾👶🏿🧒🧒🏻🧒🏼🧒🏽🧒🏾🧒🏿👦👦🏻👦🏼👦🏽👦🏾👦🏿👧👧🏻👧🏼👧🏽👧🏾👧🏿🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿👨👨🏻👨🏼👨🏽👨🏾👨🏿👩👩🏻👩🏼👩🏽👩🏾👩🏿🧓🧓🏻🧓🏼🧓🏽🧓🏾🧓🏿👴👴🏻👴🏼👴🏽👴🏾👴🏿👵👵🏻👵🏼👵🏽👵🏾👵🏿👨⚕️👨⚕👨🏻⚕️👨🏻⚕👨🏼⚕️👨🏼⚕👨🏽⚕️👨🏽⚕👨🏾⚕️👨🏾⚕👨🏿⚕️👨🏿⚕👩⚕️👩⚕👩🏻⚕️👩🏻⚕👩🏼⚕️👩🏼⚕👩🏽⚕️👩🏽⚕👩🏾⚕️👩🏾⚕👩🏿⚕️👩🏿⚕👨🎓👨🏻🎓👨🏼🎓👨🏽🎓👨🏾🎓👨🏿🎓👩🎓👩🏻🎓👩🏼🎓👩🏽🎓👩🏾🎓👩🏿🎓👨🏫👨🏻🏫👨🏼🏫👨🏽🏫👨🏾🏫👨🏿🏫👩🏫👩🏻🏫👩🏼🏫👩🏽🏫👩🏾🏫👩🏿🏫👨⚖️👨⚖👨🏻⚖️👨🏻⚖👨🏼⚖️👨🏼⚖👨🏽⚖️👨🏽⚖👨🏾⚖️👨🏾⚖👨🏿⚖️👨🏿⚖👩⚖️👩⚖👩🏻⚖️👩🏻⚖👩🏼⚖️👩🏼⚖👩🏽⚖️👩🏽⚖👩🏾⚖️👩🏾⚖👩🏿⚖️👩🏿⚖👨🌾👨🏻🌾👨🏼🌾👨🏽🌾👨🏾🌾👨🏿🌾👩🌾👩🏻🌾👩🏼🌾👩🏽🌾👩🏾🌾👩🏿🌾👨🍳👨🏻🍳👨🏼🍳👨🏽🍳👨🏾🍳👨🏿🍳👩🍳👩🏻🍳👩🏼🍳👩🏽🍳👩🏾🍳👩🏿🍳👨🔧👨🏻🔧👨🏼🔧👨🏽🔧👨🏾🔧👨🏿🔧👩🔧👩🏻🔧👩🏼🔧👩🏽🔧👩🏾🔧👩🏿🔧👨🏭👨🏻🏭👨🏼🏭👨🏽🏭👨🏾🏭👨🏿🏭👩🏭👩🏻🏭👩🏼🏭👩🏽🏭👩🏾🏭👩🏿🏭👨💼👨🏻💼👨🏼💼👨🏽💼👨🏾💼👨🏿💼👩💼👩🏻💼👩🏼💼👩🏽💼👩🏾💼👩🏿💼👨🔬👨🏻🔬👨🏼🔬👨🏽🔬👨🏾🔬👨🏿🔬👩🔬👩🏻🔬👩🏼🔬👩🏽🔬👩🏾🔬👩🏿🔬👨💻👨🏻💻👨🏼💻👨🏽💻👨🏾💻👨🏿💻👩💻👩🏻💻👩🏼💻👩🏽💻👩🏾💻👩🏿💻👨🎤👨🏻🎤👨🏼🎤👨🏽🎤👨🏾🎤👨🏿🎤👩🎤👩🏻🎤👩🏼🎤👩🏽🎤👩🏾🎤👩🏿🎤👨🎨👨🏻🎨👨🏼🎨👨🏽🎨👨🏾🎨👨🏿🎨👩🎨👩🏻🎨👩🏼🎨👩🏽🎨👩🏾🎨👩🏿🎨👨✈️👨✈👨🏻✈️👨🏻✈👨🏼✈️👨🏼✈👨🏽✈️👨🏽✈👨🏾✈️👨🏾✈👨🏿✈️👨🏿✈👩✈️👩✈👩🏻✈️👩🏻✈👩🏼✈️👩🏼✈👩🏽✈️👩🏽✈👩🏾✈️👩🏾✈👩🏿✈️👩🏿✈👨🚀👨🏻🚀👨🏼🚀👨🏽🚀👨🏾🚀👨🏿🚀👩🚀👩🏻🚀👩🏼🚀👩🏽🚀👩🏾🚀👩🏿🚀👨🚒👨🏻🚒👨🏼🚒👨🏽🚒👨🏾🚒👨🏿🚒👩🚒👩🏻🚒👩🏼🚒👩🏽🚒👩🏾🚒👩🏿🚒👮👮🏻👮🏼👮🏽👮🏾👮🏿👮♂️👮♂👮🏻♂️👮🏻♂👮🏼♂️👮🏼♂👮🏽♂️👮🏽♂👮🏾♂️👮🏾♂👮🏿♂️👮🏿♂👮♀️👮♀👮🏻♀️👮🏻♀👮🏼♀️👮🏼♀👮🏽♀️👮🏽♀👮🏾♀️👮🏾♀👮🏿♀️👮🏿♀🕵️🕵🕵🏻🕵🏼🕵🏽🕵🏾🕵🏿🕵️♂️🕵♂️🕵️♂🕵♂🕵🏻♂️🕵🏻♂🕵🏼♂️🕵🏼♂🕵🏽♂️🕵🏽♂🕵🏾♂️🕵🏾♂🕵🏿♂️🕵🏿♂🕵️♀️🕵♀️🕵️♀🕵♀🕵🏻♀️🕵🏻♀🕵🏼♀️🕵🏼♀🕵🏽♀️🕵🏽♀🕵🏾♀️🕵🏾♀🕵🏿♀️🕵🏿♀💂💂🏻💂🏼💂🏽💂🏾💂🏿💂♂️💂♂💂🏻♂️💂🏻♂💂🏼♂️💂🏼♂💂🏽♂️💂🏽♂💂🏾♂️💂🏾♂💂🏿♂️💂🏿♂💂♀️💂♀💂🏻♀️💂🏻♀💂🏼♀️💂🏼♀💂🏽♀️💂🏽♀💂🏾♀️💂🏾♀💂🏿♀️💂🏿♀👷👷🏻👷🏼👷🏽👷🏾👷🏿👷♂️👷♂👷🏻♂️👷🏻♂👷🏼♂️👷🏼♂👷🏽♂️👷🏽♂👷🏾♂️👷🏾♂👷🏿♂️👷🏿♂👷♀️👷♀👷🏻♀️👷🏻♀👷🏼♀️👷🏼♀👷🏽♀️👷🏽♀👷🏾♀️👷🏾♀👷🏿♀️👷🏿♀🤴🤴🏻🤴🏼🤴🏽🤴🏾🤴🏿👸👸🏻👸🏼👸🏽👸🏾👸🏿👳👳🏻👳🏼👳🏽👳🏾👳🏿👳♂️👳♂👳🏻♂️👳🏻♂👳🏼♂️👳🏼♂👳🏽♂️👳🏽♂👳🏾♂️👳🏾♂👳🏿♂️👳🏿♂👳♀️👳♀👳🏻♀️👳🏻♀👳🏼♀️👳🏼♀👳🏽♀️👳🏽♀👳🏾♀️👳🏾♀👳🏿♀️👳🏿♀👲👲🏻👲🏼👲🏽👲🏾👲🏿🧕🧕🏻🧕🏼🧕🏽🧕🏾🧕🏿🧔🧔🏻🧔🏼🧔🏽🧔🏾🧔🏿👱👱🏻👱🏼👱🏽👱🏾👱🏿👱♂️👱♂👱🏻♂️👱🏻♂👱🏼♂️👱🏼♂👱🏽♂️👱🏽♂👱🏾♂️👱🏾♂👱🏿♂️👱🏿♂👱♀️👱♀👱🏻♀️👱🏻♀👱🏼♀️👱🏼♀👱🏽♀️👱🏽♀👱🏾♀️👱🏾♀👱🏿♀️👱🏿♀👨🦰👨🏻🦰👨🏼🦰👨🏽🦰👨🏾🦰👨🏿🦰👩🦰👩🏻🦰👩🏼🦰👩🏽🦰👩🏾🦰👩🏿🦰👨🦱👨🏻🦱👨🏼🦱👨🏽🦱👨🏾🦱👨🏿🦱👩🦱👩🏻🦱👩🏼🦱👩🏽🦱👩🏾🦱👩🏿🦱👨🦲👨🏻🦲👨🏼🦲👨🏽🦲👨🏾🦲👨🏿🦲👩🦲👩🏻🦲👩🏼🦲👩🏽🦲👩🏾🦲👩🏿🦲👨🦳👨🏻🦳👨🏼🦳👨🏽🦳👨🏾🦳👨🏿🦳👩🦳👩🏻🦳👩🏼🦳👩🏽🦳👩🏾🦳👩🏿🦳🤵🤵🏻🤵🏼🤵🏽🤵🏾🤵🏿👰👰🏻👰🏼👰🏽👰🏾👰🏿🤰🤰🏻🤰🏼🤰🏽🤰🏾🤰🏿🤱🤱🏻🤱🏼🤱🏽🤱🏾🤱🏿👼👼🏻👼🏼👼🏽👼🏾👼🏿🎅🎅🏻🎅🏼🎅🏽🎅🏾🎅🏿🤶🤶🏻🤶🏼🤶🏽🤶🏾🤶🏿🦸🦸🏻🦸🏼🦸🏽🦸🏾🦸🏿🦸♀️🦸♀🦸🏻♀️🦸🏻♀🦸🏼♀️🦸🏼♀🦸🏽♀️🦸🏽♀🦸🏾♀️🦸🏾♀🦸🏿♀️🦸🏿♀🦸♂️🦸♂🦸🏻♂️🦸🏻♂🦸🏼♂️🦸🏼♂🦸🏽♂️🦸🏽♂🦸🏾♂️🦸🏾♂🦸🏿♂️🦸🏿♂🦹🦹🏻🦹🏼🦹🏽🦹🏾🦹🏿🦹♀️🦹♀🦹🏻♀️🦹🏻♀🦹🏼♀️🦹🏼♀🦹🏽♀️🦹🏽♀🦹🏾♀️🦹🏾♀🦹🏿♀️🦹🏿♀🦹♂️🦹♂🦹🏻♂️🦹🏻♂🦹🏼♂️🦹🏼♂🦹🏽♂️🦹🏽♂🦹🏾♂️🦹🏾♂🦹🏿♂️🦹🏿♂🧙🧙🏻🧙🏼🧙🏽🧙🏾🧙🏿🧙♀️🧙♀🧙🏻♀️🧙🏻♀🧙🏼♀️🧙🏼♀🧙🏽♀️🧙🏽♀🧙🏾♀️🧙🏾♀🧙🏿♀️🧙🏿♀🧙♂️🧙♂🧙🏻♂️🧙🏻♂🧙🏼♂️🧙🏼♂🧙🏽♂️🧙🏽♂🧙🏾♂️🧙🏾♂🧙🏿♂️🧙🏿♂🧚🧚🏻🧚🏼🧚🏽🧚🏾🧚🏿🧚♀️🧚♀🧚🏻♀️🧚🏻♀🧚🏼♀️🧚🏼♀🧚🏽♀️🧚🏽♀🧚🏾♀️🧚🏾♀🧚🏿♀️🧚🏿♀🧚♂️🧚♂🧚🏻♂️🧚🏻♂🧚🏼♂️🧚🏼♂🧚🏽♂️🧚🏽♂🧚🏾♂️🧚🏾♂🧚🏿♂️🧚🏿♂🧛🧛🏻🧛🏼🧛🏽🧛🏾🧛🏿🧛♀️🧛♀🧛🏻♀️🧛🏻♀🧛🏼♀️🧛🏼♀🧛🏽♀️🧛🏽♀🧛🏾♀️🧛🏾♀🧛🏿♀️🧛🏿♀🧛♂️🧛♂🧛🏻♂️🧛🏻♂🧛🏼♂️🧛🏼♂🧛🏽♂️🧛🏽♂🧛🏾♂️🧛🏾♂🧛🏿♂️🧛🏿♂🧜🧜🏻🧜🏼🧜🏽🧜🏾🧜🏿🧜♀️🧜♀🧜🏻♀️🧜🏻♀🧜🏼♀️🧜🏼♀🧜🏽♀️🧜🏽♀🧜🏾♀️🧜🏾♀🧜🏿♀️🧜🏿♀🧜♂️🧜♂🧜🏻♂️🧜🏻♂🧜🏼♂️🧜🏼♂🧜🏽♂️🧜🏽♂🧜🏾♂️🧜🏾♂🧜🏿♂️🧜🏿♂🧝🧝🏻🧝🏼🧝🏽🧝🏾🧝🏿🧝♀️🧝♀🧝🏻♀️🧝🏻♀🧝🏼♀️🧝🏼♀🧝🏽♀️🧝🏽♀🧝🏾♀️🧝🏾♀🧝🏿♀️🧝🏿♀🧝♂️🧝♂🧝🏻♂️🧝🏻♂🧝🏼♂️🧝🏼♂🧝🏽♂️🧝🏽♂🧝🏾♂️🧝🏾♂🧝🏿♂️🧝🏿♂🧞🧞♀️🧞♀🧞♂️🧞♂🧟🧟♀️🧟♀🧟♂️🧟♂🙍🙍🏻🙍🏼🙍🏽🙍🏾🙍🏿🙍♂️🙍♂🙍🏻♂️🙍🏻♂🙍🏼♂️🙍🏼♂🙍🏽♂️🙍🏽♂🙍🏾♂️🙍🏾♂🙍🏿♂️🙍🏿♂🙍♀️🙍♀🙍🏻♀️🙍🏻♀🙍🏼♀️🙍🏼♀🙍🏽♀️🙍🏽♀🙍🏾♀️🙍🏾♀🙍🏿♀️🙍🏿♀🙎🙎🏻🙎🏼🙎🏽🙎🏾🙎🏿🙎♂️🙎♂🙎🏻♂️🙎🏻♂🙎🏼♂️🙎🏼♂🙎🏽♂️🙎🏽♂🙎🏾♂️🙎🏾♂🙎🏿♂️🙎🏿♂🙎♀️🙎♀🙎🏻♀️🙎🏻♀🙎🏼♀️🙎🏼♀🙎🏽♀️🙎🏽♀🙎🏾♀️🙎🏾♀🙎🏿♀️🙎🏿♀🙅🙅🏻🙅🏼🙅🏽🙅🏾🙅🏿🙅♂️🙅♂🙅🏻♂️🙅🏻♂🙅🏼♂️🙅🏼♂🙅🏽♂️🙅🏽♂🙅🏾♂️🙅🏾♂🙅🏿♂️🙅🏿♂🙅♀️🙅♀🙅🏻♀️🙅🏻♀🙅🏼♀️🙅🏼♀🙅🏽♀️🙅🏽♀🙅🏾♀️🙅🏾♀🙅🏿♀️🙅🏿♀🙆🙆🏻🙆🏼🙆🏽🙆🏾🙆🏿🙆♂️🙆♂🙆🏻♂️🙆🏻♂🙆🏼♂️🙆🏼♂🙆🏽♂️🙆🏽♂🙆🏾♂️🙆🏾♂🙆🏿♂️🙆🏿♂🙆♀️🙆♀🙆🏻♀️🙆🏻♀🙆🏼♀️🙆🏼♀🙆🏽♀️🙆🏽♀🙆🏾♀️🙆🏾♀🙆🏿♀️🙆🏿♀💁💁🏻💁🏼💁🏽💁🏾💁🏿💁♂️💁♂💁🏻♂️💁🏻♂💁🏼♂️💁🏼♂💁🏽♂️💁🏽♂💁🏾♂️💁🏾♂💁🏿♂️💁🏿♂💁♀️💁♀💁🏻♀️💁🏻♀💁🏼♀️💁🏼♀💁🏽♀️💁🏽♀💁🏾♀️💁🏾♀💁🏿♀️💁🏿♀🙋🙋🏻🙋🏼🙋🏽🙋🏾🙋🏿🙋♂️🙋♂🙋🏻♂️🙋🏻♂🙋🏼♂️🙋🏼♂🙋🏽♂️🙋🏽♂🙋🏾♂️🙋🏾♂🙋🏿♂️🙋🏿♂🙋♀️🙋♀🙋🏻♀️🙋🏻♀🙋🏼♀️🙋🏼♀🙋🏽♀️🙋🏽♀🙋🏾♀️🙋🏾♀🙋🏿♀️🙋🏿♀🙇🙇🏻🙇🏼🙇🏽🙇🏾🙇🏿🙇♂️🙇♂🙇🏻♂️🙇🏻♂🙇🏼♂️🙇🏼♂🙇🏽♂️🙇🏽♂🙇🏾♂️🙇🏾♂🙇🏿♂️🙇🏿♂🙇♀️🙇♀🙇🏻♀️🙇🏻♀🙇🏼♀️🙇🏼♀🙇🏽♀️🙇🏽♀🙇🏾♀️🙇🏾♀🙇🏿♀️🙇🏿♀🤦🤦🏻🤦🏼🤦🏽🤦🏾🤦🏿🤦♂️🤦♂🤦🏻♂️🤦🏻♂🤦🏼♂️🤦🏼♂🤦🏽♂️🤦🏽♂🤦🏾♂️🤦🏾♂🤦🏿♂️🤦🏿♂🤦♀️🤦♀🤦🏻♀️🤦🏻♀🤦🏼♀️🤦🏼♀🤦🏽♀️🤦🏽♀🤦🏾♀️🤦🏾♀🤦🏿♀️🤦🏿♀🤷🤷🏻🤷🏼🤷🏽🤷🏾🤷🏿🤷♂️🤷♂🤷🏻♂️🤷🏻♂🤷🏼♂️🤷🏼♂🤷🏽♂️🤷🏽♂🤷🏾♂️🤷🏾♂🤷🏿♂️🤷🏿♂🤷♀️🤷♀🤷🏻♀️🤷🏻♀🤷🏼♀️🤷🏼♀🤷🏽♀️🤷🏽♀🤷🏾♀️🤷🏾♀🤷🏿♀️🤷🏿♀💆💆🏻💆🏼💆🏽💆🏾💆🏿💆♂️💆♂💆🏻♂️💆🏻♂💆🏼♂️💆🏼♂💆🏽♂️💆🏽♂💆🏾♂️💆🏾♂💆🏿♂️💆🏿♂💆♀️💆♀💆🏻♀️💆🏻♀💆🏼♀️💆🏼♀💆🏽♀️💆🏽♀💆🏾♀️💆🏾♀💆🏿♀️💆🏿♀💇💇🏻💇🏼💇🏽💇🏾💇🏿💇♂️💇♂💇🏻♂️💇🏻♂💇🏼♂️💇🏼♂💇🏽♂️💇🏽♂💇🏾♂️💇🏾♂💇🏿♂️💇🏿♂💇♀️💇♀💇🏻♀️💇🏻♀💇🏼♀️💇🏼♀💇🏽♀️💇🏽♀💇🏾♀️💇🏾♀💇🏿♀️💇🏿♀🚶🚶🏻🚶🏼🚶🏽🚶🏾🚶🏿🚶♂️🚶♂🚶🏻♂️🚶🏻♂🚶🏼♂️🚶🏼♂🚶🏽♂️🚶🏽♂🚶🏾♂️🚶🏾♂🚶🏿♂️🚶🏿♂🚶♀️🚶♀🚶🏻♀️🚶🏻♀🚶🏼♀️🚶🏼♀🚶🏽♀️🚶🏽♀🚶🏾♀️🚶🏾♀🚶🏿♀️🚶🏿♀🏃🏃🏻🏃🏼🏃🏽🏃🏾🏃🏿🏃♂️🏃♂🏃🏻♂️🏃🏻♂🏃🏼♂️🏃🏼♂🏃🏽♂️🏃🏽♂🏃🏾♂️🏃🏾♂🏃🏿♂️🏃🏿♂🏃♀️🏃♀🏃🏻♀️🏃🏻♀🏃🏼♀️🏃🏼♀🏃🏽♀️🏃🏽♀🏃🏾♀️🏃🏾♀🏃🏿♀️🏃🏿♀💃💃🏻💃🏼💃🏽💃🏾💃🏿🕺🕺🏻🕺🏼🕺🏽🕺🏾🕺🏿👯👯♂️👯♂👯♀️👯♀🧖🧖🏻🧖🏼🧖🏽🧖🏾🧖🏿🧖♀️🧖♀🧖🏻♀️🧖🏻♀🧖🏼♀️🧖🏼♀🧖🏽♀️🧖🏽♀🧖🏾♀️🧖🏾♀🧖🏿♀️🧖🏿♀🧖♂️🧖♂🧖🏻♂️🧖🏻♂🧖🏼♂️🧖🏼♂🧖🏽♂️🧖🏽♂🧖🏾♂️🧖🏾♂🧖🏿♂️🧖🏿♂🧗🧗🏻🧗🏼🧗🏽🧗🏾🧗🏿🧗♀️🧗♀🧗🏻♀️🧗🏻♀🧗🏼♀️🧗🏼♀🧗🏽♀️🧗🏽♀🧗🏾♀️🧗🏾♀🧗🏿♀️🧗🏿♀🧗♂️🧗♂🧗🏻♂️🧗🏻♂🧗🏼♂️🧗🏼♂🧗🏽♂️🧗🏽♂🧗🏾♂️🧗🏾♂🧗🏿♂️🧗🏿♂🧘🧘🏻🧘🏼🧘🏽🧘🏾🧘🏿🧘♀️🧘♀🧘🏻♀️🧘🏻♀🧘🏼♀️🧘🏼♀🧘🏽♀️🧘🏽♀🧘🏾♀️🧘🏾♀🧘🏿♀️🧘🏿♀🧘♂️🧘♂🧘🏻♂️🧘🏻♂🧘🏼♂️🧘🏼♂🧘🏽♂️🧘🏽♂🧘🏾♂️🧘🏾♂🧘🏿♂️🧘🏿♂🛀🛀🏻🛀🏼🛀🏽🛀🏾🛀🏿🛌🛌🏻🛌🏼🛌🏽🛌🏾🛌🏿🕴️🕴🕴🏻🕴🏼🕴🏽🕴🏾🕴🏿🗣️🗣👤👥🤺🏇🏇🏻🏇🏼🏇🏽🏇🏾🏇🏿⛷️⛷🏂🏂🏻🏂🏼🏂🏽🏂🏾🏂🏿🏌️🏌🏌🏻🏌🏼🏌🏽🏌🏾🏌🏿🏌️♂️🏌♂️🏌️♂🏌♂🏌🏻♂️🏌🏻♂🏌🏼♂️🏌🏼♂🏌🏽♂️🏌🏽♂🏌🏾♂️🏌🏾♂🏌🏿♂️🏌🏿♂🏌️♀️🏌♀️🏌️♀🏌♀🏌🏻♀️🏌🏻♀🏌🏼♀️🏌🏼♀🏌🏽♀️🏌🏽♀🏌🏾♀️🏌🏾♀🏌🏿♀️🏌🏿♀🏄🏄🏻🏄🏼🏄🏽🏄🏾🏄🏿🏄♂️🏄♂🏄🏻♂️🏄🏻♂🏄🏼♂️🏄🏼♂🏄🏽♂️🏄🏽♂🏄🏾♂️🏄🏾♂🏄🏿♂️🏄🏿♂🏄♀️🏄♀🏄🏻♀️🏄🏻♀🏄🏼♀️🏄🏼♀🏄🏽♀️🏄🏽♀🏄🏾♀️🏄🏾♀🏄🏿♀️🏄🏿♀🚣🚣🏻🚣🏼🚣🏽🚣🏾🚣🏿🚣♂️🚣♂🚣🏻♂️🚣🏻♂🚣🏼♂️🚣🏼♂🚣🏽♂️🚣🏽♂🚣🏾♂️🚣🏾♂🚣🏿♂️🚣🏿♂🚣♀️🚣♀🚣🏻♀️🚣🏻♀🚣🏼♀️🚣🏼♀🚣🏽♀️🚣🏽♀🚣🏾♀️🚣🏾♀🚣🏿♀️🚣🏿♀🏊🏊🏻🏊🏼🏊🏽🏊🏾🏊🏿🏊♂️🏊♂🏊🏻♂️🏊🏻♂🏊🏼♂️🏊🏼♂🏊🏽♂️🏊🏽♂🏊🏾♂️🏊🏾♂🏊🏿♂️🏊🏿♂🏊♀️🏊♀🏊🏻♀️🏊🏻♀🏊🏼♀️🏊🏼♀🏊🏽♀️🏊🏽♀🏊🏾♀️🏊🏾♀🏊🏿♀️🏊🏿♀⛹️⛹⛹🏻⛹🏼⛹🏽⛹🏾⛹🏿⛹️♂️⛹♂️⛹️♂⛹♂⛹🏻♂️⛹🏻♂⛹🏼♂️⛹🏼♂⛹🏽♂️⛹🏽♂⛹🏾♂️⛹🏾♂⛹🏿♂️⛹🏿♂⛹️♀️⛹♀️⛹️♀⛹♀⛹🏻♀️⛹🏻♀⛹🏼♀️⛹🏼♀⛹🏽♀️⛹🏽♀⛹🏾♀️⛹🏾♀⛹🏿♀️⛹🏿♀🏋️🏋🏋🏻🏋🏼🏋🏽🏋🏾🏋🏿🏋️♂️🏋♂️🏋️♂🏋♂🏋🏻♂️🏋🏻♂🏋🏼♂️🏋🏼♂🏋🏽♂️🏋🏽♂🏋🏾♂️🏋🏾♂🏋🏿♂️🏋🏿♂🏋️♀️🏋♀️🏋️♀🏋♀🏋🏻♀️🏋🏻♀🏋🏼♀️🏋🏼♀🏋🏽♀️🏋🏽♀🏋🏾♀️🏋🏾♀🏋🏿♀️🏋🏿♀🚴🚴🏻🚴🏼🚴🏽🚴🏾🚴🏿🚴♂️🚴♂🚴🏻♂️🚴🏻♂🚴🏼♂️🚴🏼♂🚴🏽♂️🚴🏽♂🚴🏾♂️🚴🏾♂🚴🏿♂️🚴🏿♂🚴♀️🚴♀🚴🏻♀️🚴🏻♀🚴🏼♀️🚴🏼♀🚴🏽♀️🚴🏽♀🚴🏾♀️🚴🏾♀🚴🏿♀️🚴🏿♀🚵🚵🏻🚵🏼🚵🏽🚵🏾🚵🏿🚵♂️🚵♂🚵🏻♂️🚵🏻♂🚵🏼♂️🚵🏼♂🚵🏽♂️🚵🏽♂🚵🏾♂️🚵🏾♂🚵🏿♂️🚵🏿♂🚵♀️🚵♀🚵🏻♀️🚵🏻♀🚵🏼♀️🚵🏼♀🚵🏽♀️🚵🏽♀🚵🏾♀️🚵🏾♀🚵🏿♀️🚵🏿♀🏎️🏎🏍️🏍🤸🤸🏻🤸🏼🤸🏽🤸🏾🤸🏿🤸♂️🤸♂🤸🏻♂️🤸🏻♂🤸🏼♂️🤸🏼♂🤸🏽♂️🤸🏽♂🤸🏾♂️🤸🏾♂🤸🏿♂️🤸🏿♂🤸♀️🤸♀🤸🏻♀️🤸🏻♀🤸🏼♀️🤸🏼♀🤸🏽♀️🤸🏽♀🤸🏾♀️🤸🏾♀🤸🏿♀️🤸🏿♀🤼🤼♂️🤼♂🤼♀️🤼♀🤽🤽🏻🤽🏼🤽🏽🤽🏾🤽🏿🤽♂️🤽♂🤽🏻♂️🤽🏻♂🤽🏼♂️🤽🏼♂🤽🏽♂️🤽🏽♂🤽🏾♂️🤽🏾♂🤽🏿♂️🤽🏿♂🤽♀️🤽♀🤽🏻♀️🤽🏻♀🤽🏼♀️🤽🏼♀🤽🏽♀️🤽🏽♀🤽🏾♀️🤽🏾♀🤽🏿♀️🤽🏿♀🤾🤾🏻🤾🏼🤾🏽🤾🏾🤾🏿🤾♂️🤾♂🤾🏻♂️🤾🏻♂🤾🏼♂️🤾🏼♂🤾🏽♂️🤾🏽♂🤾🏾♂️🤾🏾♂🤾🏿♂️🤾🏿♂🤾♀️🤾♀🤾🏻♀️🤾🏻♀🤾🏼♀️🤾🏼♀🤾🏽♀️🤾🏽♀🤾🏾♀️🤾🏾♀🤾🏿♀️🤾🏿♀🤹🤹🏻🤹🏼🤹🏽🤹🏾🤹🏿🤹♂️🤹♂🤹🏻♂️🤹🏻♂🤹🏼♂️🤹🏼♂🤹🏽♂️🤹🏽♂🤹🏾♂️🤹🏾♂🤹🏿♂️🤹🏿♂🤹♀️🤹♀🤹🏻♀️🤹🏻♀🤹🏼♀️🤹🏼♀🤹🏽♀️🤹🏽♀🤹🏾♀️🤹🏾♀🤹🏿♀️🤹🏿♀👫👬👭💏👩❤️💋👨👩❤💋👨👨❤️💋👨👨❤💋👨👩❤️💋👩👩❤💋👩💑👩❤️👨👩❤👨👨❤️👨👨❤👨👩❤️👩👩❤👩👪👨👩👦👨👩👧👨👩👧👦👨👩👦👦👨👩👧👧👨👨👦👨👨👧👨👨👧👦👨👨👦👦👨👨👧👧👩👩👦👩👩👧👩👩👧👦👩👩👦👦👩👩👧👧👨👦👨👦👦👨👧👨👧👦👨👧👧👩👦👩👦👦👩👧👩👧👦👩👧👧🤳🤳🏻🤳🏼🤳🏽🤳🏾🤳🏿💪💪🏻💪🏼💪🏽💪🏾💪🏿🦵🦵🏻🦵🏼🦵🏽🦵🏾🦵🏿🦶🦶🏻🦶🏼🦶🏽🦶🏾🦶🏿👈👈🏻👈🏼👈🏽👈🏾👈🏿👉👉🏻👉🏼👉🏽👉🏾👉🏿☝️☝☝🏻☝🏼☝🏽☝🏾☝🏿👆👆🏻👆🏼👆🏽👆🏾👆🏿🖕🖕🏻🖕🏼🖕🏽🖕🏾🖕🏿👇👇🏻👇🏼👇🏽👇🏾👇🏿✌️✌✌🏻✌🏼✌🏽✌🏾✌🏿🤞🤞🏻🤞🏼🤞🏽🤞🏾🤞🏿🖖🖖🏻🖖🏼🖖🏽🖖🏾🖖🏿🤘🤘🏻🤘🏼🤘🏽🤘🏾🤘🏿🤙🤙🏻🤙🏼🤙🏽🤙🏾🤙🏿🖐️🖐🖐🏻🖐🏼🖐🏽🖐🏾🖐🏿✋✋🏻✋🏼✋🏽✋🏾✋🏿👌👌🏻👌🏼👌🏽👌🏾👌🏿👍👍🏻👍🏼👍🏽👍🏾👍🏿👎👎🏻👎🏼👎🏽👎🏾👎🏿✊✊🏻✊🏼✊🏽✊🏾✊🏿👊👊🏻👊🏼👊🏽👊🏾👊🏿🤛🤛🏻🤛🏼🤛🏽🤛🏾🤛🏿🤜🤜🏻🤜🏼🤜🏽🤜🏾🤜🏿🤚🤚🏻🤚🏼🤚🏽🤚🏾🤚🏿👋👋🏻👋🏼👋🏽👋🏾👋🏿🤟🤟🏻🤟🏼🤟🏽🤟🏾🤟🏿✍️✍✍🏻✍🏼✍🏽✍🏾✍🏿👏👏🏻👏🏼👏🏽👏🏾👏🏿👐👐🏻👐🏼👐🏽👐🏾👐🏿🙌🙌🏻🙌🏼🙌🏽🙌🏾🙌🏿🤲🤲🏻🤲🏼🤲🏽🤲🏾🤲🏿🙏🙏🏻🙏🏼🙏🏽🙏🏾🙏🏿🤝💅💅🏻💅🏼💅🏽💅🏾💅🏿👂👂🏻👂🏼👂🏽👂🏾👂🏿👃👃🏻👃🏼👃🏽👃🏾👃🏿🦰🦱🦲🦳👣👀👁️👁👁️🗨️👁🗨️👁️🗨👁🗨🧠🦴🦷👅👄💋💘❤️❤💓💔💕💖💗💙💚💛🧡💜🖤💝💞💟❣️❣💌💤💢💣💥💦💨💫💬🗨️🗨🗯️🗯💭🕳️🕳👓🕶️🕶🥽🥼👔👕👖🧣🧤🧥🧦👗👘👙👚👛👜👝🛍️🛍🎒👞👟🥾🥿👠👡👢👑👒🎩🎓🧢⛑️⛑📿💄💍💎🐵🐒🦍🐶🐕🐩🐺🦊🦝🐱🐈🦁🐯🐅🐆🐴🐎🦄🦓🦌🐮🐂🐃🐄🐷🐖🐗🐽🐏🐑🐐🐪🐫🦙🦒🐘🦏🦛🐭🐁🐀🐹🐰🐇🐿️🐿🦔🦇🐻🐨🐼🦘🦡🐾🦃🐔🐓🐣🐤🐥🐦🐧🕊️🕊🦅🦆🦢🦉🦚🦜🐸🐊🐢🦎🐍🐲🐉🦕🦖🐳🐋🐬🐟🐠🐡🦈🐙🐚🦀🦞🦐🦑🐌🦋🐛🐜🐝🐞🦗🕷️🕷🕸️🕸🦂🦟🦠💐🌸💮🏵️🏵🌹🥀🌺🌻🌼🌷🌱🌲🌳🌴🌵🌾🌿☘️☘🍀🍁🍂🍃🍇🍈🍉🍊🍋🍌🍍🥭🍎🍏🍐🍑🍒🍓🥝🍅🥥🥑🍆🥔🥕🌽🌶️🌶🥒🥬🥦🍄🥜🌰🍞🥐🥖🥨🥯🥞🧀🍖🍗🥩🥓🍔🍟🍕🌭🥪🌮🌯🥙🥚🍳🥘🍲🥣🥗🍿🧂🥫🍱🍘🍙🍚🍛🍜🍝🍠🍢🍣🍤🍥🥮🍡🥟🥠🥡🍦🍧🍨🍩🍪🎂🍰🧁🥧🍫🍬🍭🍮🍯🍼🥛☕🍵🍶🍾🍷🍸🍹🍺🍻🥂🥃🥤🥢🍽️🍽🍴🥄🔪🏺🌍🌎🌏🌐🗺️🗺🗾🧭🏔️🏔⛰️⛰🌋🗻🏕️🏕🏖️🏖🏜️🏜🏝️🏝🏞️🏞🏟️🏟🏛️🏛🏗️🏗🧱🏘️🏘🏚️🏚🏠🏡🏢🏣🏤🏥🏦🏨🏩🏪🏫🏬🏭🏯🏰💒🗼🗽⛪🕌🕍⛩️⛩🕋⛲⛺🌁🌃🏙️🏙🌄🌅🌆🌇🌉♨️♨🌌🎠🎡🎢💈🎪🚂🚃🚄🚅🚆🚇🚈🚉🚊🚝🚞🚋🚌🚍🚎🚐🚑🚒🚓🚔🚕🚖🚗🚘🚙🚚🚛🚜🚲🛴🛹🛵🚏🛣️🛣🛤️🛤🛢️🛢⛽🚨🚥🚦🛑🚧⚓⛵🛶🚤🛳️🛳⛴️⛴🛥️🛥🚢✈️✈🛩️🛩🛫🛬💺🚁🚟🚠🚡🛰️🛰🚀🛸🛎️🛎🧳⌛⏳⌚⏰⏱️⏱⏲️⏲🕰️🕰🕛🕧🕐🕜🕑🕝🕒🕞🕓🕟🕔🕠🕕🕡🕖🕢🕗🕣🕘🕤🕙🕥🕚🕦🌑🌒🌓🌔🌕🌖🌗🌘🌙🌚🌛🌜🌡️🌡☀️☀🌝🌞⭐🌟🌠☁️☁⛅⛈️⛈🌤️🌤🌥️🌥🌦️🌦🌧️🌧🌨️🌨🌩️🌩🌪️🌪🌫️🌫🌬️🌬🌀🌈🌂☂️☂☔⛱️⛱⚡❄️❄☃️☃⛄☄️☄🔥💧🌊🎃🎄🎆🎇🧨✨🎈🎉🎊🎋🎍🎎🎏🎐🎑🧧🎀🎁🎗️🎗🎟️🎟🎫🎖️🎖🏆🏅🥇🥈🥉⚽⚾🥎🏀🏐🏈🏉🎾🥏🎳🏏🏑🏒🥍🏓🏸🥊🥋🥅⛳⛸️⛸🎣🎽🎿🛷🥌🎯🎱🔮🧿🎮🕹️🕹🎰🎲🧩🧸♠️♠♥️♥♦️♦♣️♣♟️♟🃏🀄🎴🎭🖼️🖼🎨🧵🧶🔇🔈🔉🔊📢📣📯🔔🔕🎼🎵🎶🎙️🎙🎚️🎚🎛️🎛🎤🎧📻🎷🎸🎹🎺🎻🥁📱📲☎️☎📞📟📠🔋🔌💻🖥️🖥🖨️🖨⌨️⌨🖱️🖱🖲️🖲💽💾💿📀🧮🎥🎞️🎞📽️📽🎬📺📷📸📹📼🔍🔎🕯️🕯💡🔦🏮📔📕📖📗📘📙📚📓📒📃📜📄📰🗞️🗞📑🔖🏷️🏷💰💴💵💶💷💸💳🧾💹💱💲✉️✉📧📨📩📤📥📦📫📪📬📭📮🗳️🗳✏️✏✒️✒🖋️🖋🖊️🖊🖌️🖌🖍️🖍📝💼📁📂🗂️🗂📅📆🗒️🗒🗓️🗓📇📈📉📊📋📌📍📎🖇️🖇📏📐✂️✂🗃️🗃🗄️🗄🗑️🗑🔒🔓🔏🔐🔑🗝️🗝🔨⛏️⛏⚒️⚒🛠️🛠🗡️🗡⚔️⚔🔫🏹🛡️🛡🔧🔩⚙️⚙🗜️🗜⚖️⚖🔗⛓️⛓🧰🧲⚗️⚗🧪🧫🧬🔬🔭📡💉💊🚪🛏️🛏🛋️🛋🚽🚿🛁🧴🧷🧹🧺🧻🧼🧽🧯🛒🚬⚰️⚰⚱️⚱🗿🏧🚮🚰♿🚹🚺🚻🚼🚾🛂🛃🛄🛅⚠️⚠🚸⛔🚫🚳🚭🚯🚱🚷📵🔞☢️☢☣️☣⬆️⬆↗️↗➡️➡↘️↘⬇️⬇↙️↙⬅️⬅↖️↖↕️↕↔️↔↩️↩↪️↪⤴️⤴⤵️⤵🔃🔄🔙🔚🔛🔜🔝🛐⚛️⚛🕉️🕉✡️✡☸️☸☯️☯✝️✝☦️☦☪️☪☮️☮🕎🔯♈♉♊♋♌♍♎♏♐♑♒♓⛎🔀🔁🔂▶️▶⏩⏭️⏭⏯️⏯◀️◀⏪⏮️⏮🔼⏫🔽⏬⏸️⏸⏹️⏹⏺️⏺⏏️⏏🎦🔅🔆📶📳📴♀️♀♂️♂⚕️⚕♾️♾♻️♻⚜️⚜🔱📛🔰⭕✅☑️☑✔️✔✖️✖❌❎➕➖➗➰➿〽️〽✳️✳✴️✴❇️❇‼️‼⁉️⁉❓❔❕❗〰️〰©️©®️®™️™#️⃣#⃣*️⃣*⃣0️⃣0⃣1️⃣1⃣2️⃣2⃣3️⃣3⃣4️⃣4⃣5️⃣5⃣6️⃣6⃣7️⃣7⃣8️⃣8⃣9️⃣9⃣🔟💯🔠🔡🔢🔣🔤🅰️🅰🆎🅱️🅱🆑🆒🆓ℹ️ℹ🆔Ⓜ️Ⓜ🆕🆖🅾️🅾🆗🅿️🅿🆘🆙🆚🈁🈂️🈂🈷️🈷🈶🈯🉐🈹🈚🈲🉑🈸🈴🈳㊗️㊗㊙️㊙🈺🈵▪️▪▫️▫◻️◻◼️◼◽◾⬛⬜🔶🔷🔸🔹🔺🔻💠🔘🔲🔳⚪⚫🔴🔵🏁🚩🎌🏴🏳️🏳🏳️🌈🏳🌈🏴☠️🏴☠🇦🇨🇦🇩🇦🇪🇦🇫🇦🇬🇦🇮🇦🇱🇦🇲🇦🇴🇦🇶🇦🇷🇦🇸🇦🇹🇦🇺🇦🇼🇦🇽🇦🇿🇧🇦🇧🇧🇧🇩🇧🇪🇧🇫🇧🇬🇧🇭🇧🇮🇧🇯🇧🇱🇧🇲🇧🇳🇧🇴🇧🇶🇧🇷🇧🇸🇧🇹🇧🇻🇧🇼🇧🇾🇧🇿🇨🇦🇨🇨🇨🇩🇨🇫🇨🇬🇨🇭🇨🇮🇨🇰🇨🇱🇨🇲🇨🇳🇨🇴🇨🇵🇨🇷🇨🇺🇨🇻🇨🇼🇨🇽🇨🇾🇨🇿🇩🇪🇩🇬🇩🇯🇩🇰🇩🇲🇩🇴🇩🇿🇪🇦🇪🇨🇪🇪🇪🇬🇪🇭🇪🇷🇪🇸🇪🇹🇪🇺🇫🇮🇫🇯🇫🇰🇫🇲🇫🇴🇫🇷🇬🇦🇬🇧🇬🇩🇬🇪🇬🇫🇬🇬🇬🇭🇬🇮🇬🇱🇬🇲🇬🇳🇬🇵🇬🇶🇬🇷🇬🇸🇬🇹🇬🇺🇬🇼🇬🇾🇭🇰🇭🇲🇭🇳🇭🇷🇭🇹🇭🇺🇮🇨🇮🇩🇮🇪🇮🇱🇮🇲🇮🇳🇮🇴🇮🇶🇮🇷🇮🇸🇮🇹🇯🇪🇯🇲🇯🇴🇯🇵🇰🇪🇰🇬🇰🇭🇰🇮🇰🇲🇰🇳🇰🇵🇰🇷🇰🇼🇰🇾🇰🇿🇱🇦🇱🇧🇱🇨🇱🇮🇱🇰🇱🇷🇱🇸🇱🇹🇱🇺🇱🇻🇱🇾🇲🇦🇲🇨🇲🇩🇲🇪🇲🇫🇲🇬🇲🇭🇲🇰🇲🇱🇲🇲🇲🇳🇲🇴🇲🇵🇲🇶🇲🇷🇲🇸🇲🇹🇲🇺🇲🇻🇲🇼🇲🇽🇲🇾🇲🇿🇳🇦🇳🇨🇳🇪🇳🇫🇳🇬🇳🇮🇳🇱🇳🇴🇳🇵🇳🇷🇳🇺🇳🇿🇴🇲🇵🇦🇵🇪🇵🇫🇵🇬🇵🇭🇵🇰🇵🇱🇵🇲🇵🇳🇵🇷🇵🇸🇵🇹🇵🇼🇵🇾🇶🇦🇷🇪🇷🇴🇷🇸🇷🇺🇷🇼🇸🇦🇸🇧🇸🇨🇸🇩🇸🇪🇸🇬🇸🇭🇸🇮🇸🇯🇸🇰🇸🇱🇸🇲🇸🇳🇸🇴🇸🇷🇸🇸🇸🇹🇸🇻🇸🇽🇸🇾🇸🇿🇹🇦🇹🇨🇹🇩🇹🇫🇹🇬🇹🇭🇹🇯🇹🇰🇹🇱🇹🇲🇹🇳🇹🇴🇹🇷🇹🇹🇹🇻🇹🇼🇹🇿🇺🇦🇺🇬🇺🇲🇺🇳🇺🇸🇺🇾🇺🇿🇻🇦🇻🇨🇻🇪🇻🇬🇻🇮🇻🇳🇻🇺🇼🇫🇼🇸🇽🇰🇾🇪🇾🇹🇿🇦🇿🇲🇿🇼🏴🏴🏴" ); expect(() => emoji.parse(":-)")).toThrow(); expect(() => emoji.parse("😀 is an emoji")).toThrow(); expect(() => emoji.parse("😀stuff")).toThrow(); expect(() => emoji.parse("stuff😀")).toThrow(); }); test("uuid", () => { const uuid = z.string().uuid("custom error"); uuid.parse("9491d710-3185-4e06-bea0-6a2f275345e0"); uuid.parse("d89e7b01-7598-ed11-9d7a-0022489382fd"); // new sequential id uuid.parse("00000000-0000-0000-0000-000000000000"); uuid.parse("b3ce60f8-e8b9-40f5-1150-172ede56ff74"); // Variant 0 - RFC 4122: Reserved, NCS backward compatibility uuid.parse("92e76bf9-28b3-4730-cd7f-cb6bc51f8c09"); // Variant 2 - RFC 4122: Reserved, Microsoft Corporation backward compatibility const result = uuid.safeParse("9491d710-3185-4e06-bea0-6a2f275345e0X"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("bad uuid", () => { const uuid = z.string().uuid("custom error"); uuid.parse("9491d710-3185-4e06-bea0-6a2f275345e0"); const result = uuid.safeParse("invalid uuid"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("nanoid", () => { const nanoid = z.string().nanoid("custom error"); nanoid.parse("lfNZluvAxMkf7Q8C5H-QS"); nanoid.parse("mIU_4PJWikaU8fMbmkouz"); nanoid.parse("Hb9ZUtUa2JDm_dD-47EGv"); nanoid.parse("5Noocgv_8vQ9oPijj4ioQ"); const result = nanoid.safeParse("Xq90uDyhddC53KsoASYJGX"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("bad nanoid", () => { const nanoid = z.string().nanoid("custom error"); nanoid.parse("ySh_984wpDUu7IQRrLXAp"); const result = nanoid.safeParse("invalid nanoid"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("cuid", () => { const cuid = z.string().cuid(); cuid.parse("ckopqwooh000001la8mbi2im9"); const result = cuid.safeParse("cifjhdsfhsd-invalid-cuid"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("Invalid cuid"); } }); test("cuid2", () => { const cuid2 = z.string().cuid2(); const validStrings = [ "a", // short string "tz4a98xxat96iws9zmbrgj3a", // normal string "kf5vz6ssxe4zjcb409rjgo747tc5qjazgptvotk6", // longer than require("@paralleldrive/cuid2").bigLength ]; for (const s of validStrings) { cuid2.parse(s); } const invalidStrings = [ "", // empty string "tz4a98xxat96iws9zMbrgj3a", // include uppercase "tz4a98xxat96iws-zmbrgj3a", // involve symbols ]; const results = invalidStrings.map((s) => cuid2.safeParse(s)); expect(results.every((r) => !r.success)).toEqual(true); if (!results[0].success) { expect(results[0].error.issues[0].message).toEqual("Invalid cuid2"); } }); test("ulid", () => { const ulid = z.string().ulid(); ulid.parse("01ARZ3NDEKTSV4RRFFQ69G5FAV"); const result = ulid.safeParse("invalidulid"); expect(result.success).toEqual(false); const tooLong = "01ARZ3NDEKTSV4RRFFQ69G5FAVA"; expect(ulid.safeParse(tooLong).success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("Invalid ulid"); } const caseInsensitive = ulid.safeParse("01arZ3nDeKTsV4RRffQ69G5FAV"); expect(caseInsensitive.success).toEqual(true); }); test("regex", () => { z.string() .regex(/^moo+$/) .parse("mooooo"); expect(() => z.string().uuid().parse("purr")).toThrow(); }); test("regexp error message", () => { const result = z .string() .regex(/^moo+$/) .safeParse("boooo"); if (!result.success) { expect(result.error.issues[0].message).toEqual("Invalid"); } else { throw new Error("validation should have failed"); } expect(() => z.string().uuid().parse("purr")).toThrow(); }); test("regex lastIndex reset", () => { const schema = z.string().regex(/^\d+$/g); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); }); test("checks getters", () => { expect(z.string().email().isEmail).toEqual(true); expect(z.string().email().isURL).toEqual(false); expect(z.string().email().isCUID).toEqual(false); expect(z.string().email().isCUID2).toEqual(false); expect(z.string().email().isUUID).toEqual(false); expect(z.string().email().isNANOID).toEqual(false); expect(z.string().email().isIP).toEqual(false); expect(z.string().email().isCIDR).toEqual(false); expect(z.string().email().isULID).toEqual(false); expect(z.string().url().isEmail).toEqual(false); expect(z.string().url().isURL).toEqual(true); expect(z.string().url().isCUID).toEqual(false); expect(z.string().url().isCUID2).toEqual(false); expect(z.string().url().isUUID).toEqual(false); expect(z.string().url().isNANOID).toEqual(false); expect(z.string().url().isIP).toEqual(false); expect(z.string().url().isCIDR).toEqual(false); expect(z.string().url().isULID).toEqual(false); expect(z.string().cuid().isEmail).toEqual(false); expect(z.string().cuid().isURL).toEqual(false); expect(z.string().cuid().isCUID).toEqual(true); expect(z.string().cuid().isCUID2).toEqual(false); expect(z.string().cuid().isUUID).toEqual(false); expect(z.string().cuid().isNANOID).toEqual(false); expect(z.string().cuid().isIP).toEqual(false); expect(z.string().cuid().isCIDR).toEqual(false); expect(z.string().cuid().isULID).toEqual(false); expect(z.string().cuid2().isEmail).toEqual(false); expect(z.string().cuid2().isURL).toEqual(false); expect(z.string().cuid2().isCUID).toEqual(false); expect(z.string().cuid2().isCUID2).toEqual(true); expect(z.string().cuid2().isUUID).toEqual(false); expect(z.string().cuid2().isNANOID).toEqual(false); expect(z.string().cuid2().isIP).toEqual(false); expect(z.string().cuid2().isCIDR).toEqual(false); expect(z.string().cuid2().isULID).toEqual(false); expect(z.string().uuid().isEmail).toEqual(false); expect(z.string().uuid().isURL).toEqual(false); expect(z.string().uuid().isCUID).toEqual(false); expect(z.string().uuid().isCUID2).toEqual(false); expect(z.string().uuid().isUUID).toEqual(true); expect(z.string().uuid().isNANOID).toEqual(false); expect(z.string().uuid().isIP).toEqual(false); expect(z.string().uuid().isCIDR).toEqual(false); expect(z.string().uuid().isULID).toEqual(false); expect(z.string().nanoid().isEmail).toEqual(false); expect(z.string().nanoid().isURL).toEqual(false); expect(z.string().nanoid().isCUID).toEqual(false); expect(z.string().nanoid().isCUID2).toEqual(false); expect(z.string().nanoid().isUUID).toEqual(false); expect(z.string().nanoid().isNANOID).toEqual(true); expect(z.string().nanoid().isIP).toEqual(false); expect(z.string().nanoid().isCIDR).toEqual(false); expect(z.string().nanoid().isULID).toEqual(false); expect(z.string().ip().isEmail).toEqual(false); expect(z.string().ip().isURL).toEqual(false); expect(z.string().ip().isCUID).toEqual(false); expect(z.string().ip().isCUID2).toEqual(false); expect(z.string().ip().isUUID).toEqual(false); expect(z.string().ip().isNANOID).toEqual(false); expect(z.string().ip().isIP).toEqual(true); expect(z.string().ip().isCIDR).toEqual(false); expect(z.string().ip().isULID).toEqual(false); expect(z.string().cidr().isEmail).toEqual(false); expect(z.string().cidr().isURL).toEqual(false); expect(z.string().cidr().isCUID).toEqual(false); expect(z.string().cidr().isCUID2).toEqual(false); expect(z.string().cidr().isUUID).toEqual(false); expect(z.string().cidr().isNANOID).toEqual(false); expect(z.string().cidr().isIP).toEqual(false); expect(z.string().cidr().isCIDR).toEqual(true); expect(z.string().cidr().isULID).toEqual(false); expect(z.string().ulid().isEmail).toEqual(false); expect(z.string().ulid().isURL).toEqual(false); expect(z.string().ulid().isCUID).toEqual(false); expect(z.string().ulid().isCUID2).toEqual(false); expect(z.string().ulid().isUUID).toEqual(false); expect(z.string().ulid().isNANOID).toEqual(false); expect(z.string().ulid().isIP).toEqual(false); expect(z.string().ulid().isCIDR).toEqual(false); expect(z.string().ulid().isULID).toEqual(true); }); test("min max getters", () => { expect(z.string().min(5).minLength).toEqual(5); expect(z.string().min(5).min(10).minLength).toEqual(10); expect(z.string().minLength).toEqual(null); expect(z.string().max(5).maxLength).toEqual(5); expect(z.string().max(5).max(1).maxLength).toEqual(1); expect(z.string().maxLength).toEqual(null); }); test("trim", () => { expect(z.string().trim().min(2).parse(" 12 ")).toEqual("12"); // ordering of methods is respected expect(z.string().min(2).trim().parse(" 1 ")).toEqual("1"); expect(() => z.string().trim().min(2).parse(" 1 ")).toThrow(); }); test("lowerCase", () => { expect(z.string().toLowerCase().parse("ASDF")).toEqual("asdf"); expect(z.string().toUpperCase().parse("asdf")).toEqual("ASDF"); }); test("datetime", () => { const a = z.string().datetime({}); expect(a.isDatetime).toEqual(true); const b = z.string().datetime({ offset: true }); expect(b.isDatetime).toEqual(true); const c = z.string().datetime({ precision: 3 }); expect(c.isDatetime).toEqual(true); const d = z.string().datetime({ offset: true, precision: 0 }); expect(d.isDatetime).toEqual(true); const { isDatetime } = z.string().datetime(); expect(isDatetime).toEqual(true); }); test("datetime parsing", () => { const datetime = z.string().datetime(); datetime.parse("1970-01-01T00:00:00.000Z"); datetime.parse("2022-10-13T09:52:31.816Z"); datetime.parse("2022-10-13T09:52:31.8162314Z"); datetime.parse("1970-01-01T00:00:00Z"); datetime.parse("2022-10-13T09:52:31Z"); datetime.parse("2022-10-13T09:52Z"); expect(() => datetime.parse("")).toThrow(); expect(() => datetime.parse("foo")).toThrow(); expect(() => datetime.parse("2020-10-14")).toThrow(); expect(() => datetime.parse("T18:45:12.123")).toThrow(); expect(() => datetime.parse("2020-10-14T17:42:29+00:00")).toThrow(); expect(() => datetime.parse("2020-10-14T17:42.123+00:00")).toThrow(); const datetimeNoMs = z.string().datetime({ precision: 0 }); datetimeNoMs.parse("1970-01-01T00:00:00Z"); datetimeNoMs.parse("2022-10-13T09:52:31Z"); datetimeNoMs.parse("2022-10-13T09:52Z"); expect(() => datetimeNoMs.parse("tuna")).toThrow(); expect(() => datetimeNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow(); expect(() => datetimeNoMs.parse("1970-01-01T00:00:00.Z")).toThrow(); expect(() => datetimeNoMs.parse("2022-10-13T09:52:31.816Z")).toThrow(); const datetime3Ms = z.string().datetime({ precision: 3 }); datetime3Ms.parse("1970-01-01T00:00:00.000Z"); datetime3Ms.parse("2022-10-13T09:52:31.123Z"); expect(() => datetime3Ms.parse("tuna")).toThrow(); expect(() => datetime3Ms.parse("1970-01-01T00:00:00.1Z")).toThrow(); expect(() => datetime3Ms.parse("1970-01-01T00:00:00.12Z")).toThrow(); expect(() => datetime3Ms.parse("2022-10-13T09:52:31Z")).toThrow(); expect(() => datetime3Ms.parse("2022-10-13T09:52Z")).toThrow(); const datetimeOffset = z.string().datetime({ offset: true }); datetimeOffset.parse("1970-01-01T00:00:00.000Z"); datetimeOffset.parse("2022-10-13T09:52:31.816234134Z"); datetimeOffset.parse("1970-01-01T00:00:00Z"); datetimeOffset.parse("2022-10-13T09:52:31.4Z"); datetimeOffset.parse("2020-10-14T17:42:29+00:00"); datetimeOffset.parse("2020-10-14T17:42:29+03:15"); datetimeOffset.parse("2020-10-14T17:42:29+0315"); datetimeOffset.parse("2020-10-14T17:42+0315"); expect(() => datetimeOffset.parse("2020-10-14T17:42:29+03")); expect(() => datetimeOffset.parse("tuna")).toThrow(); expect(() => datetimeOffset.parse("2022-10-13T09:52:31.Z")).toThrow(); const datetimeOffsetNoMs = z.string().datetime({ offset: true, precision: 0 }); datetimeOffsetNoMs.parse("1970-01-01T00:00:00Z"); datetimeOffsetNoMs.parse("2022-10-13T09:52:31Z"); datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00:00"); datetimeOffsetNoMs.parse("2020-10-14T17:42:29+0000"); datetimeOffsetNoMs.parse("2020-10-14T17:42+0000"); expect(() => datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00")).toThrow(); expect(() => datetimeOffsetNoMs.parse("tuna")).toThrow(); expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow(); expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.Z")).toThrow(); expect(() => datetimeOffsetNoMs.parse("2022-10-13T09:52:31.816Z")).toThrow(); expect(() => datetimeOffsetNoMs.parse("2020-10-14T17:42:29.124+00:00")).toThrow(); const datetimeOffset4Ms = z.string().datetime({ offset: true, precision: 4 }); datetimeOffset4Ms.parse("1970-01-01T00:00:00.1234Z"); datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00:00"); datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+0000"); expect(() => datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00")).toThrow(); expect(() => datetimeOffset4Ms.parse("tuna")).toThrow(); expect(() => datetimeOffset4Ms.parse("1970-01-01T00:00:00.123Z")).toThrow(); expect(() => datetimeOffset4Ms.parse("2020-10-14T17:42:29.124+00:00")).toThrow(); expect(() => datetimeOffset4Ms.parse("2020-10-14T17:42+00:00")).toThrow(); }); test("date", () => { const a = z.string().date(); expect(a.isDate).toEqual(true); }); test("date parsing", () => { const date = z.string().date(); date.parse("1970-01-01"); date.parse("2022-01-31"); date.parse("2022-03-31"); date.parse("2022-04-30"); date.parse("2022-05-31"); date.parse("2022-06-30"); date.parse("2022-07-31"); date.parse("2022-08-31"); date.parse("2022-09-30"); date.parse("2022-10-31"); date.parse("2022-11-30"); date.parse("2022-12-31"); date.parse("2000-02-29"); date.parse("2400-02-29"); expect(() => date.parse("2022-02-29")).toThrow(); expect(() => date.parse("2100-02-29")).toThrow(); expect(() => date.parse("2200-02-29")).toThrow(); expect(() => date.parse("2300-02-29")).toThrow(); expect(() => date.parse("2500-02-29")).toThrow(); expect(() => date.parse("")).toThrow(); expect(() => date.parse("foo")).toThrow(); expect(() => date.parse("200-01-01")).toThrow(); expect(() => date.parse("20000-01-01")).toThrow(); expect(() => date.parse("2000-0-01")).toThrow(); expect(() => date.parse("2000-011-01")).toThrow(); expect(() => date.parse("2000-01-0")).toThrow(); expect(() => date.parse("2000-01-011")).toThrow(); expect(() => date.parse("2000/01/01")).toThrow(); expect(() => date.parse("01-01-2022")).toThrow(); expect(() => date.parse("01/01/2022")).toThrow(); expect(() => date.parse("2000-01-01 00:00:00Z")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29+00:00")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29Z")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29.123Z")).toThrow(); expect(() => date.parse("2000-00-12")).toThrow(); expect(() => date.parse("2000-12-00")).toThrow(); expect(() => date.parse("2000-01-32")).toThrow(); expect(() => date.parse("2000-13-01")).toThrow(); expect(() => date.parse("2000-21-01")).toThrow(); expect(() => date.parse("2000-02-30")).toThrow(); expect(() => date.parse("2000-02-31")).toThrow(); expect(() => date.parse("2000-04-31")).toThrow(); expect(() => date.parse("2000-06-31")).toThrow(); expect(() => date.parse("2000-09-31")).toThrow(); expect(() => date.parse("2000-11-31")).toThrow(); }); test("time", () => { const a = z.string().time(); expect(a.isTime).toEqual(true); }); test("time parsing", () => { const time = z.string().time(); time.parse("00:00:00"); time.parse("23:00:00"); time.parse("00:59:00"); time.parse("00:00:59"); time.parse("23:59:59"); time.parse("09:52:31"); time.parse("23:59:59.9999999"); time.parse("23:59"); expect(() => time.parse("")).toThrow(); expect(() => time.parse("foo")).toThrow(); expect(() => time.parse("00:00:00Z")).toThrow(); expect(() => time.parse("0:00:00")).toThrow(); expect(() => time.parse("00:0:00")).toThrow(); expect(() => time.parse("00:00:0")).toThrow(); expect(() => time.parse("00:00:00.000+00:00")).toThrow(); expect(() => time.parse("24:00:00")).toThrow(); expect(() => time.parse("00:60:00")).toThrow(); expect(() => time.parse("00:00:60")).toThrow(); expect(() => time.parse("24:60:60")).toThrow(); expect(() => time.parse("24:60")).toThrow(); const time2 = z.string().time({ precision: 2 }); time2.parse("00:00:00.00"); time2.parse("09:52:31.12"); time2.parse("23:59:59.99"); expect(() => time2.parse("")).toThrow(); expect(() => time2.parse("foo")).toThrow(); expect(() => time2.parse("00:00:00")).toThrow(); expect(() => time2.parse("00:00:00.00Z")).toThrow(); expect(() => time2.parse("00:00:00.0")).toThrow(); expect(() => time2.parse("00:00:00.000")).toThrow(); expect(() => time2.parse("00:00:00.00+00:00")).toThrow(); expect(() => time2.parse("23:59")).toThrow(); // const time3 = z.string().time({ offset: true }); // time3.parse("00:00:00Z"); // time3.parse("09:52:31Z"); // time3.parse("00:00:00+00:00"); // time3.parse("00:00:00+0000"); // time3.parse("00:00:00.000Z"); // time3.parse("00:00:00.000+00:00"); // time3.parse("00:00:00.000+0000"); // expect(() => time3.parse("")).toThrow(); // expect(() => time3.parse("foo")).toThrow(); // expect(() => time3.parse("00:00:00")).toThrow(); // expect(() => time3.parse("00:00:00.000")).toThrow(); // const time4 = z.string().time({ offset: true, precision: 0 }); // time4.parse("00:00:00Z"); // time4.parse("09:52:31Z"); // time4.parse("00:00:00+00:00"); // time4.parse("00:00:00+0000"); // expect(() => time4.parse("")).toThrow(); // expect(() => time4.parse("foo")).toThrow(); // expect(() => time4.parse("00:00:00.0")).toThrow(); // expect(() => time4.parse("00:00:00.000")).toThrow(); // expect(() => time4.parse("00:00:00.000+00:00")).toThrow(); }); test("duration", () => { const duration = z.string().duration(); expect(duration.isDuration).toEqual(true); const validDurations = [ "P3Y6M4DT12H30M5S", "P2Y9M3DT12H31M8.001S", "+P3Y6M4DT12H30M5S", "-PT0.001S", "+PT0.001S", "PT0,001S", "PT12H30M5S", "-P2M1D", "P-2M-1D", "-P5DT10H", "P-5DT-10H", "P1Y", "P2MT30M", "PT6H", "P5W", "P0.5Y", "P0,5Y", "P42YT7.004M", ]; const invalidDurations = ["foo bar", "", " ", "P", "T1H", "P0.5Y1D", "P0,5Y6M", "P1YT"]; for (const val of validDurations) { const result = duration.safeParse(val); if (!result.success) { throw Error(`Valid duration could not be parsed: ${val}`); } } for (const val of invalidDurations) { const result = duration.safeParse(val); if (result.success) { throw Error(`Invalid duration was successful parsed: ${val}`); } expect(result.error.issues[0].message).toEqual("Invalid duration"); } }); test("IP validation", () => { const ip = z.string().ip(); expect(ip.safeParse("122.122.122.122").success).toBe(true); const ipv4 = z.string().ip({ version: "v4" }); expect(() => ipv4.parse("6097:adfa:6f0b:220d:db08:5021:6191:7990")).toThrow(); const ipv6 = z.string().ip({ version: "v6" }); expect(() => ipv6.parse("254.164.77.1")).toThrow(); const validIPs = [ "1e5e:e6c8:daac:514b:114b:e360:d8c0:682c", "9d4:c956:420f:5788:4339:9b3b:2418:75c3", "474f:4c83::4e40:a47:ff95:0cda", "d329:0:25b4:db47:a9d1:0:4926:0000", "e48:10fb:1499:3e28:e4b6:dea5:4692:912c", "114.71.82.94", "0.0.0.0", "37.85.236.115", "2001:4888:50:ff00:500:d::", "2001:4888:50:ff00:0500:000d:000:0000", "2001:4888:50:ff00:0500:000d:0000:0000", ]; const invalidIPs = [ "d329:1be4:25b4:db47:a9d1:dc71:4926:992c:14af", "d5e7:7214:2b78::3906:85e6:53cc:709:32ba", "8f69::c757:395e:976e::3441", "54cb::473f:d516:0.255.256.22", "54cb::473f:d516:192.168.1", "256.0.4.4", "-1.0.555.4", "0.0.0.0.0", "1.1.1", ]; // no parameters check IPv4 or IPv6 const ipSchema = z.string().ip(); expect(validIPs.every((ip) => ipSchema.safeParse(ip).success)).toBe(true); expect(invalidIPs.every((ip) => ipSchema.safeParse(ip).success === false)).toBe(true); }); test("CIDR validation", () => { const ipv4Cidr = z.string().cidr({ version: "v4" }); expect(() => ipv4Cidr.parse("2001:0db8:85a3::8a2e:0370:7334/64")).toThrow(); const ipv6Cidr = z.string().cidr({ version: "v6" }); expect(() => ipv6Cidr.parse("192.168.0.1/24")).toThrow(); const validCidrs = [ "192.168.0.0/24", "10.0.0.0/8", "203.0.113.0/24", "192.0.2.0/24", "127.0.0.0/8", "172.16.0.0/12", "192.168.1.0/24", "fc00::/7", "fd00::/8", "2001:db8::/32", "2607:f0d0:1002:51::4/64", "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128", "2001:0db8:1234:0000::/64", ]; const invalidCidrs = [ "192.168.1.1/33", "10.0.0.1/-1", "192.168.1.1/24/24", "192.168.1.0/abc", "2001:db8::1/129", "2001:db8::1/-1", "2001:db8::1/64/64", "2001:db8::1/abc", ]; // no parameters check IPv4 or IPv6 const cidrSchema = z.string().cidr(); expect(validCidrs.every((ip) => cidrSchema.safeParse(ip).success)).toBe(true); expect(invalidCidrs.every((ip) => cidrSchema.safeParse(ip).success === false)).toBe(true); });
Save
cmd:
run