string.test.tsโข65.6 kB
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);
});