string.test.tsโข64.1 kB
import { randomBytes } from "node:crypto";
import { expect, test } from "vitest";
import * as z from "zod/v4";
const minFive = z.string().min(5, "min5");
const maxFive = z.string().max(5, "max5");
const justFive = z.string().length(5);
const nonempty = z.string().min(1, "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("length checks", () => {
minFive.parse("12345");
minFive.parse("123456");
maxFive.parse("12345");
maxFive.parse("1234");
nonempty.parse("1");
justFive.parse("12345");
expect(() => minFive.parse("1234")).toThrow();
expect(() => maxFive.parse("123456")).toThrow();
expect(() => nonempty.parse("")).toThrow();
expect(() => justFive.parse("1234")).toThrow();
expect(() => justFive.parse("123456")).toThrow();
});
test("includes", () => {
includes.parse("XincludesXX");
includesFromIndex2.parse("XXXincludesXX");
expect(() => includes.parse("XincludeXX")).toThrow();
expect(() => includesFromIndex2.parse("XincludesXX")).toThrow();
});
test("startswith/endswith", () => {
startsWith.parse("startsWithX");
endsWith.parse("XendsWith");
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);
});
test("base64 validations", () => {
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 a valid base64
];
for (const str of validBase64Strings) {
expect(str + z.string().base64().safeParse(str).success).toBe(`${str}true`);
}
const invalidBase64Strings = [
"12345", // Not padded correctly, not a multiple of 4 characters
"SGVsbG8gV29ybGQ", // Missing padding
"VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw", // Missing padding
"!UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // Invalid character '!'
"?QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // Invalid character '?'
".MTIzND2Nzg5MC4=", // Invalid character '.'
"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", // Missing padding
];
for (const str of invalidBase64Strings) {
expect(str + z.string().base64().safeParse(str).success).toBe(`${str}false`);
}
});
test("base64url validations", () => {
const base64url = z.string().base64url();
const validBase64URLStrings = [
"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 base64url
"w7_Dv8O-w74K", // รฟรฟรพรพ
"123456",
];
for (const str of validBase64URLStrings) {
expect(str + base64url.safeParse(str).success).toBe(`${str}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 '.'
// disallow valid padding
"SGVsbG8gV29ybGQ=", // "Hello World" with padding
"VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==", // "This is an encoded string" with padding
"TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcms=", // "Many hands make light work" with padding
"QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // "Base64 encoding is fun" with padding
"MTIzNDU2Nzg5MA==", // "1234567890" with padding
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", // "abcdefghijklmnopqrstuvwxyz with padding"
"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", // "ABCDEFGHIJKLMNOPQRSTUVWXYZ" with padding
"ISIkJSMmJyonKCk=", // "!\"#$%&'()*" with padding
];
for (const str of invalidBase64URLStrings) {
expect(str + base64url.safeParse(str).success).toBe(`${str}false`);
}
});
test("big base64 and base64url", () => {
const bigbase64 = randomBytes(1024 * 1024 * 10).toString("base64");
z.base64().parse(bigbase64);
const bigbase64url = randomBytes(1024 * 1024 * 10).toString("base64url");
z.base64url().parse(bigbase64url);
});
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 token", () => {
const jwt = z.string().jwt();
expect(() => jwt.parse("invalid")).toThrow();
expect(() => jwt.parse("invalid.invalid")).toThrow();
expect(() => jwt.parse("invalid.invalid.invalid")).toThrow();
// Valid JWTs
const es256jwt = z.string().jwt({ alg: "ES256" });
const d1 = makeJwt({ typ: "JWT", alg: "ES256" }, {});
jwt.parse(d1);
es256jwt.parse(d1);
// Invalid header
const d2 = makeJwt({}, {});
expect(() => jwt.parse(d2)).toThrow();
// Wrong algorithm
const d3 = makeJwt({ typ: "JWT", alg: "RS256" }, {});
expect(() => es256jwt.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();
});
test("url validations", () => {
const url = z.string().url();
url.parse("http://google.com");
url.parse("https://google.com/asdf?asdf=ljk3lk4&asdf=234#asdf");
url.parse("https://anonymous:flabada@developer.mozilla.org/en-US/docs/Web/API/URL/password");
url.parse("https://localhost");
url.parse("https://my.local");
url.parse("http://aslkfjdalsdfkjaf");
url.parse("http://localhost");
url.parse("c:");
expect(() => url.parse("asdf")).toThrow();
expect(() => url.parse("https:/")).toThrow();
expect(() => url.parse("asdfj@lkjsdf.com")).toThrow();
expect(() => url.parse("https://")).toThrow();
});
test("httpurl", () => {
const httpUrl = z.url({
protocol: /^https?$/,
hostname: z.regexes.domain,
// /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/
});
httpUrl.parse("https://example.com");
httpUrl.parse("http://example.com");
// ports
httpUrl.parse("https://example.com:8080");
httpUrl.parse("http://example.com:8080");
// subdomains
httpUrl.parse("https://sub.example.com");
httpUrl.parse("http://sub.example.com");
// paths
httpUrl.parse("https://example.com/path/to/resource");
httpUrl.parse("http://example.com/path/to/resource");
// query parameters
httpUrl.parse("https://example.com/path?query=param");
httpUrl.parse("http://example.com/path?query=param");
// fragment identifiers
httpUrl.parse("https://example.com/path#fragment");
httpUrl.parse("http://example.com/path#fragment");
// fails
expect(() => httpUrl.parse("ftp://example.com")).toThrow();
expect(() => httpUrl.parse("shttp://example.com")).toThrow();
expect(() => httpUrl.parse("httpz://example.com")).toThrow();
expect(() => httpUrl.parse("http://")).toThrow();
expect(() => httpUrl.parse("http://localhost")).toThrow();
expect(() => httpUrl.parse("http://-asdf.com")).toThrow();
expect(() =>
httpUrl.parse(
"http://asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf.com"
)
).toThrow();
expect(() => httpUrl.parse("http://asdf.c")).toThrow();
expect(() => httpUrl.parse("mailto:asdf@lckj.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("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).toMatchObject({ success: false });
expect(result.error!.issues[0].message).toEqual("custom error");
expect(result.error).toMatchInlineSnapshot(`
[ZodError: [
{
"origin": "string",
"code": "invalid_format",
"format": "nanoid",
"pattern": "/^[a-zA-Z0-9_-]{21}$/",
"path": [],
"message": "custom error"
}
]]
`);
});
test("bad nanoid", () => {
const nanoid = z.string().nanoid("custom error");
nanoid.parse("ySh_984wpDUu7IQRrLXAp");
const result = nanoid.safeParse("invalid nanoid");
expect(result).toMatchObject({ success: false });
expect(result.error!.issues[0].message).toEqual("custom error");
expect(result.error).toMatchInlineSnapshot(`
[ZodError: [
{
"origin": "string",
"code": "invalid_format",
"format": "nanoid",
"pattern": "/^[a-zA-Z0-9_-]{21}$/",
"path": [],
"message": "custom error"
}
]]
`);
});
test("good uuid", () => {
const uuid = z.string().uuid("custom error");
const goodUuids = [
"9491d710-3185-1e06-bea0-6a2f275345e0",
"9491d710-3185-2e06-bea0-6a2f275345e0",
"9491d710-3185-3e06-bea0-6a2f275345e0",
"9491d710-3185-4e06-bea0-6a2f275345e0",
"9491d710-3185-5e06-bea0-6a2f275345e0",
"9491d710-3185-5e06-aea0-6a2f275345e0",
"9491d710-3185-5e06-8ea0-6a2f275345e0",
"9491d710-3185-5e06-9ea0-6a2f275345e0",
"00000000-0000-0000-0000-000000000000",
];
for (const goodUuid of goodUuids) {
const result = uuid.safeParse(goodUuid);
expect(result.success).toEqual(true);
}
});
test(`bad uuid`, () => {
const uuid = z.string().uuid("custom error");
for (const badUuid of [
"9491d710-3185-0e06-bea0-6a2f275345e0",
"9491d710-3185-5e06-0ea0-6a2f275345e0",
"d89e7b01-7598-ed11-9d7a-0022489382fd", // new sequential id
"b3ce60f8-e8b9-40f5-1150-172ede56ff74", // Variant 0 - RFC 4122: Reserved, NCS backward compatibility
"92e76bf9-28b3-4730-cd7f-cb6bc51f8c09", // Variant 2 - RFC 4122: Reserved, Microsoft Corporation backward compatibility
"invalid uuid",
"9491d710-3185-4e06-bea0-6a2f275345e0X",
"ffffffff-ffff-ffff-ffff-ffffffffffff",
]) {
const result = uuid.safeParse(badUuid);
expect(result).toMatchObject({ success: false });
expect(result.error?.issues[0].message).toEqual("custom error");
}
});
test("good guid", () => {
const guid = z.string().guid("custom error");
for (const goodGuid of [
"9491d710-3185-4e06-bea0-6a2f275345e0",
"d89e7b01-7598-ed11-9d7a-0022489382fd", // new sequential id
"b3ce60f8-e8b9-40f5-1150-172ede56ff74", // Variant 0 - RFC 4122: Reserved, NCS backward compatibility
"92e76bf9-28b3-4730-cd7f-cb6bc51f8c09", // Variant 2 - RFC 4122: Reserved, Microsoft Corporation backward compatibility
"00000000-0000-0000-0000-000000000000",
"ffffffff-ffff-ffff-ffff-ffffffffffff",
]) {
const result = guid.safeParse(goodGuid);
expect(result.success).toEqual(true);
}
});
test("bad guid", () => {
const guid = z.string().guid("custom error");
for (const badGuid of ["9491d710-3185-4e06-bea0-6a2f275345e0X"]) {
const result = guid.safeParse(badGuid);
expect(result).toMatchObject({ success: false });
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).toMatchObject({ success: false });
expect(result.error!.issues[0].message).toEqual("Invalid cuid");
expect(result.error).toMatchInlineSnapshot(`
[ZodError: [
{
"origin": "string",
"code": "invalid_format",
"format": "cuid",
"pattern": "/^[cC][^\\\\s-]{8,}$/",
"path": [],
"message": "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).toMatchObject({ success: false });
const tooLong = "01ARZ3NDEKTSV4RRFFQ69G5FAVA";
expect(ulid.safeParse(tooLong)).toMatchObject({ success: false });
const caseInsensitive = ulid.safeParse("01arZ3nDeKTsV4RRffQ69G5FAV");
expect(caseInsensitive.success).toEqual(true);
expect(result.error!.issues[0].message).toEqual("Invalid ULID");
expect(result.error).toMatchInlineSnapshot(`
[ZodError: [
{
"origin": "string",
"code": "invalid_format",
"format": "ulid",
"pattern": "/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/",
"path": [],
"message": "Invalid ULID"
}
]]
`);
});
test("xid", () => {
const xid = z.string().xid();
xid.parse("9m4e2mr0ui3e8a215n4g");
const result = xid.safeParse("invalidxid");
expect(result).toMatchObject({ success: false });
expect(result.error!.issues[0].message).toEqual("Invalid XID");
expect(result.error).toMatchInlineSnapshot(`
[ZodError: [
{
"origin": "string",
"code": "invalid_format",
"format": "xid",
"pattern": "/^[0-9a-vA-V]{20}$/",
"path": [],
"message": "Invalid XID"
}
]]
`);
});
test("ksuid", () => {
const ksuid = z.string().ksuid();
ksuid.parse("0o0t9hkGxgFLtd3lmJ4TSTeY0Vb");
const result = ksuid.safeParse("invalidksuid");
expect(result).toMatchObject({ success: false });
const tooLong = "0o0t9hkGxgFLtd3lmJ4TSTeY0VbA";
expect(ksuid.safeParse(tooLong)).toMatchObject({ success: false });
expect(result.error!.issues).toMatchInlineSnapshot(`
[
{
"code": "invalid_format",
"format": "ksuid",
"message": "Invalid KSUID",
"origin": "string",
"path": [],
"pattern": "/^[A-Za-z0-9]{27}$/",
},
]
`);
});
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");
expect(result.error!.issues).toMatchInlineSnapshot(`
[
{
"code": "invalid_format",
"format": "regex",
"message": "Invalid string: must match pattern /^moo+$/",
"origin": "string",
"path": [],
"pattern": "/^moo+$/",
},
]
`);
expect(() => z.string().uuid().parse("purr")).toThrow();
});
test("regexp error custom message", () => {
const result = z
.string()
.regex(/^moo+$/, { message: "Custom error message" })
.safeParse("boooo");
expect(result.error!.issues).toMatchInlineSnapshot(`
[
{
"code": "invalid_format",
"format": "regex",
"message": "Custom error message",
"origin": "string",
"path": [],
"pattern": "/^moo+$/",
},
]
`);
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("format", () => {
expect(z.string().email().format).toEqual("email");
expect(z.string().url().format).toEqual("url");
expect(z.string().jwt().format).toEqual("jwt");
expect(z.string().emoji().format).toEqual("emoji");
expect(z.string().guid().format).toEqual("guid");
expect(z.string().uuid().format).toEqual("uuid");
expect(z.string().uuidv4().format).toEqual("uuid");
expect(z.string().uuidv6().format).toEqual("uuid");
expect(z.string().uuidv7().format).toEqual("uuid");
expect(z.string().nanoid().format).toEqual("nanoid");
expect(z.string().guid().format).toEqual("guid");
expect(z.string().cuid().format).toEqual("cuid");
expect(z.string().cuid2().format).toEqual("cuid2");
expect(z.string().ulid().format).toEqual("ulid");
expect(z.string().base64().format).toEqual("base64");
// expect(z.string().jsonString().format).toEqual("json_string");
// expect(z.string().json().format).toEqual("json_string");
expect(z.string().xid().format).toEqual("xid");
expect(z.string().ksuid().format).toEqual("ksuid");
// expect(z.string().ip().format).toEqual("ip");
expect(z.string().ipv4().format).toEqual("ipv4");
expect(z.string().ipv6().format).toEqual("ipv6");
expect(z.string().e164().format).toEqual("e164");
expect(z.string().datetime().format).toEqual("datetime");
expect(z.string().date().format).toEqual("date");
expect(z.string().time().format).toEqual("time");
expect(z.string().duration().format).toEqual("duration");
});
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().max(5).max(10).maxLength).toEqual(5);
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("IP validation", () => {
// const ipSchema = z.string().ip();
// // General IP validation (accepts both v4 and v6)
// expect(ipSchema.safeParse("114.71.82.94").success).toBe(true);
// expect(ipSchema.safeParse("0.0.0.0").success).toBe(true);
// expect(ipSchema.safeParse("37.85.236.115").success).toBe(true);
// expect(ipSchema.safeParse("1e5e:e6c8:daac:514b:114b:e360:d8c0:682c").success).toBe(true);
// expect(ipSchema.safeParse("9d4:c956:420f:5788:4339:9b3b:2418:75c3").success).toBe(true);
// expect(ipSchema.safeParse("a6ea::2454:a5ce:94.105.123.75").success).toBe(true);
// expect(ipSchema.safeParse("474f:4c83::4e40:a47:ff95:0cda").success).toBe(true);
// expect(ipSchema.safeParse("d329:0:25b4:db47:a9d1:0:4926:0000").success).toBe(true);
// expect(ipSchema.safeParse("e48:10fb:1499:3e28:e4b6:dea5:4692:912c").success).toBe(true);
// expect(ipSchema.safeParse("d329:1be4:25b4:db47:a9d1:dc71:4926:992c:14af").success).toBe(false);
// expect(ipSchema.safeParse("d5e7:7214:2b78::3906:85e6:53cc:709:32ba").success).toBe(false);
// expect(ipSchema.safeParse("8f69::c757:395e:976e::3441").success).toBe(false);
// expect(ipSchema.safeParse("54cb::473f:d516:0.255.256.22").success).toBe(false);
// expect(ipSchema.safeParse("54cb::473f:d516:192.168.1").success).toBe(false);
// expect(ipSchema.safeParse("256.0.4.4").success).toBe(false);
// expect(ipSchema.safeParse("-1.0.555.4").success).toBe(false);
// expect(ipSchema.safeParse("0.0.0.0.0").success).toBe(false);
// expect(ipSchema.safeParse("1.1.1").success).toBe(false);
// });
test("IPv4 validation", () => {
const ipv4 = z.string().ipv4();
// Valid IPv4 addresses
expect(ipv4.safeParse("114.71.82.94").success).toBe(true);
expect(ipv4.safeParse("0.0.0.0").success).toBe(true);
expect(ipv4.safeParse("37.85.236.115").success).toBe(true);
expect(ipv4.safeParse("192.168.0.1").success).toBe(true);
expect(ipv4.safeParse("255.255.255.255").success).toBe(true);
expect(ipv4.safeParse("1.2.3.4").success).toBe(true);
// Invalid IPv4 addresses
expect(ipv4.safeParse("256.0.4.4").success).toBe(false);
expect(ipv4.safeParse("-1.0.555.4").success).toBe(false);
expect(ipv4.safeParse("0.0.0.0.0").success).toBe(false);
expect(ipv4.safeParse("1.1.1").success).toBe(false);
expect(ipv4.safeParse("1e5e:e6c8:daac:514b:114b:e360:d8c0:682c").success).toBe(false);
expect(ipv4.safeParse("a6ea::2454:a5ce:94.105.123.75").success).toBe(false);
expect(ipv4.safeParse("not an ip").success).toBe(false);
expect(ipv4.safeParse("1.2.3").success).toBe(false);
expect(ipv4.safeParse("1.2.3.4.5").success).toBe(false);
expect(ipv4.safeParse("1.2.3.256").success).toBe(false);
// Test specific error
expect(() => ipv4.parse("6097:adfa:6f0b:220d:db08:5021:6191:7990")).toThrow();
});
test("IPv6 validation", () => {
const ipv6 = z.string().ipv6();
// Valid IPv6 addresses
expect(ipv6.safeParse("1e5e:e6c8:daac:514b:114b:e360:d8c0:682c").success).toBe(true);
expect(ipv6.safeParse("9d4:c956:420f:5788:4339:9b3b:2418:75c3").success).toBe(true);
expect(ipv6.safeParse("a6ea::2454:a5ce:94.105.123.75").success).toBe(true);
expect(ipv6.safeParse("474f:4c83::4e40:a47:ff95:0cda").success).toBe(true);
expect(ipv6.safeParse("d329:0:25b4:db47:a9d1:0:4926:0000").success).toBe(true);
expect(ipv6.safeParse("e48:10fb:1499:3e28:e4b6:dea5:4692:912c").success).toBe(true);
expect(ipv6.safeParse("::1").success).toBe(true);
expect(ipv6.safeParse("2001:db8::").success).toBe(true);
expect(ipv6.safeParse("2001:0db8:85a3:0000:0000:8a2e:0370:7334").success).toBe(true);
expect(ipv6.safeParse("2001:db8::192.168.0.1").success).toBe(true);
expect(ipv6.safeParse("::ffff:192.168.0.1").success).toBe(true);
expect(ipv6.safeParse("::ffff:c000:0280").success).toBe(true); // IPv4-mapped IPv6 address
expect(ipv6.safeParse("64:ff9b::192.168.0.1").success).toBe(true); // IPv4/IPv6 translation
// Invalid IPv6 addresses
expect(ipv6.safeParse("d329:1be4:25b4:db47:a9d1:dc71:4926:992c:14af").success).toBe(false);
expect(ipv6.safeParse("d5e7:7214:2b78::3906:85e6:53cc:709:32ba").success).toBe(false);
expect(ipv6.safeParse("8f69::c757:395e:976e::3441").success).toBe(false);
expect(ipv6.safeParse("54cb::473f:d516:0.255.256.22").success).toBe(false);
expect(ipv6.safeParse("54cb::473f:d516:192.168.1").success).toBe(false);
expect(ipv6.safeParse("114.71.82.94").success).toBe(false);
expect(ipv6.safeParse("not an ip").success).toBe(false);
expect(ipv6.safeParse("g123::1234:5678").success).toBe(false);
// Test specific error
expect(() => ipv6.parse("254.164.77.1")).toThrow();
});
test("CIDR v4 validation", () => {
const cidrV4 = z.string().cidrv4();
// Valid CIDR v4 addresses
expect(cidrV4.safeParse("192.168.0.0/24").success).toBe(true);
expect(cidrV4.safeParse("10.0.0.0/8").success).toBe(true);
expect(cidrV4.safeParse("172.16.0.0/12").success).toBe(true);
expect(cidrV4.safeParse("0.0.0.0/0").success).toBe(true);
expect(cidrV4.safeParse("255.255.255.255/32").success).toBe(true);
// Invalid CIDR v4 addresses
expect(cidrV4.safeParse("192.168.0.0").success).toBe(false); // Missing prefix
expect(cidrV4.safeParse("192.168.0.0/33").success).toBe(false); // Invalid prefix length
expect(cidrV4.safeParse("256.0.0.0/24").success).toBe(false); // Invalid IP
expect(cidrV4.safeParse("192.168.0.0/-1").success).toBe(false); // Negative prefix length
expect(cidrV4.safeParse("not a cidr").success).toBe(false); // Invalid format
});
test("CIDR v6 validation", () => {
const cidrV6 = z.string().cidrv6();
// Valid CIDR v6 addresses
expect(cidrV6.safeParse("2001:db8::/32").success).toBe(true);
expect(cidrV6.safeParse("::/0").success).toBe(true);
expect(cidrV6.safeParse("fe80::/10").success).toBe(true);
expect(cidrV6.safeParse("::1/128").success).toBe(true);
expect(cidrV6.safeParse("2001:0db8:85a3::/64").success).toBe(true);
// Invalid CIDR v6 addresses
expect(cidrV6.safeParse("2001:db8::").success).toBe(false); // Missing prefix
expect(cidrV6.safeParse("2001:db8::/129").success).toBe(false); // Invalid prefix length
expect(cidrV6.safeParse("2001:db8::/abc").success).toBe(false); // Invalid prefix format
expect(cidrV6.safeParse("not a cidr").success).toBe(false); // Invalid format
expect(cidrV6.safeParse("192.168.0.0/24").success).toBe(false); // IPv4 CIDR in v6 validation
});
test("E.164 validation", () => {
const e164Number = z.string().e164();
expect(e164Number.safeParse("+1555555").success).toBe(true);
const validE164Numbers = [
"+1555555", // min-length (7 digits + '+')
"+15555555",
"+155555555",
"+1555555555",
"+15555555555",
"+155555555555",
"+1555555555555",
"+15555555555555",
"+155555555555555",
"+105555555555555",
"+100555555555555", // max-length (15 digits + '+')
];
const invalidE164Numbers = [
"", // empty
"+", // only plus sign
"-", // wrong sign
" 555555555", // starts with space
"555555555", // missing plus sign
"+1 555 555 555", // space after plus sign
"+1555 555 555", // space between numbers
"+1555+555", // multiple plus signs
"+1555555555555555", // too long
"+115abc55", // non numeric characters in number part
"+1555555 ", // space after number
];
expect(validE164Numbers.every((number) => e164Number.safeParse(number).success)).toBe(true);
expect(invalidE164Numbers.every((number) => e164Number.safeParse(number).success === false)).toBe(true);
});