From ec33647814406d3e013a3d230e87632743525e00 Mon Sep 17 00:00:00 2001 From: Pat Date: Wed, 20 Mar 2024 21:33:56 +0100 Subject: [PATCH 01/15] add my folder with git ignoring rules #15 --- staff/angel-patino/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 staff/angel-patino/.gitignore diff --git a/staff/angel-patino/.gitignore b/staff/angel-patino/.gitignore new file mode 100644 index 000000000..e43b0f988 --- /dev/null +++ b/staff/angel-patino/.gitignore @@ -0,0 +1 @@ +.DS_Store From 5b925bcea182b06a20f5e5c78d3641142b0ec266 Mon Sep 17 00:00:00 2001 From: Pat Date: Thu, 21 Mar 2024 20:47:24 +0100 Subject: [PATCH 02/15] add my folder with thumbs.db #15 --- staff/angel-patino/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/staff/angel-patino/.gitignore b/staff/angel-patino/.gitignore index e43b0f988..47efe4857 100644 --- a/staff/angel-patino/.gitignore +++ b/staff/angel-patino/.gitignore @@ -1 +1,2 @@ .DS_Store +thumbs.db From c851739ac7541a783360d2bd3eaa2b4e7102599d Mon Sep 17 00:00:00 2001 From: Pat Date: Wed, 1 May 2024 17:18:40 +0200 Subject: [PATCH 03/15] add initial code for social code app #124 --- .../socialcode/app/components/Component.js | 59 +++++++++++++ .../socialcode/app/components/core/Button.css | 7 ++ .../socialcode/app/components/core/Button.js | 11 +++ .../socialcode/app/components/core/Field.css | 5 ++ .../socialcode/app/components/core/Field.js | 29 +++++++ .../socialcode/app/components/core/Form.css | 5 ++ .../socialcode/app/components/core/Form.js | 15 ++++ .../socialcode/app/components/core/Heading.js | 7 ++ .../socialcode/app/components/core/Input.css | 6 ++ .../socialcode/app/components/core/Input.js | 20 +++++ .../socialcode/app/components/core/Label.js | 9 ++ .../socialcode/app/components/core/Link.js | 15 ++++ .../app/components/core/SubmitButton.css | 3 + .../app/components/core/SubmitForm.js | 10 +++ .../components/library/FormWithFeedback.css | 12 +++ .../components/library/FormWithFeedback.js | 30 +++++++ staff/angel-patino/socialcode/app/data.js | 27 ++++++ staff/angel-patino/socialcode/app/errors.js | 25 ++++++ staff/angel-patino/socialcode/app/global.css | 15 ++++ .../socialcode/app/home/index.css | 12 +++ .../socialcode/app/home/index.html | 26 ++++++ .../angel-patino/socialcode/app/home/index.js | 37 ++++++++ staff/angel-patino/socialcode/app/logic.js | 87 +++++++++++++++++++ .../socialcode/app/login/LoginForm.js | 33 +++++++ .../socialcode/app/login/index.css | 5 ++ .../socialcode/app/login/index.html | 43 +++++++++ .../socialcode/app/login/index.js | 50 +++++++++++ .../socialcode/app/register/RegisterForm.js | 77 ++++++++++++++++ .../socialcode/app/register/index.css | 5 ++ .../socialcode/app/register/index.html | 42 +++++++++ .../socialcode/app/register/index.js | 59 +++++++++++++ 31 files changed, 786 insertions(+) create mode 100644 staff/angel-patino/socialcode/app/components/Component.js create mode 100644 staff/angel-patino/socialcode/app/components/core/Button.css create mode 100644 staff/angel-patino/socialcode/app/components/core/Button.js create mode 100644 staff/angel-patino/socialcode/app/components/core/Field.css create mode 100644 staff/angel-patino/socialcode/app/components/core/Field.js create mode 100644 staff/angel-patino/socialcode/app/components/core/Form.css create mode 100644 staff/angel-patino/socialcode/app/components/core/Form.js create mode 100644 staff/angel-patino/socialcode/app/components/core/Heading.js create mode 100644 staff/angel-patino/socialcode/app/components/core/Input.css create mode 100644 staff/angel-patino/socialcode/app/components/core/Input.js create mode 100644 staff/angel-patino/socialcode/app/components/core/Label.js create mode 100644 staff/angel-patino/socialcode/app/components/core/Link.js create mode 100644 staff/angel-patino/socialcode/app/components/core/SubmitButton.css create mode 100644 staff/angel-patino/socialcode/app/components/core/SubmitForm.js create mode 100644 staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css create mode 100644 staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js create mode 100644 staff/angel-patino/socialcode/app/data.js create mode 100644 staff/angel-patino/socialcode/app/errors.js create mode 100644 staff/angel-patino/socialcode/app/global.css create mode 100644 staff/angel-patino/socialcode/app/home/index.css create mode 100644 staff/angel-patino/socialcode/app/home/index.html create mode 100644 staff/angel-patino/socialcode/app/home/index.js create mode 100644 staff/angel-patino/socialcode/app/logic.js create mode 100644 staff/angel-patino/socialcode/app/login/LoginForm.js create mode 100644 staff/angel-patino/socialcode/app/login/index.css create mode 100644 staff/angel-patino/socialcode/app/login/index.html create mode 100644 staff/angel-patino/socialcode/app/login/index.js create mode 100644 staff/angel-patino/socialcode/app/register/RegisterForm.js create mode 100644 staff/angel-patino/socialcode/app/register/index.css create mode 100644 staff/angel-patino/socialcode/app/register/index.html create mode 100644 staff/angel-patino/socialcode/app/register/index.js diff --git a/staff/angel-patino/socialcode/app/components/Component.js b/staff/angel-patino/socialcode/app/components/Component.js new file mode 100644 index 000000000..bfa269ea2 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/Component.js @@ -0,0 +1,59 @@ +class Component { + constructor(tagNameOrContainer = 'div'){ + if (typeof tagNameOrContainer === 'string') + this.container = document.createElement(tagNameOrContainer) + else if (tagNameOrContainer instanceof HTMLElement || tagNameOrContainer instanceof HTMLDocument) + this.container = tagNameOrContainer + else + throw new Error('tagNameOrContainer is not a tagName or container') + + this.children = [] +} + + add(child) { + if (!(child instanceof Component)) throw new TypeError('child is not component') + + this.children.push(child) + + this.container.appendChild(child.container) + } + + remove(child) { + if (!(child instanceof Component)) throw new TypeError('child is not component') + + const index = this.children.indexOf(child) + + if(index > -1) + this.children.splice(index, 1) + + this.container.removeChild(child.container) + } + + setText(text) { + this.container.innerText = text + } + + setId(id) { + this.container.id = id + } + + addClass(clazz) { + this.container.classList.add(clazz) + } + + removeClass(clazz) { + this.container.classList.remove(clazz) + } + + onClick(listener) { + this.container.addEventListener('click', listener) + } + + onKeyDown(listener) { + this.container.addEventListener('keydown', listener) + } + + onKeyUp(listener) { + this.container.addEventListener('keyup', listener) + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Button.css b/staff/angel-patino/socialcode/app/components/core/Button.css new file mode 100644 index 000000000..3cbda9cba --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Button.css @@ -0,0 +1,7 @@ +.Button { + padding: .4rem; + background-color: transparent; + font-size: 1rem; + border: 1px solid var(--first-color); + +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Button.js b/staff/angel-patino/socialcode/app/components/core/Button.js new file mode 100644 index 000000000..be3d3b9a7 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Button.js @@ -0,0 +1,11 @@ +class Button extends Component { + constructor() { + super('button') + + this.addClass('Button') + } + + setType(type) { + this.container.type = type + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Field.css b/staff/angel-patino/socialcode/app/components/core/Field.css new file mode 100644 index 000000000..708f5849b --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Field.css @@ -0,0 +1,5 @@ +.Field { + display: flex; + flex-direction: column; + margin: .25rem 0; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Field.js b/staff/angel-patino/socialcode/app/components/core/Field.js new file mode 100644 index 000000000..84e9a8c7f --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Field.js @@ -0,0 +1,29 @@ +class Field extends Component { + constructor(id, type, text) { + super('div') + + this.addClass('Field') + + const label = new Label + label.setText(text) + label.setFor(id) + + const input = new Input + input.setId(id) + input.setType(type) + + this.add(label) + this.add(input) + } + + + setPlaceholder(placeholder) { + this.children[1].setPlaceholder(placeholder) + } + + getValue() { + const input = this.children[1] + + return input.getValue() + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Form.css b/staff/angel-patino/socialcode/app/components/core/Form.css new file mode 100644 index 000000000..379cad316 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Form.css @@ -0,0 +1,5 @@ +.Form { + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Form.js b/staff/angel-patino/socialcode/app/components/core/Form.js new file mode 100644 index 000000000..3aac6de76 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Form.js @@ -0,0 +1,15 @@ +class Form extends Component { + constructor() { + super('form') + + this.addClass('Form') + } + + onSubmit(listener) { + this.container.addEventListener('submit', listener) + } + + clear () { + this.container.reset() + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Heading.js b/staff/angel-patino/socialcode/app/components/core/Heading.js new file mode 100644 index 000000000..39d0cdf18 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Heading.js @@ -0,0 +1,7 @@ +class Heading extends Component{ + constructor(level) { + super('h' + level) + + } +} + diff --git a/staff/angel-patino/socialcode/app/components/core/Input.css b/staff/angel-patino/socialcode/app/components/core/Input.css new file mode 100644 index 000000000..6c09b66f6 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Input.css @@ -0,0 +1,6 @@ +.Input { + padding: .4rem; + background-color: transparent; + font-size: 1rem; + border: 1px solid var(--first-color); +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Input.js b/staff/angel-patino/socialcode/app/components/core/Input.js new file mode 100644 index 000000000..d892aea22 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Input.js @@ -0,0 +1,20 @@ +class Input extends Component{ + constructor() { + super('input') + + this.addClass('Input') +} + + + setType(type) { + this.container.type = type + } + + setPlaceholder(placeholder) { + this.container.placeholder = placeholder + } + + getValue() { + return this.container.value + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Label.js b/staff/angel-patino/socialcode/app/components/core/Label.js new file mode 100644 index 000000000..f5ef5a8ec --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Label.js @@ -0,0 +1,9 @@ +class Label extends Component { + constructor(){ + super('label') + } + + setFor(id) { + this.container.htmlFor = id + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Link.js b/staff/angel-patino/socialcode/app/components/core/Link.js new file mode 100644 index 000000000..4f02d5658 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Link.js @@ -0,0 +1,15 @@ +class Link extends Component { + constructor(){ + super('a') + + this.setUrl('') + } + + setUrl(url) { + this.container.href = url + } + + setTarget(target) { + this.container.target = target + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/SubmitButton.css b/staff/angel-patino/socialcode/app/components/core/SubmitButton.css new file mode 100644 index 000000000..4452ec3b8 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/SubmitButton.css @@ -0,0 +1,3 @@ +.SubmitButton { + margin: .5rem 0; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/SubmitForm.js b/staff/angel-patino/socialcode/app/components/core/SubmitForm.js new file mode 100644 index 000000000..07644c152 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/SubmitForm.js @@ -0,0 +1,10 @@ +class SubmitButton extends Button { + constructor(text) { + super() + + this.addClass('SubmitButton') + + this.setType('submit') + this.setText(text) + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css new file mode 100644 index 000000000..13d8dcc6e --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css @@ -0,0 +1,12 @@ +.LoginForm { + padding: 1rem; +} + + +.LoginForm .Feedback { + color: tomato; +} + +.LoginForm .Feedback.success { + color: greenyellow; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js new file mode 100644 index 000000000..03d2281d2 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js @@ -0,0 +1,30 @@ +class FormWithFeedback extends Form { + constructor() { + super() + + this.addClass('FormWithFeedback') + + const feedbackPanel = new Component('p') + feedbackPanel.addClass('Feedback') + + this.feedbackPanel = feedbackPanel + } + + setFeedbakc(message, level) { + if(level === 'success') + this.feedbackPanel.addClass('success') + + this.feedbackPanel.setText(message) + + this.add(this.feedbackPanel) + } + clear () { + super.clear() + + this.feedbackPanel.setText('') + this.feedbackPanel.removeClass('success') + + this.remove(this.feedbackPanel) + + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/data.js b/staff/angel-patino/socialcode/app/data.js new file mode 100644 index 000000000..c12c651d0 --- /dev/null +++ b/staff/angel-patino/socialcode/app/data.js @@ -0,0 +1,27 @@ +const data = {} + +data.findUser = callback => { + const usersJson = localStorage.users + + if (!usersJson) usersJson = '[]' + +const users = JSON.parse(usersJson) +const user = users.find(callback) + +return user + +} + +data.insertUser = user => { + const usersJson = localStorage.users + + if (!usersJson) usersJson = '[]' + + const users = JSON.parse(usersJson) + + users.push(user) + + usersJson = JSON.stringify(users) + + localStorage.users = usersJson +} diff --git a/staff/angel-patino/socialcode/app/errors.js b/staff/angel-patino/socialcode/app/errors.js new file mode 100644 index 000000000..496b3ea8c --- /dev/null +++ b/staff/angel-patino/socialcode/app/errors.js @@ -0,0 +1,25 @@ +class ContentError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + + +class MatchError extends Error { + constructor(message){ + super(message) + this.name = this.constructor.name + } +} + + + +class DuplicityError extends Error { + constructor(message){ + super(message) + this.name = this.constructor.name + } +} + diff --git a/staff/angel-patino/socialcode/app/global.css b/staff/angel-patino/socialcode/app/global.css new file mode 100644 index 000000000..b9c379744 --- /dev/null +++ b/staff/angel-patino/socialcode/app/global.css @@ -0,0 +1,15 @@ +@import url('https://fonts.googleapis.com/css2?family=Workbench&display=swap'); + +:root { + --first-color: yellowgreen; + --second-color: black; +} + +* { + font-family: Workbench; + color: var(--first-color); +} + +body { + background-color: var(--second-color); +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/index.css b/staff/angel-patino/socialcode/app/home/index.css new file mode 100644 index 000000000..102162302 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/index.css @@ -0,0 +1,12 @@ +.View { + display: flex; + flex-direction: column; + align-items: center; +} + +.Header { + display: flex; + gap: 1rem; + width: 100%; + justify-content: right; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/index.html b/staff/angel-patino/socialcode/app/home/index.html new file mode 100644 index 000000000..0e67cf458 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/index.html @@ -0,0 +1,26 @@ + + + + + + + Home + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/index.js b/staff/angel-patino/socialcode/app/home/index.js new file mode 100644 index 000000000..b909973d2 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/index.js @@ -0,0 +1,37 @@ +if (!logic.isUserLoggedIn()) + location.href = '../login' + + const view = new Component(document.body) + view.addClass('View') + + const header = new Component('header') + header.addClass('Header') + + const userName = logic.getUserName() + + const usernameTitle = new Heading(3) + usernameTitle.setText(username) + + header.add(usernameTitle) + + const logoutButton = new Button + logoutButton.setText('Logout') + + logoutButton.onClick(() => { + logic.logoutUser() + + location.href='../login' + }) + + header.add(logoutButton) + + const footer = new Component('footer') + footer.addClass('Footer') + + const addPostButton = new Button + addPostButton.setText('') + + footer.add(addPostButton) + + view.add(header) + view.add(footer) \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/logic.js b/staff/angel-patino/socialcode/app/logic.js new file mode 100644 index 000000000..71b1e7686 --- /dev/null +++ b/staff/angel-patino/socialcode/app/logic.js @@ -0,0 +1,87 @@ +var logic = {} + +var EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +var USERNAME_REGEX = /^[a-zA-Z0-9-_]+$/ +var PASSWORD_REGEX = /^[a-zA-Z0-9-_$%&=\[\]\{\}\<\>\(\)]{8,}$/ + +var NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ + +logic.registerUser = function (name, surname, email, username, password, passwordRepeat) { + if (!NAME_REGEX.test(name)) + throw new ContentError('name is not valid') + + if (!NAME_REGEX.test(surname)) + throw new ContentError('surname is not valid') + + if (!EMAIL_REGEX.test(email)) + throw new ContentError('email is not valid') + + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!PASSWORD_REGEX.test(password)) + throw new ContentError('password is not valid') + + if (password !== passwordRepeat) + throw new MatchError('passwords don\'t match') + + var user = data.findUser(function (user) { + return user.email === email || user.username === username + }) + + if (user) + throw new DuplicityError('user already exists') + + user = { + name: name, + surname: surname, + email: email, + username: username, + password: password + } + + data.insertUser(user) +} + +logic.loginUser = function (username, password) { + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!PASSWORD_REGEX.test(password)) + throw new ContentError('password is not valid') + + var user = data.findUser(function (user) { + return user.username === username + }) + + if (!user) + throw new MatchError('user not found') + + if (user.password !== password) + throw new MatchError('wrong password') + + sessionStorage.username = username +} + +logic.isUserLoggedIn = function () { + // if (sessionStorage.username) + // return true + + // return false + + // return sessionStorage.username ? true : false + + return !!sessionStorage.username +} + +logic.logoutUser = function () { + delete sessionStorage.username +} + +logic.getUserName = function () { + var user = data.findUser(function (user) { + return user.username === sessionStorage.username + }) + + return user.name +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/LoginForm.js b/staff/angel-patino/socialcode/app/login/LoginForm.js new file mode 100644 index 000000000..2db8e39b3 --- /dev/null +++ b/staff/angel-patino/socialcode/app/login/LoginForm.js @@ -0,0 +1,33 @@ +class LoginForm extends FormWithFeedback { + constructor(){ + super() + + this.addClass('LoginForm') + + const usernameField = new Field('username', 'text', 'Username') + + const passwordField = new Field('password', 'password', 'Password') + + const submitButton = new SubmitButton('Login') + + + this.add(usernameField) + this.add(passwordField) + this.add(submitButton) + + } + + getUsername() { + const usernameField = this.children[0] + + return usernameField.getValue() + } + + getPassword() { + const passwordField = this.children[1] + + return passwordField.getValue() + } + + +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/index.css b/staff/angel-patino/socialcode/app/login/index.css new file mode 100644 index 000000000..968ff685b --- /dev/null +++ b/staff/angel-patino/socialcode/app/login/index.css @@ -0,0 +1,5 @@ +.View { + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/index.html b/staff/angel-patino/socialcode/app/login/index.html new file mode 100644 index 000000000..f3878b11b --- /dev/null +++ b/staff/angel-patino/socialcode/app/login/index.html @@ -0,0 +1,43 @@ + + + + + + + Login + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/staff/angel-patino/socialcode/app/login/index.js b/staff/angel-patino/socialcode/app/login/index.js new file mode 100644 index 000000000..6bb1db2c8 --- /dev/null +++ b/staff/angel-patino/socialcode/app/login/index.js @@ -0,0 +1,50 @@ +if(logic.isUserLoggedIn()) + location.href = '../home' + +const view = new Component(document.body) +view.addClass('View') + +const title = new Heading(1) +title.setText('Login') + +const loginForm = new LoginForm +loginForm.onSubmit(event => { + event.preventDefault() + + const username = loginForm.getUsername() + const password = loginForm.getPassword() + + + try { + logic.loginUser(username, password) + + loginForm.clear() + + loginForm.setFeedback('user successfully logged in', 'success') + + setTimeout(() => + location.href = '../home', 1500) + } catch (error) { + if (error instanceof ContentError) + //alert(error.message) + loginForm.setFeedback(error.message + ', please, correct it') + else if (error instanceof MatchError) + loginForm.setFeedback('wrong credentials') + else + loginForm.setFeedback('sorry, there was an error, please try again later') + + } +}) + +const registerLink = new Link +registerLink.setText('Register') +//registerLink.setUrl('../register') +registerLink.onClick(event => { + event.preventDefault() + + setTimeout(() => location.href = '../register', 500) +}) + +view.add(title) +view.add(loginForm) +view.add(registerLink) \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/register/RegisterForm.js b/staff/angel-patino/socialcode/app/register/RegisterForm.js new file mode 100644 index 000000000..6011ed677 --- /dev/null +++ b/staff/angel-patino/socialcode/app/register/RegisterForm.js @@ -0,0 +1,77 @@ +class RegisterForm extends FormWithFeedback { + constructor(){ + super() + + + this.addClass('RegisterForm') + + const nameField = new Field('name', 'text', 'Name') + nameField.setPlaceholder('name') + + const surnameField = new Field('surname', 'text', 'Surname') + surnameField.setPlaceholder('surname') + + const emailField = new Field('email', 'email', 'E-mail') + emailField.setPlaceholder('name@example.com') + + const usernameField = new Field('username', 'text', 'Username') + usernameField.setPlaceholder('username') + + const passwordField = new Field('password', 'password', 'Password') + passwordField.setPlaceholder('password') + + const passwordRepeatField = new Field('password', 'password', 'Password repeat') + passwordRepeatField.setPlaceholder('repeat password') + + const submitButton = new SubmitButton('Register') + + + this.add(nameField) + this.add(surnameField) + this.add(emailField) + this.add(usernameField) + this.add(passwordField) + this.add(passwordRepeatField) + this.add(submitButton) + +} + + + +getName() { + const nameField = this.children[0] + + return nameField.getValue() +} + +getSurname() { + const surnameField = this.children[1] + + return surnameField.getValue() +} + +getEmail() { + const emailField = this.children[2] + + return emailField.getValue() +} + +getUsername() { + const usernameField = this.children[3] + + return usernameField.getValue() +} + +getPassword() { + const passwordField = this.children[4] + + return passwordField.getValue() +} + +getPasswordRepeat() { + const passwordFieldRepeat = this.children[5] + + return passwordFieldRepeat.getValue() +} + +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/register/index.css b/staff/angel-patino/socialcode/app/register/index.css new file mode 100644 index 000000000..968ff685b --- /dev/null +++ b/staff/angel-patino/socialcode/app/register/index.css @@ -0,0 +1,5 @@ +.View { + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/register/index.html b/staff/angel-patino/socialcode/app/register/index.html new file mode 100644 index 000000000..bc7f023b7 --- /dev/null +++ b/staff/angel-patino/socialcode/app/register/index.html @@ -0,0 +1,42 @@ + + + + + + + Register + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/register/index.js b/staff/angel-patino/socialcode/app/register/index.js new file mode 100644 index 000000000..bc7ac9fdc --- /dev/null +++ b/staff/angel-patino/socialcode/app/register/index.js @@ -0,0 +1,59 @@ +//if(sessionStorage.username) +if(logic.isUserLoggedIn()) + location.href = '../home' + +const view = new Component(document.body) +view.addClass('View') + +const title = new Heading(1) +title.setText('Register') +title.onClick(() => alert('By clicking on this title you wont get anything .P')) + + +const registerForm = new RegisterForm +registerForm.onSubmit(event => { + event.preventDefault() + + + const name = registerForm.getName() + const surname = registerForm.getSurname() + const email = registerForm.getEmail() + const username = registerForm.getUsername() + const password = registerForm.getPassword() + const passwordRepeat = registerForm.getPasswordRepeat() + + try { + logic.registerUser(name, surname, email, username, password, passwordRepeat) + + registerForm.clear() + + registerForm.setFeedback('user successfully registered', 'success') + + setTimeout(() => location.href = '../login', 2000) + + } catch (error) { + if (error instanceof ContentError) + //alert(error.message) + registerForm.setFeedback(error.message + ', please, correct it') + if (error instanceof MatchError) + registerForm.setFeedback(error.message + ',please, retype them') + else if ( error instanceof DuplicityError) + registerForm.setFeedback(error.message + ', please, enter new one') + else + registerForm.setFeedback('sorry, there was an error, please try again later') + + } +}) + +const loginLink = new Link +loginLink.setText('Login') +//loginLink.setUrl('../login') +loginLink.onClick(event => { + event.preventDefault() + + setTimeout(() => location.href = '../login', 500) +}) + +view.add(title) +view.add(registerForm) +view.add(loginLink) \ No newline at end of file From 7dbcb5f1cf640feccb8e2046b89f1197ab30ae31 Mon Sep 17 00:00:00 2001 From: Pat Date: Sat, 4 May 2024 17:07:01 +0200 Subject: [PATCH 04/15] add create post logic and view #124 --- staff/.DS_Store | Bin 0 -> 6148 bytes .../socialcode/app/components/Component.js | 43 ++++---- .../socialcode/app/components/core/Button.js | 2 +- .../socialcode/app/components/core/Field.js | 26 ++--- .../socialcode/app/components/core/Form.js | 6 +- .../socialcode/app/components/core/Image.css | 3 + .../socialcode/app/components/core/Image.js | 15 +++ .../socialcode/app/components/core/Input.js | 10 +- .../socialcode/app/components/core/Label.js | 2 +- .../app/components/core/SubmitForm.js | 8 +- .../components/library/FormWithFeedback.css | 6 +- .../components/library/FormWithFeedback.js | 11 +- staff/angel-patino/socialcode/app/data.js | 36 ++++++- staff/angel-patino/socialcode/app/errors.js | 15 ++- staff/angel-patino/socialcode/app/global.css | 1 + .../app/home/components/CreatePostForm.js | 44 ++++++++ .../socialcode/app/home/components/Post.js | 26 +++++ .../socialcode/app/home/index.css | 18 ++++ .../socialcode/app/home/index.html | 26 ++++- .../angel-patino/socialcode/app/home/index.js | 83 +++++++++++---- staff/angel-patino/socialcode/app/logic.js | 57 +++++----- .../socialcode/app/login/LoginForm.js | 30 +++--- .../socialcode/app/login/index.html | 11 +- .../socialcode/app/login/index.js | 10 +- .../socialcode/app/register/RegisterForm.js | 100 ++++++++---------- .../socialcode/app/register/index.html | 6 +- .../socialcode/app/register/index.js | 23 ++-- 27 files changed, 400 insertions(+), 218 deletions(-) create mode 100644 staff/.DS_Store create mode 100644 staff/angel-patino/socialcode/app/components/core/Image.css create mode 100644 staff/angel-patino/socialcode/app/components/core/Image.js create mode 100644 staff/angel-patino/socialcode/app/home/components/CreatePostForm.js create mode 100644 staff/angel-patino/socialcode/app/home/components/Post.js diff --git a/staff/.DS_Store b/staff/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..515387198f5ae1ae624e5644976b511205e8a698 GIT binary patch literal 6148 zcmeHK!Ab)$5S`SjTdL55g2#Z@imeq&-z1tiQnT) zl8RD$RIJRv(QuZKY% z=7X#q_Alwznbv7E>4(vI(j9hcYe!n;VWPTylaRz+q+DGjD%Qic9;i4oxq%(zmt$r=5rNQR!zZs=)8h*s8+`ykKYM`r_<1 zjqrVmJd^Yk^i`_W9dag<5TA_|UASDimR6 z$LC8!2jN@fmK9(HzA8YqA6kv>|C8DEzaeqM3a|qIQvp#v4vyQnHg~s9t&;9qiGG7l pM!dq}M+$b#Rg7G^iZ{@WVO&xN(RWx_#0VPy5il}v!wUSW0`HgxR^R{t literal 0 HcmV?d00001 diff --git a/staff/angel-patino/socialcode/app/components/Component.js b/staff/angel-patino/socialcode/app/components/Component.js index bfa269ea2..3da1c66e3 100644 --- a/staff/angel-patino/socialcode/app/components/Component.js +++ b/staff/angel-patino/socialcode/app/components/Component.js @@ -1,21 +1,21 @@ class Component { - constructor(tagNameOrContainer = 'div'){ - if (typeof tagNameOrContainer === 'string') - this.container = document.createElement(tagNameOrContainer) - else if (tagNameOrContainer instanceof HTMLElement || tagNameOrContainer instanceof HTMLDocument) - this.container = tagNameOrContainer - else - throw new Error('tagNameOrContainer is not a tagName or container') + constructor(tagNameOrContainer = 'div') { + if (typeof tagNameOrContainer === 'string') + this.container = document.createElement(tagNameOrContainer) + else if (tagNameOrContainer instanceof HTMLElement || tagNameOrContainer instanceof HTMLDocument) + this.container = tagNameOrContainer + else + throw new Error('tagNameOrContainer is not a tagName or container') - this.children = [] -} + this.children = [] + } add(child) { - if (!(child instanceof Component)) throw new TypeError('child is not component') + if (!(child instanceof Component)) throw new TypeError('child is not component') - this.children.push(child) + this.children.push(child) - this.container.appendChild(child.container) + this.container.appendChild(child.container) } remove(child) { @@ -23,11 +23,12 @@ class Component { const index = this.children.indexOf(child) - if(index > -1) - this.children.splice(index, 1) - - this.container.removeChild(child.container) - } + if (index > -1) + this.children.splice(index, 1) + + if (this.container.contains(child.container)) + this.container.removeChild(child.container) + } setText(text) { this.container.innerText = text @@ -38,19 +39,19 @@ class Component { } addClass(clazz) { - this.container.classList.add(clazz) + this.container.classList.add(clazz) } removeClass(clazz) { - this.container.classList.remove(clazz) + this.container.classList.remove(clazz) } onClick(listener) { - this.container.addEventListener('click', listener) + this.container.addEventListener('click', listener) } onKeyDown(listener) { - this.container.addEventListener('keydown', listener) + this.container.addEventListener('keydown', listener) } onKeyUp(listener) { diff --git a/staff/angel-patino/socialcode/app/components/core/Button.js b/staff/angel-patino/socialcode/app/components/core/Button.js index be3d3b9a7..546756704 100644 --- a/staff/angel-patino/socialcode/app/components/core/Button.js +++ b/staff/angel-patino/socialcode/app/components/core/Button.js @@ -6,6 +6,6 @@ class Button extends Component { } setType(type) { - this.container.type = type + this.container.type = type } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Field.js b/staff/angel-patino/socialcode/app/components/core/Field.js index 84e9a8c7f..622931322 100644 --- a/staff/angel-patino/socialcode/app/components/core/Field.js +++ b/staff/angel-patino/socialcode/app/components/core/Field.js @@ -2,28 +2,28 @@ class Field extends Component { constructor(id, type, text) { super('div') - this.addClass('Field') + this.addClass('Field') - const label = new Label - label.setText(text) - label.setFor(id) + const label = new Label + label.setText(text) + label.setFor(id) - const input = new Input - input.setId(id) - input.setType(type) + const input = new Input + input.setId(id) + input.setType(type) - this.add(label) - this.add(input) + this.add(label) + this.add(input) } - setPlaceholder(placeholder) { + setPlaceholder(placeholder) { this.children[1].setPlaceholder(placeholder) } getValue() { - const input = this.children[1] + const input = this.children[1] - return input.getValue() - } + return input.getValue() + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Form.js b/staff/angel-patino/socialcode/app/components/core/Form.js index 3aac6de76..62377cef5 100644 --- a/staff/angel-patino/socialcode/app/components/core/Form.js +++ b/staff/angel-patino/socialcode/app/components/core/Form.js @@ -6,10 +6,10 @@ class Form extends Component { } onSubmit(listener) { - this.container.addEventListener('submit', listener) + this.container.addEventListener('submit', listener) } - clear () { - this.container.reset() + clear() { + this.container.reset() } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Image.css b/staff/angel-patino/socialcode/app/components/core/Image.css new file mode 100644 index 000000000..d1abcb0ea --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Image.css @@ -0,0 +1,3 @@ +.Image { + width: 100%; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Image.js b/staff/angel-patino/socialcode/app/components/core/Image.js new file mode 100644 index 000000000..70988c875 --- /dev/null +++ b/staff/angel-patino/socialcode/app/components/core/Image.js @@ -0,0 +1,15 @@ +class Image extends Component { + constructor() { + super('img') + + this.addClass('Image') + } + + setUrl(url) { + this.container.src = url + } + + setAltText(altText) { + this.container.alt = altText + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Input.js b/staff/angel-patino/socialcode/app/components/core/Input.js index d892aea22..d3bc9793e 100644 --- a/staff/angel-patino/socialcode/app/components/core/Input.js +++ b/staff/angel-patino/socialcode/app/components/core/Input.js @@ -1,20 +1,20 @@ -class Input extends Component{ +class Input extends Component { constructor() { super('input') this.addClass('Input') -} + } setType(type) { - this.container.type = type + this.container.type = type } setPlaceholder(placeholder) { - this.container.placeholder = placeholder + this.container.placeholder = placeholder } getValue() { - return this.container.value + return this.container.value } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Label.js b/staff/angel-patino/socialcode/app/components/core/Label.js index f5ef5a8ec..25527e795 100644 --- a/staff/angel-patino/socialcode/app/components/core/Label.js +++ b/staff/angel-patino/socialcode/app/components/core/Label.js @@ -4,6 +4,6 @@ class Label extends Component { } setFor(id) { - this.container.htmlFor = id + this.container.htmlFor = id } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/SubmitForm.js b/staff/angel-patino/socialcode/app/components/core/SubmitForm.js index 07644c152..6cfc25781 100644 --- a/staff/angel-patino/socialcode/app/components/core/SubmitForm.js +++ b/staff/angel-patino/socialcode/app/components/core/SubmitForm.js @@ -1,10 +1,10 @@ class SubmitButton extends Button { - constructor(text) { + constructor(text) { super() - this.addClass('SubmitButton') + this.addClass('SubmitButton') - this.setType('submit') - this.setText(text) + this.setType('submit') + this.setText(text) } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css index 13d8dcc6e..3e1f9d2fe 100644 --- a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css +++ b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css @@ -1,12 +1,12 @@ -.LoginForm { +.FormWithFeedback { padding: 1rem; } -.LoginForm .Feedback { +.FormWithFeedback .Feedback { color: tomato; } -.LoginForm .Feedback.success { +.FormWithFeedback .Feedback.success { color: greenyellow; } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js index 03d2281d2..2f8c9d603 100644 --- a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js +++ b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js @@ -11,20 +11,21 @@ class FormWithFeedback extends Form { } setFeedbakc(message, level) { - if(level === 'success') + if (level === 'success') this.feedbackPanel.addClass('success') - this.feedbackPanel.setText(message) + this.feedbackPanel.setText(message) - this.add(this.feedbackPanel) + this.add(this.feedbackPanel) } - clear () { + + clear() { super.clear() this.feedbackPanel.setText('') this.feedbackPanel.removeClass('success') this.remove(this.feedbackPanel) - + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/data.js b/staff/angel-patino/socialcode/app/data.js index c12c651d0..194c90029 100644 --- a/staff/angel-patino/socialcode/app/data.js +++ b/staff/angel-patino/socialcode/app/data.js @@ -3,17 +3,17 @@ const data = {} data.findUser = callback => { const usersJson = localStorage.users - if (!usersJson) usersJson = '[]' + if (!usersJson) usersJson = '[]' -const users = JSON.parse(usersJson) -const user = users.find(callback) + const users = JSON.parse(usersJson) -return user + const user = users.find(callback) + return user } data.insertUser = user => { - const usersJson = localStorage.users + let usersJson = localStorage.users if (!usersJson) usersJson = '[]' @@ -25,3 +25,29 @@ data.insertUser = user => { localStorage.users = usersJson } + +data.findPosts = callback => { + const postsJson = localStorage.posts + + if (!postsJson) postsJson = '[]' + + const posts = JSON.parse(postsJson) + + const filtered = posts.filter(callback) + + return filtered +} + +data.insertPost = post => { + let postsJson = localStorage.posts + + if (!postsJson) postsJson = '[]' + + const posts = JSON.parse(postsJson) + + posts.push(post) + + postsJson = JSON.stringify(posts) + + localStorage.posts = postsJson +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/errors.js b/staff/angel-patino/socialcode/app/errors.js index 496b3ea8c..2def4b997 100644 --- a/staff/angel-patino/socialcode/app/errors.js +++ b/staff/angel-patino/socialcode/app/errors.js @@ -1,25 +1,24 @@ class ContentError extends Error { constructor(message) { super(message) - + + //this.name = ContentError.name this.name = this.constructor.name } } - -class MatchError extends Error { - constructor(message){ +class MatchError extends Error { + constructor(message) { super(message) + this.name = this.constructor.name } } - - class DuplicityError extends Error { - constructor(message){ + constructor(message) { super(message) + this.name = this.constructor.name } } - diff --git a/staff/angel-patino/socialcode/app/global.css b/staff/angel-patino/socialcode/app/global.css index b9c379744..6855b82e8 100644 --- a/staff/angel-patino/socialcode/app/global.css +++ b/staff/angel-patino/socialcode/app/global.css @@ -12,4 +12,5 @@ body { background-color: var(--second-color); + margin: 0; } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js new file mode 100644 index 000000000..358894307 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js @@ -0,0 +1,44 @@ +class CreatePostForm extends FormWithFeedback { + constructor() { + super() + + this.addClass('CreatePostForm') + + const titleField = new Field('title', 'text', 'Title') + titleField.setPlaceholder('title') + + const imageField = new Field('image', 'text', 'Image') + imageField.setPlaceholder('image') + + const descriptionField = new Field('description', 'text', 'Description') + descriptionField.setPlaceholder('description') + + const submitButton = new SubmitButton('Create') + + + this.add(titleField) + this.add(imageField) + this.add(descriptionField) + this.add(submitButton) + + } + + getTitle() { + const titleField = this.children[0] + + return titleField.getValue() + } + + getImage() { + const imageField = this.children[1] + + return imageField.getValue() + } + + getDescription() { + const descriptionField = this.children[2] + + return descriptionField.getValue() + } + +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/Post.js b/staff/angel-patino/socialcode/app/home/components/Post.js new file mode 100644 index 000000000..203d9a8e2 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/components/Post.js @@ -0,0 +1,26 @@ +class Post extends Component { + constructor(post) { + super('article') + + const authorTitle = new Component('p') + authorTitle.setText(post.author) + + const postTitle = new Component('h2') + postTitle.setText(post.title) + + const postImage = new Image + postImage.setUrl(post.image) + + const postDescription = new Component('p') + postDescription.setText(post.description) + + const postDate = new Component('time') + postDate.setText(post.date) + + this.add(authorTitle) + this.add(postTitle) + this.add(postImage) + this.add(postDescription) + this.add(postDate) + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/index.css b/staff/angel-patino/socialcode/app/home/index.css index 102162302..0d3b2b800 100644 --- a/staff/angel-patino/socialcode/app/home/index.css +++ b/staff/angel-patino/socialcode/app/home/index.css @@ -2,6 +2,7 @@ display: flex; flex-direction: column; align-items: center; + margin: 4rem 2rem; } .Header { @@ -9,4 +10,21 @@ gap: 1rem; width: 100%; justify-content: right; + position: fixed; + top: 0; + padding: .5rem; + box-sizing: border-box; + border-bottom: 1px solid var(--first-color); + background-color: var(--second-color); +} + +.Footer { + width: 100%; + display: flex; + justify-content: center; + position: fixed; + bottom: 0; + padding: .5rem 0; + border-top: 1px solid var(--first-color); + background-color: var(--second-color); } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/index.html b/staff/angel-patino/socialcode/app/home/index.html index 0e67cf458..7d531bace 100644 --- a/staff/angel-patino/socialcode/app/home/index.html +++ b/staff/angel-patino/socialcode/app/home/index.html @@ -6,19 +6,39 @@ Home - - + + + + + + + + + + + + + - + + + + + + + + + + diff --git a/staff/angel-patino/socialcode/app/home/index.js b/staff/angel-patino/socialcode/app/home/index.js index b909973d2..07d1d1312 100644 --- a/staff/angel-patino/socialcode/app/home/index.js +++ b/staff/angel-patino/socialcode/app/home/index.js @@ -1,37 +1,74 @@ if (!logic.isUserLoggedIn()) location.href = '../login' - const view = new Component(document.body) - view.addClass('View') +const view = new Component(document.body) +view.addClass('View') - const header = new Component('header') - header.addClass('Header') +const header = new Component('header') +header.addClass('Header') +view.add(header) - const userName = logic.getUserName() +const userName = logic.getUserName() - const usernameTitle = new Heading(3) - usernameTitle.setText(username) +const usernameTitle = new Heading(3) +usernameTitle.setText(userName) +header.add(usernameTitle) - header.add(usernameTitle) +const logoutButton = new Button +logoutButton.setText('Logout') - const logoutButton = new Button - logoutButton.setText('Logout') +logoutButton.onClick(() => { + logic.logoutUser() - logoutButton.onClick(() => { - logic.logoutUser() + location.href = '../login' +}) + +header.add(logoutButton) + +const main = new Component('main') +view.add(main) + +const postList = new Component('section') +main.add(postList) + +const posts = logic.getAllPosts() + +posts.forEach(post => { + const post2 = new Post(post) + + postList.add(post2) +}) +const createPostForm = new CreatePostForm + +createPostForm.onSubmit(event => { + event.preventDefault() + + const title = createPostForm.getTitle() + const image = createPostForm.getImage() + const description = createPostForm.getDescription() + + try { + logic.createPost(title, image, description) + + // TODO dismount create post form from main + // TODO refresh post list + } catch (error) { + if (error instanceof ContentError) + createPostForm.setFeedback(error.message + ', please, correct it') + else + createPostForm.setFeedback('sorry, there was an error, please try again later') + } +}) - location.href='../login' - }) - header.add(logoutButton) +// TODO mount create post form when clicking on plus button +main.add(createPostForm) - const footer = new Component('footer') - footer.addClass('Footer') +const footer = new Component('footer') +footer.addClass('Footer') +view.add(footer) - const addPostButton = new Button - addPostButton.setText('') - - footer.add(addPostButton) +const addPostButton = new Button +addPostButton.setText('+') +footer.add(addPostButton) - view.add(header) - view.add(footer) \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/logic.js b/staff/angel-patino/socialcode/app/logic.js index 71b1e7686..98ff919c8 100644 --- a/staff/angel-patino/socialcode/app/logic.js +++ b/staff/angel-patino/socialcode/app/logic.js @@ -1,12 +1,12 @@ -var logic = {} +const logic = {} -var EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ -var USERNAME_REGEX = /^[a-zA-Z0-9-_]+$/ -var PASSWORD_REGEX = /^[a-zA-Z0-9-_$%&=\[\]\{\}\<\>\(\)]{8,}$/ +const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +const USERNAME_REGEX = /^[\w-]+$/ +const PASSWORD_REGEX = /^[\w-$%&=\[\]\{\}\<\>\(\)]{8,}$/ -var NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ +const NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ -logic.registerUser = function (name, surname, email, username, password, passwordRepeat) { +logic.registerUser = (name, surname, email, username, password, passwordRepeat) => { if (!NAME_REGEX.test(name)) throw new ContentError('name is not valid') @@ -25,9 +25,7 @@ logic.registerUser = function (name, surname, email, username, password, passwor if (password !== passwordRepeat) throw new MatchError('passwords don\'t match') - var user = data.findUser(function (user) { - return user.email === email || user.username === username - }) + const user = data.findUser(user => user.email === email || user.username === username) if (user) throw new DuplicityError('user already exists') @@ -43,16 +41,14 @@ logic.registerUser = function (name, surname, email, username, password, passwor data.insertUser(user) } -logic.loginUser = function (username, password) { +logic.loginUser = (username, password) => { if (!USERNAME_REGEX.test(username)) throw new ContentError('username is not valid') if (!PASSWORD_REGEX.test(password)) throw new ContentError('password is not valid') - var user = data.findUser(function (user) { - return user.username === username - }) + const user = data.findUser(user => user.username === username) if (!user) throw new MatchError('user not found') @@ -63,25 +59,34 @@ logic.loginUser = function (username, password) { sessionStorage.username = username } -logic.isUserLoggedIn = function () { - // if (sessionStorage.username) - // return true +logic.isUserLoggedIn = () => !!sessionStorage.username - // return false +logic.logoutUser = () => delete sessionStorage.username - // return sessionStorage.username ? true : false +logic.getUserName = () => { + const user = data.findUser(user => user.username === sessionStorage.username) - return !!sessionStorage.username + return user.name } -logic.logoutUser = function () { - delete sessionStorage.username +logic.getAllPosts = () => { + const posts = data.findPosts(() => true) + + return posts.reverse() } -logic.getUserName = function () { - var user = data.findUser(function (user) { - return user.username === sessionStorage.username - }) +logic.createPost = (title, image, description) => { + if (typeof title !== 'string' || !title.length || title.length > 50) throw new ContentError('title is not valid') + if (typeof image !== 'string' || !image.startsWith('http')) throw new ContentError('image is not valid') + if (typeof description !== 'string' || !description.length || description.length > 200) throw new ContentError('description is not valid') + + const post = { + author: sessionStorage.username, + title, + image, + description, + date: new Date().toISOString() + } - return user.name + data.insertPost(post) } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/LoginForm.js b/staff/angel-patino/socialcode/app/login/LoginForm.js index 2db8e39b3..78976046c 100644 --- a/staff/angel-patino/socialcode/app/login/LoginForm.js +++ b/staff/angel-patino/socialcode/app/login/LoginForm.js @@ -1,33 +1,29 @@ class LoginForm extends FormWithFeedback { - constructor(){ + constructor() { super() - this.addClass('LoginForm') + this.addClass('LoginForm') - const usernameField = new Field('username', 'text', 'Username') + const usernameField = new Field('username', 'text', 'Username') - const passwordField = new Field('password', 'password', 'Password') + const passwordField = new Field('password', 'password', 'Password') - const submitButton = new SubmitButton('Login') - - - this.add(usernameField) - this.add(passwordField) - this.add(submitButton) - + const submitButton = new SubmitButton('Login') + + this.add(usernameField) + this.add(passwordField) + this.add(submitButton) } getUsername() { - const usernameField = this.children[0] + const usernameField = this.children[0] - return usernameField.getValue() + return usernameField.getValue() } getPassword() { - const passwordField = this.children[1] + const passwordField = this.children[1] - return passwordField.getValue() + return passwordField.getValue() } - - } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/index.html b/staff/angel-patino/socialcode/app/login/index.html index f3878b11b..a77490bb5 100644 --- a/staff/angel-patino/socialcode/app/login/index.html +++ b/staff/angel-patino/socialcode/app/login/index.html @@ -15,16 +15,19 @@ + + - - + + + - + @@ -40,4 +43,4 @@ - + \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/index.js b/staff/angel-patino/socialcode/app/login/index.js index 6bb1db2c8..0e20a237e 100644 --- a/staff/angel-patino/socialcode/app/login/index.js +++ b/staff/angel-patino/socialcode/app/login/index.js @@ -1,4 +1,4 @@ -if(logic.isUserLoggedIn()) +if (logic.isUserLoggedIn()) location.href = '../home' const view = new Component(document.body) @@ -13,7 +13,6 @@ loginForm.onSubmit(event => { const username = loginForm.getUsername() const password = loginForm.getPassword() - try { logic.loginUser(username, password) @@ -22,23 +21,20 @@ loginForm.onSubmit(event => { loginForm.setFeedback('user successfully logged in', 'success') - setTimeout(() => - location.href = '../home', 1500) + setTimeout(() => location.href = '../home', 1000) } catch (error) { if (error instanceof ContentError) - //alert(error.message) loginForm.setFeedback(error.message + ', please, correct it') else if (error instanceof MatchError) loginForm.setFeedback('wrong credentials') else loginForm.setFeedback('sorry, there was an error, please try again later') - } }) + const registerLink = new Link registerLink.setText('Register') -//registerLink.setUrl('../register') registerLink.onClick(event => { event.preventDefault() diff --git a/staff/angel-patino/socialcode/app/register/RegisterForm.js b/staff/angel-patino/socialcode/app/register/RegisterForm.js index 6011ed677..ecf1b3a65 100644 --- a/staff/angel-patino/socialcode/app/register/RegisterForm.js +++ b/staff/angel-patino/socialcode/app/register/RegisterForm.js @@ -1,77 +1,71 @@ class RegisterForm extends FormWithFeedback { - constructor(){ + constructor() { super() + this.addClass('RegisterForm') - this.addClass('RegisterForm') + const nameField = new Field('name', 'text', 'Name') + nameField.setPlaceholder('name') - const nameField = new Field('name', 'text', 'Name') - nameField.setPlaceholder('name') + const surnameField = new Field('surname', 'text', 'Surname') + surnameField.setPlaceholder('surname') - const surnameField = new Field('surname', 'text', 'Surname') - surnameField.setPlaceholder('surname') + const emailField = new Field('email', 'email', 'E-mail') + emailField.setPlaceholder('name@example.com') - const emailField = new Field('email', 'email', 'E-mail') - emailField.setPlaceholder('name@example.com') + const usernameField = new Field('username', 'text', 'Username') + usernameField.setPlaceholder('username') - const usernameField = new Field('username', 'text', 'Username') - usernameField.setPlaceholder('username') + const passwordField = new Field('password', 'password', 'Password') + passwordField.setPlaceholder('password') - const passwordField = new Field('password', 'password', 'Password') - passwordField.setPlaceholder('password') + const passwordRepeatField = new Field('password', 'password', 'Password repeat') + passwordRepeatField.setPlaceholder('repeat password') - const passwordRepeatField = new Field('password', 'password', 'Password repeat') - passwordRepeatField.setPlaceholder('repeat password') + const submitButton = new SubmitButton('Register') - const submitButton = new SubmitButton('Register') + this.add(nameField) + this.add(surnameField) + this.add(emailField) + this.add(usernameField) + this.add(passwordField) + this.add(passwordRepeatField) + this.add(submitButton) + } + getName() { + const nameField = this.children[0] - this.add(nameField) - this.add(surnameField) - this.add(emailField) - this.add(usernameField) - this.add(passwordField) - this.add(passwordRepeatField) - this.add(submitButton) - -} + return nameField.getValue() + } + getSurname() { + const surnameField = this.children[1] + return surnameField.getValue() + } -getName() { - const nameField = this.children[0] + getEmail() { + const emailField = this.children[2] - return nameField.getValue() -} + return emailField.getValue() + } -getSurname() { - const surnameField = this.children[1] + getUsername() { + const usernameField = this.children[3] - return surnameField.getValue() -} + return usernameField.getValue() + } -getEmail() { - const emailField = this.children[2] + getPassword() { + const passwordField = this.children[4] - return emailField.getValue() -} + return passwordField.getValue() + } -getUsername() { - const usernameField = this.children[3] - - return usernameField.getValue() -} - -getPassword() { - const passwordField = this.children[4] - - return passwordField.getValue() -} - -getPasswordRepeat() { - const passwordFieldRepeat = this.children[5] - - return passwordFieldRepeat.getValue() -} + getPasswordRepeat() { + const passwordFieldRepeat = this.children[5] + return passwordFieldRepeat.getValue() + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/register/index.html b/staff/angel-patino/socialcode/app/register/index.html index bc7f023b7..9df166028 100644 --- a/staff/angel-patino/socialcode/app/register/index.html +++ b/staff/angel-patino/socialcode/app/register/index.html @@ -15,15 +15,19 @@ + + - + + + diff --git a/staff/angel-patino/socialcode/app/register/index.js b/staff/angel-patino/socialcode/app/register/index.js index bc7ac9fdc..36139c812 100644 --- a/staff/angel-patino/socialcode/app/register/index.js +++ b/staff/angel-patino/socialcode/app/register/index.js @@ -1,5 +1,4 @@ -//if(sessionStorage.username) -if(logic.isUserLoggedIn()) +if (logic.isUserLoggedIn()) location.href = '../home' const view = new Component(document.body) @@ -7,14 +6,12 @@ view.addClass('View') const title = new Heading(1) title.setText('Register') -title.onClick(() => alert('By clicking on this title you wont get anything .P')) - +title.onClick(() => alert('By clicking on this title you wont get anything .P')) const registerForm = new RegisterForm registerForm.onSubmit(event => { event.preventDefault() - const name = registerForm.getName() const surname = registerForm.getSurname() const email = registerForm.getEmail() @@ -25,29 +22,25 @@ registerForm.onSubmit(event => { try { logic.registerUser(name, surname, email, username, password, passwordRepeat) - registerForm.clear() + // registerForm.clear() registerForm.setFeedback('user successfully registered', 'success') - setTimeout(() => location.href = '../login', 2000) - + setTimeout(() => location.href = '../login', 1000) } catch (error) { if (error instanceof ContentError) - //alert(error.message) - registerForm.setFeedback(error.message + ', please, correct it') - if (error instanceof MatchError) - registerForm.setFeedback(error.message + ',please, retype them') - else if ( error instanceof DuplicityError) + registerForm.setFeedback(error.message + ', please, correct it') + else if (error instanceof MatchError) + registerForm.setFeedback(error.message + ', please, retype them') + else if (error instanceof DuplicityError) registerForm.setFeedback(error.message + ', please, enter new one') else registerForm.setFeedback('sorry, there was an error, please try again later') - } }) const loginLink = new Link loginLink.setText('Login') -//loginLink.setUrl('../login') loginLink.onClick(event => { event.preventDefault() From 4e2d53cb1bb80968839561863c5b409cc7da9192 Mon Sep 17 00:00:00 2001 From: Pat Date: Tue, 7 May 2024 13:35:35 +0200 Subject: [PATCH 05/15] mechanise create post-form visibility; post-list reload; create post-list component; add delete post functionality #124 --- .../socialcode/app/components/Component.js | 5 ++++ .../socialcode/app/components/core/Button.css | 1 + .../socialcode/app/components/core/Button.js | 6 +++-- .../core/{SubmitForm.js => SubmitButton.js} | 5 ++-- .../components/library/FormWithFeedback.css | 1 - .../components/library/FormWithFeedback.js | 3 +-- staff/angel-patino/socialcode/app/data.js | 22 ++++++++++++++-- .../app/home/components/CreatePostForm.css | 8 ++++++ .../app/home/components/CreatePostForm.js | 11 ++++++-- .../socialcode/app/home/components/Post.js | 18 +++++++++++++ .../app/home/components/PostList.js | 23 +++++++++++++++++ .../socialcode/app/home/index.html | 5 +++- .../angel-patino/socialcode/app/home/index.js | 25 ++++++++++--------- staff/angel-patino/socialcode/app/logic.js | 13 +++++++--- .../socialcode/app/login/index.html | 2 +- .../socialcode/app/register/index.html | 2 +- .../socialcode/app/register/index.js | 2 +- .../socialcode/my-app/components/Componen.js | 11 ++++++++ .../my-app/components/core/Button.js | 11 ++++++++ .../my-app/components/core/Field.js | 15 +++++++++++ .../my-app/components/core/Label.js | 10 ++++++++ .../socialcode/my-app/components/core/Link.js | 18 +++++++++++++ 22 files changed, 186 insertions(+), 31 deletions(-) rename staff/angel-patino/socialcode/app/components/core/{SubmitForm.js => SubmitButton.js} (76%) create mode 100644 staff/angel-patino/socialcode/app/home/components/CreatePostForm.css create mode 100644 staff/angel-patino/socialcode/app/home/components/PostList.js create mode 100644 staff/angel-patino/socialcode/my-app/components/Componen.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Button.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Field.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Label.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Link.js diff --git a/staff/angel-patino/socialcode/app/components/Component.js b/staff/angel-patino/socialcode/app/components/Component.js index 3da1c66e3..46da1722f 100644 --- a/staff/angel-patino/socialcode/app/components/Component.js +++ b/staff/angel-patino/socialcode/app/components/Component.js @@ -30,6 +30,11 @@ class Component { this.container.removeChild(child.container) } + removeAll() { + const children = this.children.concat() //slice(0) dos formas para crear un array + children.forEach(child => this.remove(child)) + } + setText(text) { this.container.innerText = text } diff --git a/staff/angel-patino/socialcode/app/components/core/Button.css b/staff/angel-patino/socialcode/app/components/core/Button.css index 3cbda9cba..b561d84fc 100644 --- a/staff/angel-patino/socialcode/app/components/core/Button.css +++ b/staff/angel-patino/socialcode/app/components/core/Button.css @@ -3,5 +3,6 @@ background-color: transparent; font-size: 1rem; border: 1px solid var(--first-color); + cursor: pointer; } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Button.js b/staff/angel-patino/socialcode/app/components/core/Button.js index 546756704..e13760aca 100644 --- a/staff/angel-patino/socialcode/app/components/core/Button.js +++ b/staff/angel-patino/socialcode/app/components/core/Button.js @@ -1,8 +1,10 @@ class Button extends Component { - constructor() { + constructor(text) { super('button') - this.addClass('Button') + this.addClass('Button') + + if (text) this.setText(text) } setType(type) { diff --git a/staff/angel-patino/socialcode/app/components/core/SubmitForm.js b/staff/angel-patino/socialcode/app/components/core/SubmitButton.js similarity index 76% rename from staff/angel-patino/socialcode/app/components/core/SubmitForm.js rename to staff/angel-patino/socialcode/app/components/core/SubmitButton.js index 6cfc25781..d0e032347 100644 --- a/staff/angel-patino/socialcode/app/components/core/SubmitForm.js +++ b/staff/angel-patino/socialcode/app/components/core/SubmitButton.js @@ -1,10 +1,11 @@ class SubmitButton extends Button { constructor(text) { - super() + super(text) this.addClass('SubmitButton') this.setType('submit') - this.setText(text) + + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css index 3e1f9d2fe..0e7015959 100644 --- a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css +++ b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css @@ -2,7 +2,6 @@ padding: 1rem; } - .FormWithFeedback .Feedback { color: tomato; } diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js index 2f8c9d603..f36bb7f88 100644 --- a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js +++ b/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js @@ -10,7 +10,7 @@ class FormWithFeedback extends Form { this.feedbackPanel = feedbackPanel } - setFeedbakc(message, level) { + setFeedback(message, level) { if (level === 'success') this.feedbackPanel.addClass('success') @@ -26,6 +26,5 @@ class FormWithFeedback extends Form { this.feedbackPanel.removeClass('success') this.remove(this.feedbackPanel) - } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/data.js b/staff/angel-patino/socialcode/app/data.js index 194c90029..c7df20f98 100644 --- a/staff/angel-patino/socialcode/app/data.js +++ b/staff/angel-patino/socialcode/app/data.js @@ -1,7 +1,7 @@ const data = {} data.findUser = callback => { - const usersJson = localStorage.users + let usersJson = localStorage.users if (!usersJson) usersJson = '[]' @@ -27,7 +27,7 @@ data.insertUser = user => { } data.findPosts = callback => { - const postsJson = localStorage.posts + let postsJson = localStorage.posts if (!postsJson) postsJson = '[]' @@ -50,4 +50,22 @@ data.insertPost = post => { postsJson = JSON.stringify(posts) localStorage.posts = postsJson +} + +data.deletePost = callback => { + let postsJson = localStorage.posts + + if (!postsJson) postsJson = '[]' + + const posts = JSON.parse(postsJson) + + const index = posts.findIndex(callback) + + if (index > -1) { + posts.splice(index, 1) + + postsJson = JSON.stringify(posts) + + localStorage.posts = postsJson + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.css b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.css new file mode 100644 index 000000000..5e19bd086 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.css @@ -0,0 +1,8 @@ +.CreatePostForm { + width: 100%; + position: fixed; + bottom: 3rem; + left: 0; + box-sizing: border-box; + background-color: var(--second-color); +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js index 358894307..0a89554eb 100644 --- a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js +++ b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js @@ -13,14 +13,18 @@ class CreatePostForm extends FormWithFeedback { const descriptionField = new Field('description', 'text', 'Description') descriptionField.setPlaceholder('description') - const submitButton = new SubmitButton('Create') + const cancelButton = new Button('Cancel') + cancelButton.setType('button') + + this.cancelButton = cancelButton + const submitButton = new SubmitButton('Create') this.add(titleField) this.add(imageField) this.add(descriptionField) + this.add(cancelButton) this.add(submitButton) - } getTitle() { @@ -41,4 +45,7 @@ class CreatePostForm extends FormWithFeedback { return descriptionField.getValue() } + onCancelClick(listener) { + this.cancelButton.onClick(listener) + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/Post.js b/staff/angel-patino/socialcode/app/home/components/Post.js index 203d9a8e2..46cd1d6a3 100644 --- a/staff/angel-patino/socialcode/app/home/components/Post.js +++ b/staff/angel-patino/socialcode/app/home/components/Post.js @@ -22,5 +22,23 @@ class Post extends Component { this.add(postImage) this.add(postDescription) this.add(postDate) + + if (post.author === logic.getLoggedInUsername()) { + const deleteButton = new Button('Delete') + + deleteButton.onClick(() => { + logic.deletePost(post.id) + + this.onPostDeletedListener() + + }) + + this.add(deleteButton) + } + } + + onPostDeleted(listener) { + this.onPostDeletedListener = listener + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/PostList.js b/staff/angel-patino/socialcode/app/home/components/PostList.js new file mode 100644 index 000000000..61a2e9791 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/components/PostList.js @@ -0,0 +1,23 @@ +class PostList extends Component { + constructor() { + super('section') + + this.load() + + } + + load() { + this.removeAll() + + const posts = logic.getAllPosts() + + posts.forEach(post => { + const post2 = new Post(post) + + post2.onPostDeleted(() => this.load()) + + this.add(post2) + + }) + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/index.html b/staff/angel-patino/socialcode/app/home/index.html index 7d531bace..b15eebfec 100644 --- a/staff/angel-patino/socialcode/app/home/index.html +++ b/staff/angel-patino/socialcode/app/home/index.html @@ -16,6 +16,8 @@ + + @@ -34,11 +36,12 @@ - + + diff --git a/staff/angel-patino/socialcode/app/home/index.js b/staff/angel-patino/socialcode/app/home/index.js index 07d1d1312..992450bde 100644 --- a/staff/angel-patino/socialcode/app/home/index.js +++ b/staff/angel-patino/socialcode/app/home/index.js @@ -28,16 +28,9 @@ header.add(logoutButton) const main = new Component('main') view.add(main) -const postList = new Component('section') +const postList = new PostList main.add(postList) -const posts = logic.getAllPosts() - -posts.forEach(post => { - const post2 = new Post(post) - - postList.add(post2) -}) const createPostForm = new CreatePostForm createPostForm.onSubmit(event => { @@ -50,8 +43,11 @@ createPostForm.onSubmit(event => { try { logic.createPost(title, image, description) - // TODO dismount create post form from main - // TODO refresh post list + createPostForm.clear() + + main.remove(createPostForm) + + postList.load() } catch (error) { if (error instanceof ContentError) createPostForm.setFeedback(error.message + ', please, correct it') @@ -61,8 +57,11 @@ createPostForm.onSubmit(event => { }) -// TODO mount create post form when clicking on plus button -main.add(createPostForm) +createPostForm.onCancelClick(event => { + event.preventDefault() + + main.remove(createPostForm) +}) const footer = new Component('footer') footer.addClass('Footer') @@ -70,5 +69,7 @@ view.add(footer) const addPostButton = new Button addPostButton.setText('+') + +addPostButton.onClick(() => main.add(createPostForm)) footer.add(addPostButton) diff --git a/staff/angel-patino/socialcode/app/logic.js b/staff/angel-patino/socialcode/app/logic.js index 98ff919c8..1ecce7c7b 100644 --- a/staff/angel-patino/socialcode/app/logic.js +++ b/staff/angel-patino/socialcode/app/logic.js @@ -25,7 +25,7 @@ logic.registerUser = (name, surname, email, username, password, passwordRepeat) if (password !== passwordRepeat) throw new MatchError('passwords don\'t match') - const user = data.findUser(user => user.email === email || user.username === username) + let user = data.findUser(user => user.email === email || user.username === username) if (user) throw new DuplicityError('user already exists') @@ -48,7 +48,7 @@ logic.loginUser = (username, password) => { if (!PASSWORD_REGEX.test(password)) throw new ContentError('password is not valid') - const user = data.findUser(user => user.username === username) + let user = data.findUser(user => user.username === username) if (!user) throw new MatchError('user not found') @@ -64,7 +64,7 @@ logic.isUserLoggedIn = () => !!sessionStorage.username logic.logoutUser = () => delete sessionStorage.username logic.getUserName = () => { - const user = data.findUser(user => user.username === sessionStorage.username) + let user = data.findUser(user => user.username === sessionStorage.username) return user.name } @@ -81,6 +81,7 @@ logic.createPost = (title, image, description) => { if (typeof description !== 'string' || !description.length || description.length > 200) throw new ContentError('description is not valid') const post = { + id: Date.now, author: sessionStorage.username, title, image, @@ -89,4 +90,8 @@ logic.createPost = (title, image, description) => { } data.insertPost(post) -} \ No newline at end of file +} + +logic.getLoggedInUsername = () => sessionStorage.username + +logic.deletePost = id => data.deletePost(post => post.id === id) diff --git a/staff/angel-patino/socialcode/app/login/index.html b/staff/angel-patino/socialcode/app/login/index.html index a77490bb5..fd5f50fab 100644 --- a/staff/angel-patino/socialcode/app/login/index.html +++ b/staff/angel-patino/socialcode/app/login/index.html @@ -33,7 +33,7 @@ - + diff --git a/staff/angel-patino/socialcode/app/register/index.html b/staff/angel-patino/socialcode/app/register/index.html index 9df166028..0f43d0648 100644 --- a/staff/angel-patino/socialcode/app/register/index.html +++ b/staff/angel-patino/socialcode/app/register/index.html @@ -33,7 +33,7 @@ - + diff --git a/staff/angel-patino/socialcode/app/register/index.js b/staff/angel-patino/socialcode/app/register/index.js index 36139c812..e1ddc841c 100644 --- a/staff/angel-patino/socialcode/app/register/index.js +++ b/staff/angel-patino/socialcode/app/register/index.js @@ -22,7 +22,7 @@ registerForm.onSubmit(event => { try { logic.registerUser(name, surname, email, username, password, passwordRepeat) - // registerForm.clear() + registerForm.clear() registerForm.setFeedback('user successfully registered', 'success') diff --git a/staff/angel-patino/socialcode/my-app/components/Componen.js b/staff/angel-patino/socialcode/my-app/components/Componen.js new file mode 100644 index 000000000..40b538488 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/Componen.js @@ -0,0 +1,11 @@ +class Component { + constructor(tagOrName) { + if (typeof tagOrName === 'string') + tagOrName = document.createElement(tagOrName) + else if (tagOrName instanceof HTMLElement || tagOrName instanceof HTMLDocument) + this.container = tagOrName + else + throw new Error('is not a tag or container') + } + +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Button.js b/staff/angel-patino/socialcode/my-app/components/core/Button.js new file mode 100644 index 000000000..6acbefc57 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Button.js @@ -0,0 +1,11 @@ +class Button extends Component { + constructor() { + super('button') + + this.addClass('Button') + } + + setType(type) { + this.container.type = type + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Field.js b/staff/angel-patino/socialcode/my-app/components/core/Field.js new file mode 100644 index 000000000..5b7f2c462 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Field.js @@ -0,0 +1,15 @@ +class Field extends Component { + constructor(id, type, text) { + super('div') + + this.addClass('Field') + + const label = new Label + label.setFor(id) + label.setText(text) + + const input = new Input + + + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Label.js b/staff/angel-patino/socialcode/my-app/components/core/Label.js new file mode 100644 index 000000000..a7f40a151 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Label.js @@ -0,0 +1,10 @@ +class Label extends Component { + constructor() { + super('label') + + } + + setFor(id) { + this.container.htmlFor = id + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Link.js b/staff/angel-patino/socialcode/my-app/components/core/Link.js new file mode 100644 index 000000000..291aa9e0f --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Link.js @@ -0,0 +1,18 @@ +class Link extends Component { + constructor() { + super('a') + + this.addClass('Link') + this.setUrl('') + } + + setUrl(url) { + this.container.href = url + } + + setTarget(target) { + this.container.target = target + } + + +} \ No newline at end of file From 91d74d9eb711e82f4fcc403bde2a9c6b41dd78b2 Mon Sep 17 00:00:00 2001 From: Pat Date: Thu, 9 May 2024 16:43:21 +0200 Subject: [PATCH 06/15] move more responsability (logic) to forms(create post, login and register) #124 --- .../app/home/components/Confirm.css | 11 ++++++ .../socialcode/app/home/components/Confirm.js | 34 ++++++++++++++++++ .../app/home/components/CreatePostForm.js | 35 +++++++++++++++++-- .../socialcode/app/home/components/Post.js | 17 +++++++-- .../socialcode/app/home/index.html | 2 ++ .../angel-patino/socialcode/app/home/index.js | 33 ++++------------- .../socialcode/app/login/LoginForm.js | 28 +++++++++++++++ .../socialcode/app/login/index.js | 24 +------------ .../socialcode/app/register/RegisterForm.js | 33 +++++++++++++++++ .../socialcode/app/register/index.js | 30 +--------------- .../my-app/components/core/Button.css | 7 ++++ .../my-app/components/core/Button.js | 4 ++- .../my-app/components/core/Field.css | 5 +++ .../my-app/components/core/Form.css | 5 +++ .../socialcode/my-app/components/core/Form.js | 15 ++++++++ .../my-app/components/core/Heading.css | 7 ++++ .../my-app/components/core/Heading.js | 8 +++++ .../my-app/components/core/Image.css | 3 ++ .../my-app/components/core/Image.js | 15 ++++++++ .../my-app/components/core/Input.css | 6 ++++ .../my-app/components/core/Input.js | 19 ++++++++++ .../my-app/components/core/SubmitButton.css | 3 ++ .../my-app/components/core/SubmitButton.js | 9 +++++ 23 files changed, 270 insertions(+), 83 deletions(-) create mode 100644 staff/angel-patino/socialcode/app/home/components/Confirm.css create mode 100644 staff/angel-patino/socialcode/app/home/components/Confirm.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Button.css create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Field.css create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Form.css create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Form.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Heading.css create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Heading.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Image.css create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Image.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Input.css create mode 100644 staff/angel-patino/socialcode/my-app/components/core/Input.js create mode 100644 staff/angel-patino/socialcode/my-app/components/core/SubmitButton.css create mode 100644 staff/angel-patino/socialcode/my-app/components/core/SubmitButton.js diff --git a/staff/angel-patino/socialcode/app/home/components/Confirm.css b/staff/angel-patino/socialcode/app/home/components/Confirm.css new file mode 100644 index 000000000..a0d9efe15 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/components/Confirm.css @@ -0,0 +1,11 @@ +.Confirm { + padding: .4rem; + background-color: var(--second - color); + font-size: 1rem; + border: 1px solid var(--first - color); + position: fixed; + top: 0; + left: 0; + width: 100px; + height: 250px; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/Confirm.js b/staff/angel-patino/socialcode/app/home/components/Confirm.js new file mode 100644 index 000000000..7b90f7274 --- /dev/null +++ b/staff/angel-patino/socialcode/app/home/components/Confirm.js @@ -0,0 +1,34 @@ +class Confirm extends Component { + constructor() { + super('div') + + this.addClass('Confirm') + + const question = new Component('p') + + this.question = question + + const cancelButton = new Button('Cancel') + + cancelButton.onClick(() => this.onCancelListener()) + + const confirmButton = new Button('Confirm') + confirmButton.onClick(() => this.onConfirmListener()) + + this.add(question) + this.add(cancelButton) + this.add(confirmButton) + } + + setText(text) { + this.question.setText(text) + } + + onCancel(listener) { + this.onCancelListener = listener + } + + onConfirm(listener) { + this.onConfirmListener = listener + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js index 0a89554eb..d6cf6889a 100644 --- a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js +++ b/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js @@ -16,7 +16,13 @@ class CreatePostForm extends FormWithFeedback { const cancelButton = new Button('Cancel') cancelButton.setType('button') - this.cancelButton = cancelButton + cancelButton.onClick(event => { + event.preventDefault() + + this.clear() + + this.onCancelClicklistener() + }) const submitButton = new SubmitButton('Create') @@ -25,6 +31,27 @@ class CreatePostForm extends FormWithFeedback { this.add(descriptionField) this.add(cancelButton) this.add(submitButton) + + this.onSubmit(event => { + event.preventDefault() + + const title = this.getTitle() + const image = this.getImage() + const description = this.getDescription() + + try { + logic.createPost(title, image, description) + + this.clear() + + this.onPostCreatedListener() + } catch (error) { + if (error instanceof ContentError) + createPostForm.setFeedback(error.message + ', please, correct it') + else + createPostForm.setFeedback('sorry, there was an error, please try again later') + } + }) } getTitle() { @@ -46,6 +73,10 @@ class CreatePostForm extends FormWithFeedback { } onCancelClick(listener) { - this.cancelButton.onClick(listener) + this.onCancelClicklistener = listener + } + + onPostCreated(listener) { + this.onPostSubmitListener = listener } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/home/components/Post.js b/staff/angel-patino/socialcode/app/home/components/Post.js index 46cd1d6a3..3f450feb5 100644 --- a/staff/angel-patino/socialcode/app/home/components/Post.js +++ b/staff/angel-patino/socialcode/app/home/components/Post.js @@ -27,9 +27,22 @@ class Post extends Component { const deleteButton = new Button('Delete') deleteButton.onClick(() => { - logic.deletePost(post.id) + /* logic.deletePost(post.id) - this.onPostDeletedListener() + this.onPostDeletedListener() */ + + const confirm = new Confirm + confirm.setText('Delete the post?') + + confirm.onConfirm(() => { + logic.deletePost(post.id) + + this.onPostDeletedListener() + }) + + confirm.onCancel(() => this.remove(confirm)) + + this.add(confirm) }) diff --git a/staff/angel-patino/socialcode/app/home/index.html b/staff/angel-patino/socialcode/app/home/index.html index b15eebfec..67c0ce9f2 100644 --- a/staff/angel-patino/socialcode/app/home/index.html +++ b/staff/angel-patino/socialcode/app/home/index.html @@ -13,6 +13,7 @@ + @@ -42,6 +43,7 @@ + diff --git a/staff/angel-patino/socialcode/app/home/index.js b/staff/angel-patino/socialcode/app/home/index.js index 992450bde..7685196fd 100644 --- a/staff/angel-patino/socialcode/app/home/index.js +++ b/staff/angel-patino/socialcode/app/home/index.js @@ -6,6 +6,7 @@ view.addClass('View') const header = new Component('header') header.addClass('Header') + view.add(header) const userName = logic.getUserName() @@ -33,43 +34,23 @@ main.add(postList) const createPostForm = new CreatePostForm -createPostForm.onSubmit(event => { - event.preventDefault() - - const title = createPostForm.getTitle() - const image = createPostForm.getImage() - const description = createPostForm.getDescription() - - try { - logic.createPost(title, image, description) - - createPostForm.clear() - - main.remove(createPostForm) +createPostForm.onPostCreated(() => { + main.remove(createPostForm) - postList.load() - } catch (error) { - if (error instanceof ContentError) - createPostForm.setFeedback(error.message + ', please, correct it') - else - createPostForm.setFeedback('sorry, there was an error, please try again later') - } + postList.load() }) - -createPostForm.onCancelClick(event => { - event.preventDefault() - - main.remove(createPostForm) -}) +createPostForm.onCancelClick(() => main.remove(createPostForm)) const footer = new Component('footer') footer.addClass('Footer') + view.add(footer) const addPostButton = new Button addPostButton.setText('+') addPostButton.onClick(() => main.add(createPostForm)) + footer.add(addPostButton) diff --git a/staff/angel-patino/socialcode/app/login/LoginForm.js b/staff/angel-patino/socialcode/app/login/LoginForm.js index 78976046c..6e8408408 100644 --- a/staff/angel-patino/socialcode/app/login/LoginForm.js +++ b/staff/angel-patino/socialcode/app/login/LoginForm.js @@ -13,6 +13,30 @@ class LoginForm extends FormWithFeedback { this.add(usernameField) this.add(passwordField) this.add(submitButton) + + this.onSubmit(event => { + event.preventDefault() + + const username = this.getUsername() + const password = this.getPassword() + + try { + logic.loginUser(username, password) + + this.clear() + + this.setFeedback('user successfully logged in', 'success') + + this.onLoggedInListener() + } catch (error) { + if (error instanceof ContentError) + loginForm.setFeedback(error.message + ', please, correct it') + else if (error instanceof MatchError) + loginForm.setFeedback('wrong credentials') + else + loginForm.setFeedback('sorry, there was an error, please try again later') + } + }) } getUsername() { @@ -26,4 +50,8 @@ class LoginForm extends FormWithFeedback { return passwordField.getValue() } + + onLoggedIn(listener) { + this.onLoggedInListener = listener + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/index.js b/staff/angel-patino/socialcode/app/login/index.js index 0e20a237e..f94811fee 100644 --- a/staff/angel-patino/socialcode/app/login/index.js +++ b/staff/angel-patino/socialcode/app/login/index.js @@ -8,30 +8,8 @@ const title = new Heading(1) title.setText('Login') const loginForm = new LoginForm -loginForm.onSubmit(event => { - event.preventDefault() - - const username = loginForm.getUsername() - const password = loginForm.getPassword() - - try { - logic.loginUser(username, password) - - loginForm.clear() - - loginForm.setFeedback('user successfully logged in', 'success') - - setTimeout(() => location.href = '../home', 1000) - } catch (error) { - if (error instanceof ContentError) - loginForm.setFeedback(error.message + ', please, correct it') - else if (error instanceof MatchError) - loginForm.setFeedback('wrong credentials') - else - loginForm.setFeedback('sorry, there was an error, please try again later') - } -}) +loginForm.onLoggedIn(() => setTimeout(() => location.href = '../home', 1000)) const registerLink = new Link registerLink.setText('Register') diff --git a/staff/angel-patino/socialcode/app/register/RegisterForm.js b/staff/angel-patino/socialcode/app/register/RegisterForm.js index ecf1b3a65..a971828ef 100644 --- a/staff/angel-patino/socialcode/app/register/RegisterForm.js +++ b/staff/angel-patino/socialcode/app/register/RegisterForm.js @@ -31,6 +31,35 @@ class RegisterForm extends FormWithFeedback { this.add(passwordField) this.add(passwordRepeatField) this.add(submitButton) + + this.onSubmit(event => { + event.preventDefault() + + const name = this.getName() + const surname = this.getSurname() + const email = this.getEmail() + const username = this.getUsername() + const password = this.getPassword() + const passwordRepeat = this.getPasswordRepeat() + + try { + logic.registerUser(name, surname, email, username, password, passwordRepeat) + + this.clear() + this.setFeedback('user successfully registed', 'success') + + this.onRegisteredListener() + } catch (error) { + if (error instanceof ContentError) + this.setFeedback(error.message + ', please, correct it') + else if (error instanceof MatchError) + this.setFeedback(error.message + ',please, ') + else if (error instanceof DuplicityError) + this.setFeedback(error.message + ', please, enter new one') + else + this.setFeedback('sorry, there was an error, please try again later') + } + }) } getName() { @@ -68,4 +97,8 @@ class RegisterForm extends FormWithFeedback { return passwordFieldRepeat.getValue() } + + onRegistered(listener) { + this.onRegisteredListener = listener + } } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/register/index.js b/staff/angel-patino/socialcode/app/register/index.js index e1ddc841c..2a12bc393 100644 --- a/staff/angel-patino/socialcode/app/register/index.js +++ b/staff/angel-patino/socialcode/app/register/index.js @@ -9,35 +9,7 @@ title.setText('Register') title.onClick(() => alert('By clicking on this title you wont get anything .P')) const registerForm = new RegisterForm -registerForm.onSubmit(event => { - event.preventDefault() - - const name = registerForm.getName() - const surname = registerForm.getSurname() - const email = registerForm.getEmail() - const username = registerForm.getUsername() - const password = registerForm.getPassword() - const passwordRepeat = registerForm.getPasswordRepeat() - - try { - logic.registerUser(name, surname, email, username, password, passwordRepeat) - - registerForm.clear() - - registerForm.setFeedback('user successfully registered', 'success') - - setTimeout(() => location.href = '../login', 1000) - } catch (error) { - if (error instanceof ContentError) - registerForm.setFeedback(error.message + ', please, correct it') - else if (error instanceof MatchError) - registerForm.setFeedback(error.message + ', please, retype them') - else if (error instanceof DuplicityError) - registerForm.setFeedback(error.message + ', please, enter new one') - else - registerForm.setFeedback('sorry, there was an error, please try again later') - } -}) +registerForm.onRegistered(() => setTimeout(() => location.href = '../login', 1000)) const loginLink = new Link loginLink.setText('Login') diff --git a/staff/angel-patino/socialcode/my-app/components/core/Button.css b/staff/angel-patino/socialcode/my-app/components/core/Button.css new file mode 100644 index 000000000..b4239e7a6 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Button.css @@ -0,0 +1,7 @@ +.Button { + padding: .4rem; + background-color: transparent; + font-size: 1rem; + border: 1px solid var(--first-color); + cursor: pointer; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Button.js b/staff/angel-patino/socialcode/my-app/components/core/Button.js index 6acbefc57..25ba6b6d3 100644 --- a/staff/angel-patino/socialcode/my-app/components/core/Button.js +++ b/staff/angel-patino/socialcode/my-app/components/core/Button.js @@ -1,8 +1,10 @@ class Button extends Component { - constructor() { + constructor(text) { super('button') this.addClass('Button') + + if (text) this.setText(text) } setType(type) { diff --git a/staff/angel-patino/socialcode/my-app/components/core/Field.css b/staff/angel-patino/socialcode/my-app/components/core/Field.css new file mode 100644 index 000000000..708f5849b --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Field.css @@ -0,0 +1,5 @@ +.Field { + display: flex; + flex-direction: column; + margin: .25rem 0; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Form.css b/staff/angel-patino/socialcode/my-app/components/core/Form.css new file mode 100644 index 000000000..379cad316 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Form.css @@ -0,0 +1,5 @@ +.Form { + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Form.js b/staff/angel-patino/socialcode/my-app/components/core/Form.js new file mode 100644 index 000000000..57ef54f09 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Form.js @@ -0,0 +1,15 @@ +class Form extends Compponent { + constructor() { + super('form') + + this.add('Form') + } + + onSubmit(listener) { + this.container.addEventListener('click', listener) + } + + clear() { + this.container.reset() + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Heading.css b/staff/angel-patino/socialcode/my-app/components/core/Heading.css new file mode 100644 index 000000000..47d717485 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Heading.css @@ -0,0 +1,7 @@ +.Heading { + padding: .4rem; + background-color: transparent; + font-size: 1rem; + border: 1px solid var(--first-color); + +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Heading.js b/staff/angel-patino/socialcode/my-app/components/core/Heading.js new file mode 100644 index 000000000..4b9241f28 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Heading.js @@ -0,0 +1,8 @@ +class Heading extends Component { + constructor(level) { + super('h' + level) + + this.addClass('Heading') + + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Image.css b/staff/angel-patino/socialcode/my-app/components/core/Image.css new file mode 100644 index 000000000..d1abcb0ea --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Image.css @@ -0,0 +1,3 @@ +.Image { + width: 100%; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Image.js b/staff/angel-patino/socialcode/my-app/components/core/Image.js new file mode 100644 index 000000000..e58408e08 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Image.js @@ -0,0 +1,15 @@ +class Image extends Component { + constructor() { + super('img') + + this.addClass('Image') + } + + setUrl(url) { + this.container.src = url + } + + setAlt(altText) { + this.container.alt = altText + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Input.css b/staff/angel-patino/socialcode/my-app/components/core/Input.css new file mode 100644 index 000000000..6c09b66f6 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Input.css @@ -0,0 +1,6 @@ +.Input { + padding: .4rem; + background-color: transparent; + font-size: 1rem; + border: 1px solid var(--first-color); +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/Input.js b/staff/angel-patino/socialcode/my-app/components/core/Input.js new file mode 100644 index 000000000..f8ee45f90 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/Input.js @@ -0,0 +1,19 @@ +class Input extends Component { + constructor() { + super('input') + + this.addClass('Input') + } + + setType() { + this.container.type = type + } + + setPlaceHolder(placeholder) { + this.container.placeholder = placeholder + } + + getValue() { + return this.container.value + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/SubmitButton.css b/staff/angel-patino/socialcode/my-app/components/core/SubmitButton.css new file mode 100644 index 000000000..4452ec3b8 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/SubmitButton.css @@ -0,0 +1,3 @@ +.SubmitButton { + margin: .5rem 0; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/my-app/components/core/SubmitButton.js b/staff/angel-patino/socialcode/my-app/components/core/SubmitButton.js new file mode 100644 index 000000000..7f1bc6a20 --- /dev/null +++ b/staff/angel-patino/socialcode/my-app/components/core/SubmitButton.js @@ -0,0 +1,9 @@ +class SubmitButton extends Button { + constructor(text) { + super(text) + + this.addClass('SubmiButton') + + this.setType('submit') + } +} \ No newline at end of file From 24e09c1d1f40954106aede5932b75144a4e8ad96 Mon Sep 17 00:00:00 2001 From: Pat Date: Wed, 26 Jun 2024 15:14:48 +0200 Subject: [PATCH 07/15] creation of API and app in SocialCode. In API, created folders: data, logic, test. Hash used for passwords and token used in the index.js part of the API #124 --- staff/angel-patino/socialcode/_api1/README.md | 41 + .../socialcode/_api1/data/index.js | 150 + .../socialcode/_api1/data/index.test.js | 11 + .../socialcode/_api1/data/posts.json | 1 + .../socialcode/_api1/data/users.json | 1 + staff/angel-patino/socialcode/_api1/errors.js | 39 + .../socialcode/_api1/logic/index.js | 259 + .../socialcode/_api1/logic/index.test.js | 15 + .../socialcode/_api1/package-lock.json | 0 staff/angel-patino/socialcode/api/.env | 2 + staff/angel-patino/socialcode/api/.gitignore | 2 + staff/angel-patino/socialcode/api/README.md | 29 + .../angel-patino/socialcode/api/data/index.js | 150 + .../socialcode/api/data/index.test.js | 11 + .../socialcode/api/data/posts.json | 1 + .../socialcode/api/data/users.json | 26 + staff/angel-patino/socialcode/api/index.js | 198 + .../socialcode/api/logic/authenticateUser.js | 42 + .../api/logic/authenticateUser.test.js | 14 + .../socialcode/api/logic/createPost.js | 43 + .../socialcode/api/logic/createPost.test.js | 14 + .../socialcode/api/logic/deletePost.js | 55 + .../socialcode/api/logic/deletePost.test.js | 15 + .../socialcode/api/logic/getAllPosts.js | 34 + .../socialcode/api/logic/getAllPosts.test.js | 15 + .../socialcode/api/logic/getUserName.js | 41 + .../socialcode/api/logic/getUserName.test.js | 15 + .../socialcode/api/logic/index.js | 19 + .../socialcode/api/logic/index.test.js | 15 + .../socialcode/api/logic/registerUser.js | 57 + .../socialcode/api/logic/registerUser.test.js | 16 + .../socialcode/api/package-lock.json | 847 ++++ .../angel-patino/socialcode/api/package.json | 23 + .../socialcode/api/test/register-user.sh | 1 + .../angel-patino/socialcode/app/.eslintrc.cjs | 21 + staff/angel-patino/socialcode/app/.gitignore | 24 + staff/angel-patino/socialcode/app/README.md | 8 + staff/angel-patino/socialcode/app/index.html | 16 + .../socialcode/app/package-lock.json | 4296 +++++++++++++++++ .../angel-patino/socialcode/app/package.json | 27 + .../socialcode/app/public/vite.svg | 1 + .../app/{login/index.css => src/App.css} | 0 staff/angel-patino/socialcode/app/src/App.jsx | 26 + .../app/{ => src}/components/core/Button.css | 0 .../app/src/components/core/Button.jsx | 7 + .../app/{ => src}/components/core/Field.css | 0 .../app/src/components/core/Field.jsx | 13 + .../app/{ => src}/components/core/Form.css | 0 .../app/src/components/core/Form.jsx | 7 + .../app/src/components/core/Heading.jsx | 7 + .../app/{ => src}/components/core/Image.css | 0 .../app/src/components/core/Image.jsx | 7 + .../app/{ => src}/components/core/Input.css | 0 .../app/src/components/core/Input.jsx | 7 + .../app/src/components/core/Label.css | 3 + .../app/src/components/core/Label.jsx | 7 + .../app/src/components/core/Link.jsx | 9 + .../components/core/SubmitButton.css | 0 .../app/src/components/core/SubmitButton.jsx | 8 + .../app/src/components/core/Text.css | 3 + .../app/src/components/core/Text.jsx | 7 + .../app/src/components/core/Time.jsx | 6 + .../app/src/components/core/Title.jsx | 7 + .../components/library/FormWithFeedback.css | 0 .../components/library/FormWithFeedback.jsx | 8 + .../app/src/components/library/View.css | 6 + .../app/src/components/library/View.jsx | 6 + .../angel-patino/socialcode/app/src/errors.js | 40 + .../angel-patino/socialcode/app/src/index.css | 16 + .../angel-patino/socialcode/app/src/logic.js | 236 + .../angel-patino/socialcode/app/src/main.jsx | 10 + .../socialcode/app/src/views/Home.jsx | 67 + .../socialcode/app/src/views/Login.jsx | 58 + .../socialcode/app/src/views/Register.jsx | 71 + .../src/views/components/CreatePostForm.jsx | 70 + .../app/src/views/components/Footer.css | 0 .../app/src/views/components/Footer.jsx | 13 + .../app/src/views/components/Header.css | 12 + .../app/src/views/components/Header.jsx | 9 + .../app/src/views/components/Post.jsx | 47 + .../app/src/views/components/PostList.css | 3 + .../app/src/views/components/PostList.jsx | 53 + .../socialcode/app/vite.config.js | 7 + .../{app => app1}/components/Component.js | 0 .../app1/components/core/Button.css | 8 + .../{app => app1}/components/core/Button.js | 0 .../socialcode/app1/components/core/Field.css | 5 + .../{app => app1}/components/core/Field.js | 0 .../socialcode/app1/components/core/Form.css | 5 + .../{app => app1}/components/core/Form.js | 0 .../{app => app1}/components/core/Heading.js | 0 .../socialcode/app1/components/core/Image.css | 3 + .../{app => app1}/components/core/Image.js | 0 .../socialcode/app1/components/core/Input.css | 6 + .../{app => app1}/components/core/Input.js | 0 .../{app => app1}/components/core/Label.js | 0 .../{app => app1}/components/core/Link.js | 0 .../app1/components/core/SubmitButton.css | 3 + .../components/core/SubmitButton.js | 0 .../components/library/FormWithFeedback.css | 11 + .../components/library/FormWithFeedback.js | 0 .../socialcode/{app => app1}/data.js | 0 .../socialcode/{app => app1}/errors.js | 0 .../socialcode/{app => app1}/global.css | 0 .../{app => app1}/home/components/Confirm.css | 0 .../{app => app1}/home/components/Confirm.js | 0 .../home/components/CreatePostForm.css | 0 .../home/components/CreatePostForm.js | 0 .../{app => app1}/home/components/Post.js | 0 .../{app => app1}/home/components/PostList.js | 0 .../socialcode/{app => app1}/home/index.css | 0 .../socialcode/{app => app1}/home/index.html | 0 .../socialcode/{app => app1}/home/index.js | 0 .../socialcode/{app => app1}/logic.js | 0 .../{app => app1}/login/LoginForm.js | 0 .../{app/register => app1/login}/index.css | 0 .../socialcode/{app => app1}/login/index.html | 0 .../socialcode/{app => app1}/login/index.js | 0 .../{app => app1}/register/RegisterForm.js | 0 .../socialcode/app1/register/index.css | 5 + .../{app => app1}/register/index.html | 0 .../{app => app1}/register/index.js | 0 staff/angel-patino/socialcode/com/errors.js | 48 + staff/angel-patino/socialcode/com/index.js | 1 + .../angel-patino/socialcode/com/package.json | 13 + staff/angel-patino/socialcode/com/validate.js | 66 + 126 files changed, 7625 insertions(+) create mode 100644 staff/angel-patino/socialcode/_api1/README.md create mode 100644 staff/angel-patino/socialcode/_api1/data/index.js create mode 100644 staff/angel-patino/socialcode/_api1/data/index.test.js create mode 100644 staff/angel-patino/socialcode/_api1/data/posts.json create mode 100644 staff/angel-patino/socialcode/_api1/data/users.json create mode 100644 staff/angel-patino/socialcode/_api1/errors.js create mode 100644 staff/angel-patino/socialcode/_api1/logic/index.js create mode 100644 staff/angel-patino/socialcode/_api1/logic/index.test.js create mode 100644 staff/angel-patino/socialcode/_api1/package-lock.json create mode 100644 staff/angel-patino/socialcode/api/.env create mode 100644 staff/angel-patino/socialcode/api/.gitignore create mode 100644 staff/angel-patino/socialcode/api/README.md create mode 100644 staff/angel-patino/socialcode/api/data/index.js create mode 100644 staff/angel-patino/socialcode/api/data/index.test.js create mode 100644 staff/angel-patino/socialcode/api/data/posts.json create mode 100644 staff/angel-patino/socialcode/api/data/users.json create mode 100644 staff/angel-patino/socialcode/api/index.js create mode 100644 staff/angel-patino/socialcode/api/logic/authenticateUser.js create mode 100644 staff/angel-patino/socialcode/api/logic/authenticateUser.test.js create mode 100644 staff/angel-patino/socialcode/api/logic/createPost.js create mode 100644 staff/angel-patino/socialcode/api/logic/createPost.test.js create mode 100644 staff/angel-patino/socialcode/api/logic/deletePost.js create mode 100644 staff/angel-patino/socialcode/api/logic/deletePost.test.js create mode 100644 staff/angel-patino/socialcode/api/logic/getAllPosts.js create mode 100644 staff/angel-patino/socialcode/api/logic/getAllPosts.test.js create mode 100644 staff/angel-patino/socialcode/api/logic/getUserName.js create mode 100644 staff/angel-patino/socialcode/api/logic/getUserName.test.js create mode 100644 staff/angel-patino/socialcode/api/logic/index.js create mode 100644 staff/angel-patino/socialcode/api/logic/index.test.js create mode 100644 staff/angel-patino/socialcode/api/logic/registerUser.js create mode 100644 staff/angel-patino/socialcode/api/logic/registerUser.test.js create mode 100644 staff/angel-patino/socialcode/api/package-lock.json create mode 100644 staff/angel-patino/socialcode/api/package.json create mode 100644 staff/angel-patino/socialcode/api/test/register-user.sh create mode 100644 staff/angel-patino/socialcode/app/.eslintrc.cjs create mode 100644 staff/angel-patino/socialcode/app/.gitignore create mode 100644 staff/angel-patino/socialcode/app/README.md create mode 100644 staff/angel-patino/socialcode/app/index.html create mode 100644 staff/angel-patino/socialcode/app/package-lock.json create mode 100644 staff/angel-patino/socialcode/app/package.json create mode 100644 staff/angel-patino/socialcode/app/public/vite.svg rename staff/angel-patino/socialcode/app/{login/index.css => src/App.css} (100%) create mode 100644 staff/angel-patino/socialcode/app/src/App.jsx rename staff/angel-patino/socialcode/app/{ => src}/components/core/Button.css (100%) create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Button.jsx rename staff/angel-patino/socialcode/app/{ => src}/components/core/Field.css (100%) create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Field.jsx rename staff/angel-patino/socialcode/app/{ => src}/components/core/Form.css (100%) create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Form.jsx create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Heading.jsx rename staff/angel-patino/socialcode/app/{ => src}/components/core/Image.css (100%) create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Image.jsx rename staff/angel-patino/socialcode/app/{ => src}/components/core/Input.css (100%) create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Input.jsx create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Label.css create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Label.jsx create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Link.jsx rename staff/angel-patino/socialcode/app/{ => src}/components/core/SubmitButton.css (100%) create mode 100644 staff/angel-patino/socialcode/app/src/components/core/SubmitButton.jsx create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Text.css create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Text.jsx create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Time.jsx create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Title.jsx rename staff/angel-patino/socialcode/app/{ => src}/components/library/FormWithFeedback.css (100%) create mode 100644 staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx create mode 100644 staff/angel-patino/socialcode/app/src/components/library/View.css create mode 100644 staff/angel-patino/socialcode/app/src/components/library/View.jsx create mode 100644 staff/angel-patino/socialcode/app/src/errors.js create mode 100644 staff/angel-patino/socialcode/app/src/index.css create mode 100644 staff/angel-patino/socialcode/app/src/logic.js create mode 100644 staff/angel-patino/socialcode/app/src/main.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/Home.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/Login.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/Register.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/components/CreatePostForm.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/components/Footer.css create mode 100644 staff/angel-patino/socialcode/app/src/views/components/Footer.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/components/Header.css create mode 100644 staff/angel-patino/socialcode/app/src/views/components/Header.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/components/Post.jsx create mode 100644 staff/angel-patino/socialcode/app/src/views/components/PostList.css create mode 100644 staff/angel-patino/socialcode/app/src/views/components/PostList.jsx create mode 100644 staff/angel-patino/socialcode/app/vite.config.js rename staff/angel-patino/socialcode/{app => app1}/components/Component.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/components/core/Button.css rename staff/angel-patino/socialcode/{app => app1}/components/core/Button.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/components/core/Field.css rename staff/angel-patino/socialcode/{app => app1}/components/core/Field.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/components/core/Form.css rename staff/angel-patino/socialcode/{app => app1}/components/core/Form.js (100%) rename staff/angel-patino/socialcode/{app => app1}/components/core/Heading.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/components/core/Image.css rename staff/angel-patino/socialcode/{app => app1}/components/core/Image.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/components/core/Input.css rename staff/angel-patino/socialcode/{app => app1}/components/core/Input.js (100%) rename staff/angel-patino/socialcode/{app => app1}/components/core/Label.js (100%) rename staff/angel-patino/socialcode/{app => app1}/components/core/Link.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/components/core/SubmitButton.css rename staff/angel-patino/socialcode/{app => app1}/components/core/SubmitButton.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/components/library/FormWithFeedback.css rename staff/angel-patino/socialcode/{app => app1}/components/library/FormWithFeedback.js (100%) rename staff/angel-patino/socialcode/{app => app1}/data.js (100%) rename staff/angel-patino/socialcode/{app => app1}/errors.js (100%) rename staff/angel-patino/socialcode/{app => app1}/global.css (100%) rename staff/angel-patino/socialcode/{app => app1}/home/components/Confirm.css (100%) rename staff/angel-patino/socialcode/{app => app1}/home/components/Confirm.js (100%) rename staff/angel-patino/socialcode/{app => app1}/home/components/CreatePostForm.css (100%) rename staff/angel-patino/socialcode/{app => app1}/home/components/CreatePostForm.js (100%) rename staff/angel-patino/socialcode/{app => app1}/home/components/Post.js (100%) rename staff/angel-patino/socialcode/{app => app1}/home/components/PostList.js (100%) rename staff/angel-patino/socialcode/{app => app1}/home/index.css (100%) rename staff/angel-patino/socialcode/{app => app1}/home/index.html (100%) rename staff/angel-patino/socialcode/{app => app1}/home/index.js (100%) rename staff/angel-patino/socialcode/{app => app1}/logic.js (100%) rename staff/angel-patino/socialcode/{app => app1}/login/LoginForm.js (100%) rename staff/angel-patino/socialcode/{app/register => app1/login}/index.css (100%) rename staff/angel-patino/socialcode/{app => app1}/login/index.html (100%) rename staff/angel-patino/socialcode/{app => app1}/login/index.js (100%) rename staff/angel-patino/socialcode/{app => app1}/register/RegisterForm.js (100%) create mode 100644 staff/angel-patino/socialcode/app1/register/index.css rename staff/angel-patino/socialcode/{app => app1}/register/index.html (100%) rename staff/angel-patino/socialcode/{app => app1}/register/index.js (100%) create mode 100644 staff/angel-patino/socialcode/com/errors.js create mode 100644 staff/angel-patino/socialcode/com/index.js create mode 100644 staff/angel-patino/socialcode/com/package.json create mode 100644 staff/angel-patino/socialcode/com/validate.js diff --git a/staff/angel-patino/socialcode/_api1/README.md b/staff/angel-patino/socialcode/_api1/README.md new file mode 100644 index 000000000..23be0205c --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/README.md @@ -0,0 +1,41 @@ +- list users + +```sh +🐖 curl http://localhost:8080/users -v +``` + +- register user + +```sh +🐖 curl -X POST http://localhost:8080/users -H "Content-Type: application/json" -d '{"name":"Pepito","surname":"Grillo","email":"pepito@grillo.com","username":"pepitogrillo","password":"123123123","passwordRepeat":"123123123"}' -v +``` + +- authenticate user + +```sh +🐖 curl -X POST http://localhost:8080/users/auth -H "Content-Type: application/json" -d '{"username":"pepitogrillo","password":"123123123"}' -v +``` + +- get user name + +```sh +🐖 curl http://localhost:8080/users/pepitogrillo -H "Authorization: Basic peterpan" -v +``` + +- list posts + +```sh +🐖 curl http://localhost:8080/posts -v +``` + +- create post + +```sh +🐖 curl -X POST http://localhost:8080/posts -H "Authorization: Basic peterpan" -H "Content-Type: application/json" -d '{"title":"blah","image":"https://upload.wikimedia.org/wikipedia/commons/1/1d/Blah_Blah_Blah.jpg","description":"blah blah"}' -v +``` + +- delete post + +```sh +🐖 curl -X DELETE http://localhost:8080/posts/8826114904894882-1716924151129 -H "Authorization: Basic peterpan" -v +``` diff --git a/staff/angel-patino/socialcode/_api1/data/index.js b/staff/angel-patino/socialcode/_api1/data/index.js new file mode 100644 index 000000000..282c25d5f --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/data/index.js @@ -0,0 +1,150 @@ +import fs from 'fs' +import { SystemError } from '../errors.js' + +const data = {} + +data.findUser = (condition, callback) => { + fs.readFile('./data/users.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const users = JSON.parse(json) + + const user = users.find(condition) + + callback(null, user) + }) +} + +data.insertUser = (user, callback) => { + fs.readFile('./data/users.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const users = JSON.parse(json) + + users.push(user) + + const newJson = JSON.stringify(users) + + fs.writeFile('./data/users.json', newJson, error => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + callback(null) + }) + }) +} + +data.findPosts = (condition, callback) => { + fs.readFile('./data/posts.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + const filtered = posts.filter(condition) + + callback(null, filtered) + }) +} + +data.findPost = (condition, callback) => { + fs.readFile('./data/posts.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + const post = posts.find(condition) + + callback(null, post) + }) +} + +data.insertPost = (post, callback) => { + fs.readFile('./data.posts.json', 'utf8', (error, json) => { + if (eror) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + post.id = `${Math.random().toString().slice(2)}-${Date.now()}` + + posts.push(post) + + const newJson = JSON.stringify(posts) + + fs.writeFile('./data/posts.json', newJson, error => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + callback(null) + }) + }) +} + +data.deletePost = (condition, callback) => { + fs.readFile('./data/posts.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + const index = posts.findIndex(condition) + + if (index > -1) { + posts.splice(index, 1) + + const newJson = JSON.stringify(posts) + + fs.writeFile('./data/posts.json', newJson, error => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + callback(null) + }) + } else callback(null) + }) +} + +export default data \ No newline at end of file diff --git a/staff/angel-patino/socialcode/_api1/data/index.test.js b/staff/angel-patino/socialcode/_api1/data/index.test.js new file mode 100644 index 000000000..0401edcd9 --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/data/index.test.js @@ -0,0 +1,11 @@ +import data from './index.js' + +data.deletePost(post => post.title === 'smile 2', error => { + if (error) { + console.error(error) + + return + } + + console.log('post deleted') +}) \ No newline at end of file diff --git a/staff/angel-patino/socialcode/_api1/data/posts.json b/staff/angel-patino/socialcode/_api1/data/posts.json new file mode 100644 index 000000000..799642e5c --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/data/posts.json @@ -0,0 +1 @@ +[{"id":"5838445445732432-1716838752497","author":"peterpan","title":"smile","image":"https://m.media-amazon.com/images/I/4199zzjb7KS._AC_US750_.jpg","description":"=)","date":"2024-05-27T19:39:12.497Z"},{"id":"9479665295277597-1716918092784","author":"peterpan","title":"smile","image":"https://static.vecteezy.com/system/resources/thumbnails/002/592/172/small_2x/smile-emoji-pop-art-line-style-icon-free-vector.jpg","description":":)","date":"2024-05-28T17:41:29.807Z"},{"id":"319312588649324-1716922581404","author":"peterpan","title":"hello world","image":"https://miro.medium.com/v2/resize:fit:1024/1*OohqW5DGh9CQS4hLY5FXzA.png","description":"console.log(\"hello world\")","date":"2024-05-28T18:56:21.403Z"},{"author":"peterpan","title":"i love javascript","image":"https://ih1.redbubble.net/image.350413705.3778/st,small,507x507-pad,600x600,f8f8f8.u5.jpg","description":"i <3 js","date":"2024-05-28T19:20:28.064Z","id":"472418913117465-1716924028065"},{"author":"peterpan","title":"i love debugging","image":"https://m.media-amazon.com/images/I/A1nYNISnPeL._CLa%7C2140%2C2000%7CA1vRVLLQpmL.png%7C0%2C0%2C2140%2C2000%2B0.0%2C0.0%2C2140.0%2C2000.0_AC_UY1000_.png","description":"yeahhh!","date":"2024-05-28T19:22:31.127Z","id":"8826114904894882-1716924151127"},{"author":"pepitogrillo","title":"here me","image":"https://m.media-amazon.com/images/I/71mkTfMrVGL.jpg","description":":P","date":"2024-05-29T19:14:02.892Z","id":"2553895236558785-1717010042893"},{"author":"wendydarling","title":"so nice steps to success","image":"https://upload.wikimedia.org/wikipedia/en/thumb/1/18/DisneyWendy.JPG/250px-DisneyWendy.JPG","description":"=)","date":"2024-05-29T19:37:38.949Z","id":"7952574083575106-1717011458949"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"yeah","date":"2024-06-03T18:48:18.824Z","id":"9579635771106894-1717440498824"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"2","date":"2024-06-03T18:50:08.017Z","id":"9328445310908826-1717440608018"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"2","date":"2024-06-03T18:51:40.989Z","id":"0016783604479264636-1717440700989"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"3","date":"2024-06-03T18:55:56.251Z","id":"06762123104443174-1717440956251"}] \ No newline at end of file diff --git a/staff/angel-patino/socialcode/_api1/data/users.json b/staff/angel-patino/socialcode/_api1/data/users.json new file mode 100644 index 000000000..3820d80e0 --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/data/users.json @@ -0,0 +1 @@ +[{"name":"Pepito","surname":"Grillo","email":"pepito@grillo.com","username":"pepitogrillo","password":"123123123"},{"name":"Peter","surname":"Pan","email":"peter@pan.com","username":"peterpan","password":"123123123"},{"name":"Wendy","surname":"Darling","email":"wendy@darling.com","username":"wendydarling","password":"123123123"},{"name":"Pin","surname":"Ocho","email":"pin@ocho.com","username":"pinocho","password":"123123123"},{"name":"Ele","surname":"Fante","email":"ele@fante.com","username":"elefante","password":"123123123"},{"name":"Le","surname":"On","email":"le@on.com","username":"leon","password":"123123123"}] \ No newline at end of file diff --git a/staff/angel-patino/socialcode/_api1/errors.js b/staff/angel-patino/socialcode/_api1/errors.js new file mode 100644 index 000000000..6e205f9e6 --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/errors.js @@ -0,0 +1,39 @@ +class ContentError extends Error { + constructor(message) { + super(message) + + //this.name = ContentError.name + this.name = this.constructor.name + } +} + +class MatchError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class DuplicityError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class SystemError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +export { + ContentError, + MatchError, + DuplicityError, + SystemError +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/_api1/logic/index.js b/staff/angel-patino/socialcode/_api1/logic/index.js new file mode 100644 index 000000000..81f348176 --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/logic/index.js @@ -0,0 +1,259 @@ +import data from '../data/index.js' +import { ContentError, DuplicityError, MatchError } from '../errors.js' + +const logic = {} + +const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +const USERNAME_REGEX = /^[\w-]+$/ +const PASSWORD_REGEX = /^[\w-$%&=\[\]\{\}\<\>\(\)]{8,}$/ + +const NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ + +const ID_REGEX = /^[0-9]+-[0-9]+$/ + +logic.registerUser = (name, surname, email, username, password, passwordRepeat, callback) => { + if (!NAME_REGEX.test(name)) + throw new ContentError('name is not valid') + + if (!NAME_REGEX.test(surname)) + throw new ContentError('surname is not valid') + + if (!EMAIL_REGEX.test(email)) + throw new ContentError('email is not valid') + + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!PASSWORD_REGEX.test(password)) + throw new ContentError('password is not valid') + + if (password !== passwordRepeat) + throw new MatchError('passwords don\'t match') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + data.findUser(user => user.email === email || user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (user) { + callback(new DuplicityError('user already exists')) + + return + } + + const newUser = { + name: name, + surname: surname, + email: email, + username: username, + password: password + } + + data.insertUser(newUser, error => { + if (error) { + callback(error) + + return + } + + callback(null) + }) + }) +} + +logic.authenticateUser = (username, password, callback) => { + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!PASSWORD_REGEX.test(password)) + throw new ContentError('password is not valid') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + if (user.password !== password) { + callback(new MatchError('wrong password')) + + return + } + + callback(null) + }) +} + +logic.getUserName = (username, targetUsername, callback) => { + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!USERNAME_REGEX.test(targetUsername)) + throw new ContentError('targetUsername is not valid') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + data.findUser(user => user.username === targetUsername, (error, targetUser) => { + if (error) { + callback(error) + + return + } + + if (!targetUser) { + callback(new MatchError('targetUser not found')) + + return + } + + callback(null, targetUser.name) + }) + }) +} + +logic.getAllPosts = callback => { + data.findPosts(() => true, (error, posts) => { + if (error) { + callback(error) + + return + } + + callback(null, posts.reverse()) + }) +} + +logic.createPost = (username, title, image, description, callback) => { + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (typeof title !== 'string' || !title.length || title.length > 50) + throw new ContentError('title is not valid') + + if (typeof image !== 'string' || !image.startsWith('http')) + throw new ContentError('image is not valid') + + if (typeof description !== 'string' || !description.length || description.length > 200) + throw new ContentError('description is not valid') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + const post = { + author: username, + title, + image, + description, + date: new Date().toISOString() + } + + data.insertPost(post, error => { + if (error) { + callback(error) + + return + } + + callback(null) + }) + }) +} + +logic.deletePost = (username, postId, callback) => { + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!ID_REGEX.test(postId)) + throw new ContentError('postId is not valid') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + data.findPost(post => post.id === postId, (error, post) => { + if (error) { + callback(error) + + return + } + + if (!post) { + callback(new MatchError('post not found')) + + return + } + + if (post.author !== username) { + callback(new MatchError('post author does not match user')) + + return + } + + data.deletePost(post => post.id === postId, error => { + if (error) { + callback(error) + + return + } + + callback(null) + }) + }) + }) +} + +export default logic \ No newline at end of file diff --git a/staff/angel-patino/socialcode/_api1/logic/index.test.js b/staff/angel-patino/socialcode/_api1/logic/index.test.js new file mode 100644 index 000000000..8faf67a46 --- /dev/null +++ b/staff/angel-patino/socialcode/_api1/logic/index.test.js @@ -0,0 +1,15 @@ +import logic from './index.js' + +try { + logic.getUserName('peterpan', 'pepitogrillo', (error, name) => { + if (error) { + console.error(error) + + return + } + + console.log('user name retrieved', name) + }) +} catch (error) { + console.error(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/_api1/package-lock.json b/staff/angel-patino/socialcode/_api1/package-lock.json new file mode 100644 index 000000000..e69de29bb diff --git a/staff/angel-patino/socialcode/api/.env b/staff/angel-patino/socialcode/api/.env new file mode 100644 index 000000000..f36b229a8 --- /dev/null +++ b/staff/angel-patino/socialcode/api/.env @@ -0,0 +1,2 @@ +PORT = 9010 +JWT_SECRET = peter and wendy have a rollete \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/.gitignore b/staff/angel-patino/socialcode/api/.gitignore new file mode 100644 index 000000000..8f7b39f43 --- /dev/null +++ b/staff/angel-patino/socialcode/api/.gitignore @@ -0,0 +1,2 @@ +node_modules +!.env \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/README.md b/staff/angel-patino/socialcode/api/README.md new file mode 100644 index 000000000..b067dacc0 --- /dev/null +++ b/staff/angel-patino/socialcode/api/README.md @@ -0,0 +1,29 @@ +- authenticate user + +```sh +🐖 curl -X POST http://localhost:8080/users/auth -H "Content-Type: application/json" -d '{"username":"pepitogrillo","password":"123123123"}' -v +``` + +- get user name + +```sh +🐖 curl http://localhost:8080/users/pepitogrillo -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3ZW5keWRhcmxpbmciLCJpYXQiOjE3MTg2NTExMDAsImV4cCI6MTcxODY1NDcwMH0.XcfzV8eMk7dv8kawAArnIqbECPkv2dZI8px2zKbI8s4" -v +``` + +- get all posts + +```sh +🐖 curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwZXBpdG9ncmlsbG8iLCJpYXQiOjE3MTg3MzMyOTAsImV4cCI6MTcxODczNjg5MH0.wRhT3s-62X7hS3oxH0cMAulEy7smhzyVpibkvfLjXFQ" http://localhost:8080/posts -v +``` + +- create post + +```sh +🐖 curl -X POST http://localhost:8080/posts -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwZXBpdG9ncmlsbG8iLCJpYXQiOjE3MTg2NTI4OTgsImV4cCI6MTcxODY1NjQ5OH0.cVvHfJ_cE-dDMgD9UduooRkPG6Dxa09PRKZnABDxj2I" -H "Content-Type: application/json" -d '{"title":"blah","image":"https://upload.wikimedia.org/wikipedia/commons/1/1d/Blah_Blah_Blah.jpg","description":"blah blah"}' -v +``` + +- delete post + +```sh +🐖 curl -X DELETE http://localhost:8080/posts/8826114904894882-1716924151129 -H "Authorization: Basic peterpan" -v +``` diff --git a/staff/angel-patino/socialcode/api/data/index.js b/staff/angel-patino/socialcode/api/data/index.js new file mode 100644 index 000000000..2456202c1 --- /dev/null +++ b/staff/angel-patino/socialcode/api/data/index.js @@ -0,0 +1,150 @@ +import fs from 'fs' +import { SystemError } from 'com/errors.js' + +const data = {} + +data.findUser = (condition, callback) => { + fs.readFile('./data/users.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const users = JSON.parse(json) + + const user = users.find(condition) + + callback(null, user) + }) +} + +data.insertUser = (user, callback) => { + fs.readFile('./data/users.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const users = JSON.parse(json) + + users.push(user) + + const newJson = JSON.stringify(users) + + fs.writeFile('./data/users.json', newJson, error => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + callback(null) + }) + }) +} + +data.findPosts = (condition, callback) => { + fs.readFile('./data/posts.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + const filtered = posts.filter(condition) + + callback(null, filtered) + }) +} + +data.findPost = (condition, callback) => { + fs.readFile('./data/posts.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + const post = posts.find(condition) + + callback(null, post) + }) +} + +data.insertPost = (post, callback) => { + fs.readFile('./data.posts.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + post.id = `${Math.random().toString().slice(2)}-${Date.now()}` + + posts.push(post) + + const newJson = JSON.stringify(posts) + + fs.writeFile('./data/posts.json', newJson, error => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + callback(null) + }) + }) +} + +data.deletePost = (condition, callback) => { + fs.readFile('./data/posts.json', 'utf8', (error, json) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!json) json = '[]' + + const posts = JSON.parse(json) + + const index = posts.findIndex(condition) + + if (index > -1) { + posts.splice(index, 1) + + const newJson = JSON.stringify(posts) + + fs.writeFile('./data/posts.json', newJson, error => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + callback(null) + }) + } else callback(null) + }) +} + +export default data \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/data/index.test.js b/staff/angel-patino/socialcode/api/data/index.test.js new file mode 100644 index 000000000..0401edcd9 --- /dev/null +++ b/staff/angel-patino/socialcode/api/data/index.test.js @@ -0,0 +1,11 @@ +import data from './index.js' + +data.deletePost(post => post.title === 'smile 2', error => { + if (error) { + console.error(error) + + return + } + + console.log('post deleted') +}) \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/data/posts.json b/staff/angel-patino/socialcode/api/data/posts.json new file mode 100644 index 000000000..799642e5c --- /dev/null +++ b/staff/angel-patino/socialcode/api/data/posts.json @@ -0,0 +1 @@ +[{"id":"5838445445732432-1716838752497","author":"peterpan","title":"smile","image":"https://m.media-amazon.com/images/I/4199zzjb7KS._AC_US750_.jpg","description":"=)","date":"2024-05-27T19:39:12.497Z"},{"id":"9479665295277597-1716918092784","author":"peterpan","title":"smile","image":"https://static.vecteezy.com/system/resources/thumbnails/002/592/172/small_2x/smile-emoji-pop-art-line-style-icon-free-vector.jpg","description":":)","date":"2024-05-28T17:41:29.807Z"},{"id":"319312588649324-1716922581404","author":"peterpan","title":"hello world","image":"https://miro.medium.com/v2/resize:fit:1024/1*OohqW5DGh9CQS4hLY5FXzA.png","description":"console.log(\"hello world\")","date":"2024-05-28T18:56:21.403Z"},{"author":"peterpan","title":"i love javascript","image":"https://ih1.redbubble.net/image.350413705.3778/st,small,507x507-pad,600x600,f8f8f8.u5.jpg","description":"i <3 js","date":"2024-05-28T19:20:28.064Z","id":"472418913117465-1716924028065"},{"author":"peterpan","title":"i love debugging","image":"https://m.media-amazon.com/images/I/A1nYNISnPeL._CLa%7C2140%2C2000%7CA1vRVLLQpmL.png%7C0%2C0%2C2140%2C2000%2B0.0%2C0.0%2C2140.0%2C2000.0_AC_UY1000_.png","description":"yeahhh!","date":"2024-05-28T19:22:31.127Z","id":"8826114904894882-1716924151127"},{"author":"pepitogrillo","title":"here me","image":"https://m.media-amazon.com/images/I/71mkTfMrVGL.jpg","description":":P","date":"2024-05-29T19:14:02.892Z","id":"2553895236558785-1717010042893"},{"author":"wendydarling","title":"so nice steps to success","image":"https://upload.wikimedia.org/wikipedia/en/thumb/1/18/DisneyWendy.JPG/250px-DisneyWendy.JPG","description":"=)","date":"2024-05-29T19:37:38.949Z","id":"7952574083575106-1717011458949"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"yeah","date":"2024-06-03T18:48:18.824Z","id":"9579635771106894-1717440498824"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"2","date":"2024-06-03T18:50:08.017Z","id":"9328445310908826-1717440608018"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"2","date":"2024-06-03T18:51:40.989Z","id":"0016783604479264636-1717440700989"},{"author":"peterpan","title":"i love cors","image":"https://i.fbcd.co/products/resized/resized-750-500/error-404-love-not-found-1-a8475b183ad4b00571288e1ab29da1100a45da6220d658efa7989dd0c45d90ae.jpg","description":"3","date":"2024-06-03T18:55:56.251Z","id":"06762123104443174-1717440956251"}] \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/data/users.json b/staff/angel-patino/socialcode/api/data/users.json new file mode 100644 index 000000000..1b1791b4b --- /dev/null +++ b/staff/angel-patino/socialcode/api/data/users.json @@ -0,0 +1,26 @@ +[ + { + "name":"Pepito","surname":"Grillo","email":"pepito@grillo.com","username":"pepitogrillo","password":"123123123" + }, + { + "name":"Peter","surname":"Pan","email":"peter@pan.com","username":"peterpan","password":"123123123" + }, + { + "name":"Wendy","surname":"Darling","email":"wendy@darling.com","username":"wendydarling","password":"123123123" + }, + { + "name":"Pin","surname":"Ocho","email":"pin@ocho.com","username":"pinocho","password":"123123123" + }, + { + "name":"Ele","surname":"Fante","email":"ele@fante.com","username":"elefante","password":"123123123" + }, + { + "name":"Le","surname":"On","email":"le@on.com","username":"leon","password":"123123123" + }, + { + "name":"man","surname":"zana","email":"man@zana.com","username":"manzana","password":"123qweasd" + }, + { + "name":"P","surname":"era","email":"p@era.com","username":"Pera","password":"qweqweqwe" + } +] \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/index.js b/staff/angel-patino/socialcode/api/index.js new file mode 100644 index 000000000..29bf32ea6 --- /dev/null +++ b/staff/angel-patino/socialcode/api/index.js @@ -0,0 +1,198 @@ +import 'dotenv/config' +import express from 'express' +import logic from './logic/index.js' +import cors from 'cors' +import jwt from 'jsonwebtoken' +import { SystemError } from 'com/errors.js' + +const { PORT, JWT_SECRET } = process.env + +const { JsonWebTokenError, TokenExpiredError } = jwt + +const api = express() + +api.use(express.static('public')) + +api.use(cors()) + +api.get('/', (req, res) => res.send('Hello, World!')) + +const jsonBodyParser = express.json({ strict: true, type: 'application/json' }) + +api.post('/users', jsonBodyParser, (req, res) => { + const { name, surname, email, username, password, passwordRepeat } = req.body + + try { + logic.registerUser(name, surname, email, username, password, passwordRepeat, error => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(201).send() + }) + } catch (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + } +}) + + + +api.post('/users/auth', jsonBodyParser, (req, res) => { + const { username, password } = req.body + + try { + logic.authenticateUser(username, password, error => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + jwt.sign({ sub: username }, JWT_SECRET, { expiresIn: '1h ' }, (error, token) => { + if (erorr) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.json(token) + }) + }) + } catch (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + } +}) + +api.get('/users/:targetUsername', (req, res) => { + try { + const token = req.headers.authorization.slice(7) + + jwt.verify(token, JWT_SECRET, (error, payload) => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { targetUsername } = req.params + + logic.getUserName(username, targetUsername, (error, name) => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.json(name) + }) + + }) + } catch (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else + res.status(500).json({ error: error.constructor.name, message: error.message }) + } +}) + +api.get('/posts', (req, res) => { + try { + const token = req.headers.authorization.slice(7) + + jwt.verify(token, JWT_SECRET, (error, payload) => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: erorr.message }) + + return + } + + const { sub: username } = payload + + logic.getAllPosts(username, (error, posts) => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.json(posts) + }) + }) + } catch (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: error.constructor.name, message: error.message }) + else + res.status(500).json({ error: error.constructor.name, message: error.message }) + } +}) + +api.post('/posts', jsonBodyParser, (req, res) => { + try { + const token = req.headers.authorization.slice(7) + + jwt.verify(token, JWT_SECRET, (error, payload) => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { title, image, description } = req.body + + logic.createPost(username, title, image, description, error => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(201).send() + }) + }) + } catch (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else + res.status(500).json({ error: error.constructor.name, message: error.message }) + } +}) +api.delete('/posts/:postId', (req, res) => { + try { + const token = req.headers.authorization.slice(7) + + jwt.verify(token, JWT_SECRET, (error, payload) => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { postId } = req.params + + logic.deletePost(username, postId, error => { + if (error) { + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(204).send() + }) + }) + } catch (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else + res.status(500).json({ error: error.constructor.name, message: error.message }) + } +}) + +api.listen(PORT, () => console.log(`API running on PORT ${PORT}`)) \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/authenticateUser.js b/staff/angel-patino/socialcode/api/logic/authenticateUser.js new file mode 100644 index 000000000..364871e3f --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/authenticateUser.js @@ -0,0 +1,42 @@ +import data from '../data/index.js' +import { MatchError, SystemError } from 'com/errors.js' +import validate from 'com/vaildate.js' +import bcrypt from 'bcryptjs' + +const authenticateUser = (username, password, callback) => { + validate.username(username) + validate.passwors(password) + validate.callback(callback) + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + bcrypt.compare(password, user.password, (error, match) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + if (!match) { + callback(new MatchError('wrong password')) + + return + } + + callback(null) + }) + }) +} + +export default authenticateUser \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/authenticateUser.test.js b/staff/angel-patino/socialcode/api/logic/authenticateUser.test.js new file mode 100644 index 000000000..865087f09 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/authenticateUser.test.js @@ -0,0 +1,14 @@ +import logic from "./index.js" + +try { + logic.authenticateUser('leon', '123123123', error => { + if (error) { + console.error(error) + + return + } + console.log('user authenticated') + }) +} catch (error) { + console.errror(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/createPost.js b/staff/angel-patino/socialcode/api/logic/createPost.js new file mode 100644 index 000000000..2ccbfc099 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/createPost.js @@ -0,0 +1,43 @@ +import data from '../data.index.js' +import { MatchError } from 'com/errors.js' +import validate from 'com/validate.js' + +const createPost = (username, title, image, description, callback) => { + validate.username(username) + validate.text(title, 'title', 50) + validate.url(image, 'image') + validate.text(description, 'description', 200) + validate.callback(callback) + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + return + } + + if (!user) { + callback(new MatchError('user not foung')) + + return + } + + const post = { + author: username, + title, + image, + description, + date: new Date().toISOString() + } + + data.insertPost(post, error => { + if (error) { + callback(error) + + return + } + callback(callback) + }) + }) +} + +export default createPost \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/createPost.test.js b/staff/angel-patino/socialcode/api/logic/createPost.test.js new file mode 100644 index 000000000..31cc1a32a --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/createPost.test.js @@ -0,0 +1,14 @@ +import logic from "./index.js" + +try { + logic.createPost('peterpan', 'hellod wordl', 'https://miro.medium.com/v2/resize:fit:1024/1*OohqW5DGh9CQS4hLY5FXzA.png', 'console.log("hello world")', error => { + if (error) { + console.error(error) + + return + } + console.log('post created') + }) +} catch (error) { + console.error(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/deletePost.js b/staff/angel-patino/socialcode/api/logic/deletePost.js new file mode 100644 index 000000000..13351238f --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/deletePost.js @@ -0,0 +1,55 @@ +import data from '../data/index.js' +import { MatchError } from 'com/errors.js' +import validate from 'com/validate.js' + +const deletePost = (username, postId, callback) => { + validate.username(username) + validate.id(postId, 'postId') + validate.callback(callback) + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + data.findPost(post => post.id === postId, (error, post) => { + if (error) { + callback(error) + + return + } + + if (!post) { + callback(new MatchError('post not found')) + + return + } + + if (post.author !== username) { + callback(new MatchError('post author does not match user')) + + return + } + + data.deletePost(post => post.id === postId, error => { + if (error) { + callback(error) + + return + } + + callback(null) + }) + }) + }) +} + +export default deletePost \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/deletePost.test.js b/staff/angel-patino/socialcode/api/logic/deletePost.test.js new file mode 100644 index 000000000..d03a6d9e6 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/deletePost.test.js @@ -0,0 +1,15 @@ +import logic from "./index.js" + +try { + logic.deletePost('pepitogrillo', '2553895236558785-1717010042893', error => { + if (error) { + console.error(error) + + return + } + + console.log('post deleted') + }) +} catch (error) { + console.error(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/getAllPosts.js b/staff/angel-patino/socialcode/api/logic/getAllPosts.js new file mode 100644 index 000000000..499f3c083 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/getAllPosts.js @@ -0,0 +1,34 @@ +import data from '../data/index.js' +import { MatchError } from 'com/errors.js' +import validate from 'com/validate.js' + +const getAllPosts = (username, callback) => { + validate.username(username) + validate.callbakc(callback) + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + data.findPosts(() => true, (error, posts) => { + if (error) { + callback(error) + + return + } + + callback(null, posts.reverse()) + }) + }) +} + +export default getAllPosts \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/getAllPosts.test.js b/staff/angel-patino/socialcode/api/logic/getAllPosts.test.js new file mode 100644 index 000000000..7e5a2fd61 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/getAllPosts.test.js @@ -0,0 +1,15 @@ +import logic from './index.js' + +try { + logic.getAllPosts('pepitogrillo', (error, posts) => { + if (error) { + console.error(error) + + return + } + + console.log('posts retrieved', posts) + }) +} catch (error) { + console.error(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/getUserName.js b/staff/angel-patino/socialcode/api/logic/getUserName.js new file mode 100644 index 000000000..a45ab8659 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/getUserName.js @@ -0,0 +1,41 @@ +import data from "../data/index.js" +import { MatchError } from 'com/errors.js' +import validate from 'com/validate.js' + +const getUserName = (username, targetUsername, callback) => { + validate.username(username) + validate.targetUsername(targetUsername, 'targetUsername') + validate.callback(callback) + + data.findUser(user => user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (!user) { + callback(new MatchError('user not found')) + + return + } + + data.findUser(user => user.username === targetUsername, (error, targetUser) => { + if (error) { + callback(error) + + return + } + + if (!targetUser) { + callback(new MatchError('targetUser not found')) + + return + } + + callback(null, targetUser.name) + }) + }) +} + +export default getUserName \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/getUserName.test.js b/staff/angel-patino/socialcode/api/logic/getUserName.test.js new file mode 100644 index 000000000..c3c34dbad --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/getUserName.test.js @@ -0,0 +1,15 @@ +import logic from './index.js' + +try { + logic.getUserName('peterpan', ' pepitogrillo', (error, name) => { + if (error) { + console.error(error) + + return + } + + console.log('user name retrieved', name) + }) +} catch (error) { + console.error(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/index.js b/staff/angel-patino/socialcode/api/logic/index.js new file mode 100644 index 000000000..fa232ce49 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/index.js @@ -0,0 +1,19 @@ +import registerUser from './registerUser.js' +import authenticateUser from './authenticateUser.js' +import getUserName from './getUserName.js' + +import getAllPosts from './getAllPosts.js' +import createPost from './createPost.js' +import deletePost from './deletePost.js' + +const logic = { + registerUser, + authenticateUser, + getUserName, + + getAllPosts, + createPost, + deletePost +} + +export default logic \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/index.test.js b/staff/angel-patino/socialcode/api/logic/index.test.js new file mode 100644 index 000000000..8faf67a46 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/index.test.js @@ -0,0 +1,15 @@ +import logic from './index.js' + +try { + logic.getUserName('peterpan', 'pepitogrillo', (error, name) => { + if (error) { + console.error(error) + + return + } + + console.log('user name retrieved', name) + }) +} catch (error) { + console.error(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/registerUser.js b/staff/angel-patino/socialcode/api/logic/registerUser.js new file mode 100644 index 000000000..1458a232e --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/registerUser.js @@ -0,0 +1,57 @@ +import data from '../data/index.js' +import { DuplicityError, SystemError } from 'com/errors.js' +import validate from 'com/validate.js' +import bcrypt from 'bcryptjs' + +const registerUser = (name, surname, email, username, password, passwordRepeat, callback) => { + validate.name(name) + validate.surname(surname, 'surname') + validate.email(email) + validate.username(username) + validate.password(password) + validate.passwordsMatch(password, passwordRepeat) + validate.callback(callback) + + data.findUser(user => user.email === email || user.username === username, (error, user) => { + if (error) { + callback(error) + + return + } + + if (user) { + callback(new DuplicityError('user already exists')) + + return + } + + bcrypt.hash(password, 8, (error, hash) => { + if (error) { + callback(new SystemError(error.message)) + + return + } + + const newUser = { + name: name, + surname: surname, + email: email, + username: username, + password: hash + } + + + data.insertUser(newUser, error => { + if (error) { + callback(error) + + return + } + + callback(null) + }) + }) + }) +} + +export default registerUser \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/logic/registerUser.test.js b/staff/angel-patino/socialcode/api/logic/registerUser.test.js new file mode 100644 index 000000000..d41f53276 --- /dev/null +++ b/staff/angel-patino/socialcode/api/logic/registerUser.test.js @@ -0,0 +1,16 @@ +import logic from './index.js' + +try { + //logic.registerUser('Peter', 'Pan', 'peter@pan.com', 'peterpan', '123123123', '123123123', error => { + logic.registerUser('Wendy', 'Darling', 'wendy@darling.com', 'wendydarling', '123123123', '123123123', error => { + if (error) { + console.error(error) + + return + } + + console.log('user registered') + }) +} catch (error) { + console.error(error) +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/package-lock.json b/staff/angel-patino/socialcode/api/package-lock.json new file mode 100644 index 000000000..c32415035 --- /dev/null +++ b/staff/angel-patino/socialcode/api/package-lock.json @@ -0,0 +1,847 @@ +{ + "name": "api", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "api", + "version": "0.0.0", + "license": "ISC", + "dependencies": { + "bcryptjs": "^2.4.3", + "com": "file:../com", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "jsonwebtoken": "^9.0.2" + } + }, + "../com": { + "version": "0.0.0", + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/com": { + "resolved": "../com", + "link": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + } + } +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/api/package.json b/staff/angel-patino/socialcode/api/package.json new file mode 100644 index 000000000..2f32c88ce --- /dev/null +++ b/staff/angel-patino/socialcode/api/package.json @@ -0,0 +1,23 @@ +{ + "name": "api", + "version": "0.0.0", + "description": "", + "type": "module", + "main": "index.js", + "scripts": { + "start": "node .", + "inspect": "node --inspect-brk .", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcryptjs": "^2.4.3", + "com": "file:../com", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "jsonwebtoken": "^9.0.2" + } +} diff --git a/staff/angel-patino/socialcode/api/test/register-user.sh b/staff/angel-patino/socialcode/api/test/register-user.sh new file mode 100644 index 000000000..37c221fd7 --- /dev/null +++ b/staff/angel-patino/socialcode/api/test/register-user.sh @@ -0,0 +1 @@ +curl -X POST http://localhost:9010/users -H "Content-Type: application/json" -d '{"name":"Pepito","surname":"Grillo","email":"pepito3@grillo.com","username":"pepito3grillo","password":"123123123","passwordRepeat":"123123123"}' -v \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/.eslintrc.cjs b/staff/angel-patino/socialcode/app/.eslintrc.cjs new file mode 100644 index 000000000..3e212e1d4 --- /dev/null +++ b/staff/angel-patino/socialcode/app/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react/jsx-no-target-blank': 'off', + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/staff/angel-patino/socialcode/app/.gitignore b/staff/angel-patino/socialcode/app/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/staff/angel-patino/socialcode/app/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/staff/angel-patino/socialcode/app/README.md b/staff/angel-patino/socialcode/app/README.md new file mode 100644 index 000000000..f768e33fc --- /dev/null +++ b/staff/angel-patino/socialcode/app/README.md @@ -0,0 +1,8 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/staff/angel-patino/socialcode/app/index.html b/staff/angel-patino/socialcode/app/index.html new file mode 100644 index 000000000..61f9e60ae --- /dev/null +++ b/staff/angel-patino/socialcode/app/index.html @@ -0,0 +1,16 @@ + + + + + + + + Social Code + + + +
+ + + + diff --git a/staff/angel-patino/socialcode/app/package-lock.json b/staff/angel-patino/socialcode/app/package-lock.json new file mode 100644 index 000000000..4d623baa6 --- /dev/null +++ b/staff/angel-patino/socialcode/app/package-lock.json @@ -0,0 +1,4296 @@ +{ + "name": "app", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "app", + "version": "0.0.0", + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "vite": "^5.2.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.6", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz", + "integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", + "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helpers": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/template": "^7.24.6", + "@babel/traverse": "^7.24.6", + "@babel/types": "^7.24.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", + "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz", + "integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.24.6", + "@babel/helper-validator-option": "^7.24.6", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz", + "integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz", + "integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz", + "integrity": "sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz", + "integrity": "sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz", + "integrity": "sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-module-imports": "^7.24.6", + "@babel/helper-simple-access": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz", + "integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz", + "integrity": "sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", + "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz", + "integrity": "sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz", + "integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.6", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.6.tgz", + "integrity": "sha512-FfZfHXtQ5jYPQsCRyLpOv2GeLIIJhs8aydpNh39vRDjhD411XcfWDni5i7OjP/Rs8GAtTn7sWFFELJSHqkIxYg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.6.tgz", + "integrity": "sha512-BQTBCXmFRreU3oTUXcGKuPOfXAGb1liNY4AvvFKsOBAJ89RKcTsIrSsnMYkj59fNa66OFKnSa4AJZfy5Y4B9WA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", + "integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz", + "integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", + "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", + "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.0.tgz", + "integrity": "sha512-KcEbMsn4Dpk+LIbHMj7gDPRKaTMStxxWRkRmxsg/jVdFdJCZWt1SchZcf0M4t8lIKdwwMsEyzhrcOXRrDPtOBw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.24.5", + "@babel/plugin-transform-react-jsx-self": "^7.24.5", + "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.toreversed": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001627", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz", + "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.788", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.788.tgz", + "integrity": "sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.34.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", + "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.3", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.19", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.hasown": "^1.1.4", + "object.values": "^1.2.0", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.7.tgz", + "integrity": "sha512-yrj+KInFmwuQS2UQcg1SF83ha1tuHC1jMQbRNyuWtlEzzKRDgAl7L4Yp4NlDUZTZNlWvHEzOtJhMi40R7JxcSw==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", + "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "dev": true, + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/staff/angel-patino/socialcode/app/package.json b/staff/angel-patino/socialcode/app/package.json new file mode 100644 index 000000000..07d93d013 --- /dev/null +++ b/staff/angel-patino/socialcode/app/package.json @@ -0,0 +1,27 @@ +{ + "name": "app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "vite": "^5.2.0" + } +} diff --git a/staff/angel-patino/socialcode/app/public/vite.svg b/staff/angel-patino/socialcode/app/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/staff/angel-patino/socialcode/app/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/login/index.css b/staff/angel-patino/socialcode/app/src/App.css similarity index 100% rename from staff/angel-patino/socialcode/app/login/index.css rename to staff/angel-patino/socialcode/app/src/App.css diff --git a/staff/angel-patino/socialcode/app/src/App.jsx b/staff/angel-patino/socialcode/app/src/App.jsx new file mode 100644 index 000000000..25e0f77b9 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/App.jsx @@ -0,0 +1,26 @@ + import { useState } from 'react' + import Register from './views/Register.jsx' + import Login from './views/Login.jsx' + import Home from './views/Home.jsx' + + function App() { + console.log('App -> paint') + + const [view, setView] = useState('login') + + const handleGoToLogin = () => setView('login') + + const handleGoToHome = () => setView('home') + + const handleGoToRegister = () => setView('register') + + return ( + <> + {view === 'register' && } + {view === 'login' && } + {view === 'home' && } + + ) + } + + export default App \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Button.css b/staff/angel-patino/socialcode/app/src/components/core/Button.css similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Button.css rename to staff/angel-patino/socialcode/app/src/components/core/Button.css diff --git a/staff/angel-patino/socialcode/app/src/components/core/Button.jsx b/staff/angel-patino/socialcode/app/src/components/core/Button.jsx new file mode 100644 index 000000000..933e04985 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Button.jsx @@ -0,0 +1,7 @@ + import './Button.css' + + function Button ({ type, className, onClick, children }) { + return + } + + export default Button \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Field.css b/staff/angel-patino/socialcode/app/src/components/core/Field.css similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Field.css rename to staff/angel-patino/socialcode/app/src/components/core/Field.css diff --git a/staff/angel-patino/socialcode/app/src/components/core/Field.jsx b/staff/angel-patino/socialcode/app/src/components/core/Field.jsx new file mode 100644 index 000000000..956cd2726 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Field.jsx @@ -0,0 +1,13 @@ + import Label from './Label' + import Input from './Input' + + import './Field.css' + + function Field({ id, type, placeholder, children}) { + return
+ + +
+ } + + export default Field \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Form.css b/staff/angel-patino/socialcode/app/src/components/core/Form.css similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Form.css rename to staff/angel-patino/socialcode/app/src/components/core/Form.css diff --git a/staff/angel-patino/socialcode/app/src/components/core/Form.jsx b/staff/angel-patino/socialcode/app/src/components/core/Form.jsx new file mode 100644 index 000000000..5c24fa26f --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Form.jsx @@ -0,0 +1,7 @@ +import './Form.css' + +function Form ({ className, onSubmit, children }) { + return
{children}
+} + +export default Form \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/core/Heading.jsx b/staff/angel-patino/socialcode/app/src/components/core/Heading.jsx new file mode 100644 index 000000000..a35828f98 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Heading.jsx @@ -0,0 +1,7 @@ + function Heading({ level, children }) { + const Tag = `h${level}` + + return {children} + } + + export default Heading \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Image.css b/staff/angel-patino/socialcode/app/src/components/core/Image.css similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Image.css rename to staff/angel-patino/socialcode/app/src/components/core/Image.css diff --git a/staff/angel-patino/socialcode/app/src/components/core/Image.jsx b/staff/angel-patino/socialcode/app/src/components/core/Image.jsx new file mode 100644 index 000000000..a3f3b0f87 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Image.jsx @@ -0,0 +1,7 @@ + import './Image.css' + + function Image({ src }) { + return + } + + export default Image \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Input.css b/staff/angel-patino/socialcode/app/src/components/core/Input.css similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Input.css rename to staff/angel-patino/socialcode/app/src/components/core/Input.css diff --git a/staff/angel-patino/socialcode/app/src/components/core/Input.jsx b/staff/angel-patino/socialcode/app/src/components/core/Input.jsx new file mode 100644 index 000000000..a1e6fd644 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Input.jsx @@ -0,0 +1,7 @@ + import './Input.css' + + function Input({ id, type, placeholder }) { + return + } + + export default Input \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/core/Label.css b/staff/angel-patino/socialcode/app/src/components/core/Label.css new file mode 100644 index 000000000..948e3fe63 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Label.css @@ -0,0 +1,3 @@ +.Label { + margin-bottom: .5rem; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/core/Label.jsx b/staff/angel-patino/socialcode/app/src/components/core/Label.jsx new file mode 100644 index 000000000..5dd5740b1 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Label.jsx @@ -0,0 +1,7 @@ + import './Label.css' + + function Label({ htmlFor, children }) { + return + } + + export default Label \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/core/Link.jsx b/staff/angel-patino/socialcode/app/src/components/core/Link.jsx new file mode 100644 index 000000000..8dad69471 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Link.jsx @@ -0,0 +1,9 @@ +export default ({ onClick, children }) => {children} + +/* + function Link( { onClick, children}) + export defautl Link + + export default function Link + +*/ \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/SubmitButton.css b/staff/angel-patino/socialcode/app/src/components/core/SubmitButton.css similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/SubmitButton.css rename to staff/angel-patino/socialcode/app/src/components/core/SubmitButton.css diff --git a/staff/angel-patino/socialcode/app/src/components/core/SubmitButton.jsx b/staff/angel-patino/socialcode/app/src/components/core/SubmitButton.jsx new file mode 100644 index 000000000..ca69d7bd4 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/SubmitButton.jsx @@ -0,0 +1,8 @@ +import Button from "./Button" +import './SubmitButton.css' + +function SubmitButton ({ children }) { + return +} + +export default SubmitButton \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/core/Text.css b/staff/angel-patino/socialcode/app/src/components/core/Text.css new file mode 100644 index 000000000..c7e99b309 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Text.css @@ -0,0 +1,3 @@ +.Text { + color:blueviolet; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/core/Text.jsx b/staff/angel-patino/socialcode/app/src/components/core/Text.jsx new file mode 100644 index 000000000..85df362a1 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Text.jsx @@ -0,0 +1,7 @@ +import "./Text.css" + +function Text({ children }) { + return

{children}

+} + +export default Text diff --git a/staff/angel-patino/socialcode/app/src/components/core/Time.jsx b/staff/angel-patino/socialcode/app/src/components/core/Time.jsx new file mode 100644 index 000000000..09f3d0415 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Time.jsx @@ -0,0 +1,6 @@ +function Time({ children: time }) { + const formattedTime = new Date(time).toLocaleString() + return +} + +export default Time diff --git a/staff/angel-patino/socialcode/app/src/components/core/Title.jsx b/staff/angel-patino/socialcode/app/src/components/core/Title.jsx new file mode 100644 index 000000000..72ae29490 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Title.jsx @@ -0,0 +1,7 @@ +import Heading from "./Heading" + +function Title ({ children }){ + return {children} +} + +export default Title diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css b/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.css similarity index 100% rename from staff/angel-patino/socialcode/app/components/library/FormWithFeedback.css rename to staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.css diff --git a/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx b/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx new file mode 100644 index 000000000..2a73c9850 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx @@ -0,0 +1,8 @@ +import './FormWithFeedback.css' +import Form from '../core/Form.jsx' + +function FormWithFeedback ({ onSubmit, children }) { + return
{children}
+} + +export default FormWithFeedback \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/library/View.css b/staff/angel-patino/socialcode/app/src/components/library/View.css new file mode 100644 index 000000000..860e35893 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/library/View.css @@ -0,0 +1,6 @@ +.View { + display: flex; + flex-direction: column; + align-items: center; +} + diff --git a/staff/angel-patino/socialcode/app/src/components/library/View.jsx b/staff/angel-patino/socialcode/app/src/components/library/View.jsx new file mode 100644 index 000000000..67b702372 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/library/View.jsx @@ -0,0 +1,6 @@ + import './View.css' + + function View({ tag: Tag = 'div', className, children }) { + return {children} + } + export default View \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/errors.js b/staff/angel-patino/socialcode/app/src/errors.js new file mode 100644 index 000000000..4a846f80e --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/errors.js @@ -0,0 +1,40 @@ +class ContentError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class MatchError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class DuplicityError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class SystemError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +const errors = { + ContentError, + MatchError, + DuplicityError, + SystemError +} + +export default errors \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/index.css b/staff/angel-patino/socialcode/app/src/index.css new file mode 100644 index 000000000..a1051312a --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/index.css @@ -0,0 +1,16 @@ +@import url('https://fonts.googleapis.com/css2?family=Workbench&display=swap'); + +:root { + --first-color: yellowgreen; + --second-color: black; +} + +* { + font-family: Workbench; + color: var(--first-color); +} + +body { + background-color: var(--second-color); + margin-top: 5rem; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic.js b/staff/angel-patino/socialcode/app/src/logic.js new file mode 100644 index 000000000..b6163c1ca --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic.js @@ -0,0 +1,236 @@ +import errors from './errors' + +const { ContentError, MatchError } = errors + +const logic = {} + +const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +const USERNAME_REGEX = /^[\w-]+$/ +const PASSWORD_REGEX = /^[\w-$%&=\[\]\{\}\<\>\(\)]{8,}$/ + +const NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ + +const ID_REGEX = /^[0-9]+-[0-9]+$/ + +logic.registerUser = (name, surname, email, username, password, passwordRepeat, callback) => { + if (!NAME_REGEX.test(name)) + throw new ContentError('name is not valid') + + if (!NAME_REGEX.test(surname)) + throw new ContentError('surname is not valid') + + if (!EMAIL_REGEX.test(email)) + throw new ContentError('email is not valid') + + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!PASSWORD_REGEX.test(password)) + throw new ContentError('password is not valid') + + if (password !== passwordRepeat) + throw new MatchError('passwords don\'t match') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 201) { + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('POST', 'http://localhost:8080/users') + + const body = { name, surname, email, username, password, passwordRepeat } + + const json = JSON.stringify(body) + + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.send(json) +} + +logic.loginUser = (username, password, callback) => { + if (!USERNAME_REGEX.test(username)) + throw new ContentError('username is not valid') + + if (!PASSWORD_REGEX.test(password)) + throw new ContentError('password is not valid') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 200) { + sessionStorage.username = username + + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('POST', 'http://localhost:8080/users/auth') + + const body = { username, password } + + const json = JSON.stringify(body) + + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.send(json) +} + +logic.isUserLoggedIn = () => !!sessionStorage.username + +logic.logoutUser = () => delete sessionStorage.username + +logic.getUserName = callback => { + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 200) { + const name = JSON.parse(xhr.response) + + callback(null, name) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('GET', `http://localhost:8080/users/${sessionStorage.username}`) + + xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.send() +} + +logic.getAllPosts = callback => { + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 200) { + const posts = JSON.parse(xhr.response) + + callback(null, posts) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('GET', 'http://localhost:8080/posts') + + xhr.send() +} + +logic.createPost = (title, image, description, callback) => { + if (typeof title !== 'string' || !title.length || title.length > 50) + throw new ContentError('title is not valid') + + if (typeof image !== 'string' || !image.startsWith('http')) + throw new ContentError('image is not valid') + + if (typeof description !== 'string' || !description.length || description.length > 200) + throw new ContentError('description is not valid') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 201) { + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('POST', 'http://localhost:8080/posts') + + xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + + const body = { + title, + image, + description + } + + const json = JSON.stringify(body) + + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.send(json) +} + +logic.getLoggedInUsername = () => sessionStorage.username + +logic.deletePost = (postId, callback) => { + if (!ID_REGEX.test(postId)) + throw new ContentError('postId is not valid') + + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 204) { + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('DELETE', `http://localhost:8080/posts/${postId}`) + + xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.send() +} + +export default logic \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/main.jsx b/staff/angel-patino/socialcode/app/src/main.jsx new file mode 100644 index 000000000..6c2c63444 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + +) \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/views/Home.jsx b/staff/angel-patino/socialcode/app/src/views/Home.jsx new file mode 100644 index 000000000..35ce59c5a --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/Home.jsx @@ -0,0 +1,67 @@ +import { useState, useEffect } from "react" +import PostList from "./components/PostList.jsx" +import View from "../components/library/View.jsx" +import Header from "./components/Header.jsx" +import Button from "../components/core/Button.jsx" +import Heading from "../components/core/Heading.jsx" +import Footer from "./components/Footer.jsx" + +import logic from "../logic.js" +import CreatePostForm from "./components/CreatePostForm" + +function Home({ onUserLoggedOut }) { + const [name, setName] = useState("") + const [view, setView] = useState("") + const [postListRefreshStamp, setPostListRefreshStamp] = useState(0) + + const handleLogout = () => { + logic.logoutUser() + + onUserLoggedOut() + } + + useEffect(() => { + try { + logic.getUserName((error, name) => { + if (error) { + console.error(error) + + alert(error.message) + + return + } + setName(name) + }) + } catch (error) { + console.error(error) + + alert(error.message) + } + }, []) + + const handleCreatePostClick = () => setView("create-post") + const handleCancelCreatePostClick = () => setView("") + + return ( + +
+ {name} + +
+ + + + + {view === "create-post" && ( + + )} + + +
+
+ ) +} + +export default Home diff --git a/staff/angel-patino/socialcode/app/src/views/Login.jsx b/staff/angel-patino/socialcode/app/src/views/Login.jsx new file mode 100644 index 000000000..e102fc4b8 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/Login.jsx @@ -0,0 +1,58 @@ +import logic from '../logic.js' +import Field from '../components/core/Field.jsx' +import SubmitButton from '../components/core/SubmitButton.jsx' +import FormWithFeedback from '../components/library/FormWithFeedback.jsx' +import Link from '../components/core/Link.jsx' +import Title from '../components/core/Title.jsx' +import View from '../components/library/View.jsx' + +function Login({ onUserLoggedIn, onRegisterLinkClick }){ + + const handleLoginSubmit = event => { + event.preventDefault() + + const form = event.target + + const username = form.username.value + const password = form.password.value + + try { + logic.loginUser(username, password, error => { + if (error) { + console.log(error) + + alert(error.message) + + return + } + + onUserLoggedIn() + }) + } catch (error) { + console.log(error) + + alert(error.message) + } + } + + const handleRegisterClick = event => { + event.preventDefault() + + onRegisterLinkClick() + } + return + Login + + + Username + + Password + + Login + + + Register + + } + + export default Login \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/views/Register.jsx b/staff/angel-patino/socialcode/app/src/views/Register.jsx new file mode 100644 index 000000000..14ed5e5c8 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/Register.jsx @@ -0,0 +1,71 @@ +import logic from '../logic.js' +import Field from '../components/core/Field.jsx' +import SubmitButton from '../components/core/SubmitButton.jsx' +import FormWithFeedback from '../components/library/FormWithFeedback.jsx' +import Link from '../components/core/Link.jsx' +import Title from '../components/core/Title.jsx' +import View from '../components/library/View.jsx' + +function Register({ onUserRegistered, onLoginLinkClick }) { + + const handleRegisterSubmit = event => { + event.preventDefault() + + const form = event.target + + const name = form.name.value + const surname = form.surname.value + const email = form.email.value + const username = form.username.value + const password = form.password.value + const passwordRepeat = form.passwordRepeat.value + + try { + logic.registerUser(name, surname, email, username, password, passwordRepeat, error => { + if(error) { + console.log(error) + + alert(error.message) + + return + } + onUserRegistered() + }) + } catch (error) { + console.error(error) + + alert(error.message) + } + } + + const handleLoginClick = event => { + event.preventDefault() + onLoginLinkClick() + } + + return ( + + Register + + + Name + + Surname + + E-mail + + Username + + Password + + Password Repeat + + Register + + + Login + + ) + } + +export default Register \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/views/components/CreatePostForm.jsx b/staff/angel-patino/socialcode/app/src/views/components/CreatePostForm.jsx new file mode 100644 index 000000000..4395ddc33 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/components/CreatePostForm.jsx @@ -0,0 +1,70 @@ +import logic from "../../logic" + +function CreatePostForm({ onCancelCreatePostClick, onPostCreated }) { + const handleCreatePostClick = () => onCancelCreatePostClick() + + const handleCreatePostSubmit = (event) => { + event.preventDefault() + + const form = event.form + + const title = form.title.value + const image = form.image.value + const description = form.description.value + + try { + logic.createPost(title, image, description, (error) => { + if (error) { + console.error(erorr) + alert(error.message) + + return + } + + setPostListRefreshStamp(Date.now) + setView("") + }) + } catch (error) { + console.error(error) + alert(error.message) + } + } + + return ( +
+
+ + +
+
+ + +
+
+ + +
+ + + +
+ ) +} + +export default CreatePostForm diff --git a/staff/angel-patino/socialcode/app/src/views/components/Footer.css b/staff/angel-patino/socialcode/app/src/views/components/Footer.css new file mode 100644 index 000000000..e69de29bb diff --git a/staff/angel-patino/socialcode/app/src/views/components/Footer.jsx b/staff/angel-patino/socialcode/app/src/views/components/Footer.jsx new file mode 100644 index 000000000..febcebfd3 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/components/Footer.jsx @@ -0,0 +1,13 @@ +import "./Footer.css" +import Button from "../../components/core/Button" + +function Footer({ onCreatePostClick }) { + const handleCancelCreatePostClick = () => onCreatePostClick() + return ( +
+ +
+ ) +} + +export default Footer diff --git a/staff/angel-patino/socialcode/app/src/views/components/Header.css b/staff/angel-patino/socialcode/app/src/views/components/Header.css new file mode 100644 index 000000000..2be38a04b --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/components/Header.css @@ -0,0 +1,12 @@ +.Header { + display: flex; + gap: 1rem; + width: 100%; + justify-content: right; + position: fixed; + top: 0; + padding: .5rem; + box-sizing: border-box; + border-bottom: 1px solid var(--first-color); + background-color: var(--second-color); +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/views/components/Header.jsx b/staff/angel-patino/socialcode/app/src/views/components/Header.jsx new file mode 100644 index 000000000..a0e517ec0 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/components/Header.jsx @@ -0,0 +1,9 @@ + import './Header.css' + + function Header({ children} ){ + return
+ {children} +
+ } + + export default Header \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/views/components/Post.jsx b/staff/angel-patino/socialcode/app/src/views/components/Post.jsx new file mode 100644 index 000000000..5c61493f0 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/components/Post.jsx @@ -0,0 +1,47 @@ +import Image from "../../components/core/Image" +import Heading from "../../components/core/Heading" +import Button from "../../components/core/Button" +import Text from "../../components/core/Text" +import Time from "../../components/core/Time" + +import logic from "../../logic" + +function Post({ post, onPostDeleted }) { + const handleDeletePost = () => { + if (confirm("Delete post?")) + try { + logic.deletePost(postId, (error) => { + if (error) { + console.error(error) + + return + } + + onPostDeleted() + }) + } catch (error) { + console.error(error) + + alert(error.message) + } + } + + return ( +
+ {post.author} + + {post.title} + + + + {post.description} + + + + {post.author === logic.getLoggedInUsername() && ( + + )} +
+ ) +} +export default Post diff --git a/staff/angel-patino/socialcode/app/src/views/components/PostList.css b/staff/angel-patino/socialcode/app/src/views/components/PostList.css new file mode 100644 index 000000000..f1cbd0494 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/components/PostList.css @@ -0,0 +1,3 @@ + .PostList { + margin: 60px 10px; + } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/views/components/PostList.jsx b/staff/angel-patino/socialcode/app/src/views/components/PostList.jsx new file mode 100644 index 000000000..ec3b12c32 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/views/components/PostList.jsx @@ -0,0 +1,53 @@ +import { useEffect, useState } from "react" +import "./PostList.css" + +import Post from "./Post" +import View from "../../components/library/View" + +import logic from "../../logic" + +function PostList({ refreshStamp }) { + console.log("PostList -> render") + + const [posts, setPosts] = useState([]) + + useEffect(() => { + console.log("PostList -> useEffect") + + loadPosts() + }, [refreshStamp]) + + const loadPosts = () => { + try { + logic.getAllPosts((error, posts) => { + if (error) { + console.error(error) + + alert(error.message) + + return + } + + console.log("PostList -> setPosts") + + setPosts(posts) + }) + } catch (error) { + console.error(error) + + alert(error.message) + } + } + + const handlePostDeletePost = () => loadPosts() + + return ( + + {posts.map((post) => ( + + ))} + + ) +} + +export default PostList diff --git a/staff/angel-patino/socialcode/app/vite.config.js b/staff/angel-patino/socialcode/app/vite.config.js new file mode 100644 index 000000000..5a33944a9 --- /dev/null +++ b/staff/angel-patino/socialcode/app/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/staff/angel-patino/socialcode/app/components/Component.js b/staff/angel-patino/socialcode/app1/components/Component.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/Component.js rename to staff/angel-patino/socialcode/app1/components/Component.js diff --git a/staff/angel-patino/socialcode/app1/components/core/Button.css b/staff/angel-patino/socialcode/app1/components/core/Button.css new file mode 100644 index 000000000..b561d84fc --- /dev/null +++ b/staff/angel-patino/socialcode/app1/components/core/Button.css @@ -0,0 +1,8 @@ +.Button { + padding: .4rem; + background-color: transparent; + font-size: 1rem; + border: 1px solid var(--first-color); + cursor: pointer; + +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Button.js b/staff/angel-patino/socialcode/app1/components/core/Button.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Button.js rename to staff/angel-patino/socialcode/app1/components/core/Button.js diff --git a/staff/angel-patino/socialcode/app1/components/core/Field.css b/staff/angel-patino/socialcode/app1/components/core/Field.css new file mode 100644 index 000000000..708f5849b --- /dev/null +++ b/staff/angel-patino/socialcode/app1/components/core/Field.css @@ -0,0 +1,5 @@ +.Field { + display: flex; + flex-direction: column; + margin: .25rem 0; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Field.js b/staff/angel-patino/socialcode/app1/components/core/Field.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Field.js rename to staff/angel-patino/socialcode/app1/components/core/Field.js diff --git a/staff/angel-patino/socialcode/app1/components/core/Form.css b/staff/angel-patino/socialcode/app1/components/core/Form.css new file mode 100644 index 000000000..379cad316 --- /dev/null +++ b/staff/angel-patino/socialcode/app1/components/core/Form.css @@ -0,0 +1,5 @@ +.Form { + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Form.js b/staff/angel-patino/socialcode/app1/components/core/Form.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Form.js rename to staff/angel-patino/socialcode/app1/components/core/Form.js diff --git a/staff/angel-patino/socialcode/app/components/core/Heading.js b/staff/angel-patino/socialcode/app1/components/core/Heading.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Heading.js rename to staff/angel-patino/socialcode/app1/components/core/Heading.js diff --git a/staff/angel-patino/socialcode/app1/components/core/Image.css b/staff/angel-patino/socialcode/app1/components/core/Image.css new file mode 100644 index 000000000..d1abcb0ea --- /dev/null +++ b/staff/angel-patino/socialcode/app1/components/core/Image.css @@ -0,0 +1,3 @@ +.Image { + width: 100%; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Image.js b/staff/angel-patino/socialcode/app1/components/core/Image.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Image.js rename to staff/angel-patino/socialcode/app1/components/core/Image.js diff --git a/staff/angel-patino/socialcode/app1/components/core/Input.css b/staff/angel-patino/socialcode/app1/components/core/Input.css new file mode 100644 index 000000000..6c09b66f6 --- /dev/null +++ b/staff/angel-patino/socialcode/app1/components/core/Input.css @@ -0,0 +1,6 @@ +.Input { + padding: .4rem; + background-color: transparent; + font-size: 1rem; + border: 1px solid var(--first-color); +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/Input.js b/staff/angel-patino/socialcode/app1/components/core/Input.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Input.js rename to staff/angel-patino/socialcode/app1/components/core/Input.js diff --git a/staff/angel-patino/socialcode/app/components/core/Label.js b/staff/angel-patino/socialcode/app1/components/core/Label.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Label.js rename to staff/angel-patino/socialcode/app1/components/core/Label.js diff --git a/staff/angel-patino/socialcode/app/components/core/Link.js b/staff/angel-patino/socialcode/app1/components/core/Link.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/Link.js rename to staff/angel-patino/socialcode/app1/components/core/Link.js diff --git a/staff/angel-patino/socialcode/app1/components/core/SubmitButton.css b/staff/angel-patino/socialcode/app1/components/core/SubmitButton.css new file mode 100644 index 000000000..4452ec3b8 --- /dev/null +++ b/staff/angel-patino/socialcode/app1/components/core/SubmitButton.css @@ -0,0 +1,3 @@ +.SubmitButton { + margin: .5rem 0; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/core/SubmitButton.js b/staff/angel-patino/socialcode/app1/components/core/SubmitButton.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/core/SubmitButton.js rename to staff/angel-patino/socialcode/app1/components/core/SubmitButton.js diff --git a/staff/angel-patino/socialcode/app1/components/library/FormWithFeedback.css b/staff/angel-patino/socialcode/app1/components/library/FormWithFeedback.css new file mode 100644 index 000000000..0e7015959 --- /dev/null +++ b/staff/angel-patino/socialcode/app1/components/library/FormWithFeedback.css @@ -0,0 +1,11 @@ +.FormWithFeedback { + padding: 1rem; +} + +.FormWithFeedback .Feedback { + color: tomato; +} + +.FormWithFeedback .Feedback.success { + color: greenyellow; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js b/staff/angel-patino/socialcode/app1/components/library/FormWithFeedback.js similarity index 100% rename from staff/angel-patino/socialcode/app/components/library/FormWithFeedback.js rename to staff/angel-patino/socialcode/app1/components/library/FormWithFeedback.js diff --git a/staff/angel-patino/socialcode/app/data.js b/staff/angel-patino/socialcode/app1/data.js similarity index 100% rename from staff/angel-patino/socialcode/app/data.js rename to staff/angel-patino/socialcode/app1/data.js diff --git a/staff/angel-patino/socialcode/app/errors.js b/staff/angel-patino/socialcode/app1/errors.js similarity index 100% rename from staff/angel-patino/socialcode/app/errors.js rename to staff/angel-patino/socialcode/app1/errors.js diff --git a/staff/angel-patino/socialcode/app/global.css b/staff/angel-patino/socialcode/app1/global.css similarity index 100% rename from staff/angel-patino/socialcode/app/global.css rename to staff/angel-patino/socialcode/app1/global.css diff --git a/staff/angel-patino/socialcode/app/home/components/Confirm.css b/staff/angel-patino/socialcode/app1/home/components/Confirm.css similarity index 100% rename from staff/angel-patino/socialcode/app/home/components/Confirm.css rename to staff/angel-patino/socialcode/app1/home/components/Confirm.css diff --git a/staff/angel-patino/socialcode/app/home/components/Confirm.js b/staff/angel-patino/socialcode/app1/home/components/Confirm.js similarity index 100% rename from staff/angel-patino/socialcode/app/home/components/Confirm.js rename to staff/angel-patino/socialcode/app1/home/components/Confirm.js diff --git a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.css b/staff/angel-patino/socialcode/app1/home/components/CreatePostForm.css similarity index 100% rename from staff/angel-patino/socialcode/app/home/components/CreatePostForm.css rename to staff/angel-patino/socialcode/app1/home/components/CreatePostForm.css diff --git a/staff/angel-patino/socialcode/app/home/components/CreatePostForm.js b/staff/angel-patino/socialcode/app1/home/components/CreatePostForm.js similarity index 100% rename from staff/angel-patino/socialcode/app/home/components/CreatePostForm.js rename to staff/angel-patino/socialcode/app1/home/components/CreatePostForm.js diff --git a/staff/angel-patino/socialcode/app/home/components/Post.js b/staff/angel-patino/socialcode/app1/home/components/Post.js similarity index 100% rename from staff/angel-patino/socialcode/app/home/components/Post.js rename to staff/angel-patino/socialcode/app1/home/components/Post.js diff --git a/staff/angel-patino/socialcode/app/home/components/PostList.js b/staff/angel-patino/socialcode/app1/home/components/PostList.js similarity index 100% rename from staff/angel-patino/socialcode/app/home/components/PostList.js rename to staff/angel-patino/socialcode/app1/home/components/PostList.js diff --git a/staff/angel-patino/socialcode/app/home/index.css b/staff/angel-patino/socialcode/app1/home/index.css similarity index 100% rename from staff/angel-patino/socialcode/app/home/index.css rename to staff/angel-patino/socialcode/app1/home/index.css diff --git a/staff/angel-patino/socialcode/app/home/index.html b/staff/angel-patino/socialcode/app1/home/index.html similarity index 100% rename from staff/angel-patino/socialcode/app/home/index.html rename to staff/angel-patino/socialcode/app1/home/index.html diff --git a/staff/angel-patino/socialcode/app/home/index.js b/staff/angel-patino/socialcode/app1/home/index.js similarity index 100% rename from staff/angel-patino/socialcode/app/home/index.js rename to staff/angel-patino/socialcode/app1/home/index.js diff --git a/staff/angel-patino/socialcode/app/logic.js b/staff/angel-patino/socialcode/app1/logic.js similarity index 100% rename from staff/angel-patino/socialcode/app/logic.js rename to staff/angel-patino/socialcode/app1/logic.js diff --git a/staff/angel-patino/socialcode/app/login/LoginForm.js b/staff/angel-patino/socialcode/app1/login/LoginForm.js similarity index 100% rename from staff/angel-patino/socialcode/app/login/LoginForm.js rename to staff/angel-patino/socialcode/app1/login/LoginForm.js diff --git a/staff/angel-patino/socialcode/app/register/index.css b/staff/angel-patino/socialcode/app1/login/index.css similarity index 100% rename from staff/angel-patino/socialcode/app/register/index.css rename to staff/angel-patino/socialcode/app1/login/index.css diff --git a/staff/angel-patino/socialcode/app/login/index.html b/staff/angel-patino/socialcode/app1/login/index.html similarity index 100% rename from staff/angel-patino/socialcode/app/login/index.html rename to staff/angel-patino/socialcode/app1/login/index.html diff --git a/staff/angel-patino/socialcode/app/login/index.js b/staff/angel-patino/socialcode/app1/login/index.js similarity index 100% rename from staff/angel-patino/socialcode/app/login/index.js rename to staff/angel-patino/socialcode/app1/login/index.js diff --git a/staff/angel-patino/socialcode/app/register/RegisterForm.js b/staff/angel-patino/socialcode/app1/register/RegisterForm.js similarity index 100% rename from staff/angel-patino/socialcode/app/register/RegisterForm.js rename to staff/angel-patino/socialcode/app1/register/RegisterForm.js diff --git a/staff/angel-patino/socialcode/app1/register/index.css b/staff/angel-patino/socialcode/app1/register/index.css new file mode 100644 index 000000000..968ff685b --- /dev/null +++ b/staff/angel-patino/socialcode/app1/register/index.css @@ -0,0 +1,5 @@ +.View { + display: flex; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/register/index.html b/staff/angel-patino/socialcode/app1/register/index.html similarity index 100% rename from staff/angel-patino/socialcode/app/register/index.html rename to staff/angel-patino/socialcode/app1/register/index.html diff --git a/staff/angel-patino/socialcode/app/register/index.js b/staff/angel-patino/socialcode/app1/register/index.js similarity index 100% rename from staff/angel-patino/socialcode/app/register/index.js rename to staff/angel-patino/socialcode/app1/register/index.js diff --git a/staff/angel-patino/socialcode/com/errors.js b/staff/angel-patino/socialcode/com/errors.js new file mode 100644 index 000000000..23b76907a --- /dev/null +++ b/staff/angel-patino/socialcode/com/errors.js @@ -0,0 +1,48 @@ +class ContentError extends Error { + constructor(message) { + super(message) + + //this.name = ContentError.name + this.name = this.constructor.name + } +} + +class MatchError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class DuplicityError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class SystemError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +export { + ContentError, + MatchError, + DuplicityError, + SystemError +} + +const errors = { + ContentError, + MatchError, + DuplicityError, + SystemError +} + +export default errors \ No newline at end of file diff --git a/staff/angel-patino/socialcode/com/index.js b/staff/angel-patino/socialcode/com/index.js new file mode 100644 index 000000000..abb6735af --- /dev/null +++ b/staff/angel-patino/socialcode/com/index.js @@ -0,0 +1 @@ +// import errors from './errors.js' \ No newline at end of file diff --git a/staff/angel-patino/socialcode/com/package.json b/staff/angel-patino/socialcode/com/package.json new file mode 100644 index 000000000..71b29c0c9 --- /dev/null +++ b/staff/angel-patino/socialcode/com/package.json @@ -0,0 +1,13 @@ +{ + "name": "com", + "version": "0.0.0", + "description": "", + "type": "module", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" + } \ No newline at end of file diff --git a/staff/angel-patino/socialcode/com/validate.js b/staff/angel-patino/socialcode/com/validate.js new file mode 100644 index 000000000..582f70867 --- /dev/null +++ b/staff/angel-patino/socialcode/com/validate.js @@ -0,0 +1,66 @@ +import { ContentError, MatchError } from './errors.js' + +const NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ +const USERNAME_REGEX = /^[\w-]+$/ +const PASSWORD_REGEX = /^[\w-$%&=\[\]\{\}\<\>\(\)]{8,}$/ +const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +const ID_REGEX = /^[0-9]+-[0-9]+$/ + +function validateName(name, explain = 'name') { + if (typeof name !== 'string' || !NAME_REGEX.test(name)) + throw new ContentError(`${explain} is not valid`) +} + +function validateUsername(username, explain = 'username') { + if (typeof username !== 'string' || !USERNAME_REGEX.test(username)) + throw new ContentError(`${explain} is not valid`) +} + +function validatePassword(password) { + if (typeof password !== 'string' || !PASSWORD_REGEX.test(password)) + throw new ContentError('password is not valid') +} + +function validatePasswordsMatch(password, passwordRepeat) { + if (password !== passwordRepeat) + throw new MatchError('passwords don\'t match') +} + +function validateEmail(email) { + if (typeof email !== 'string' || !EMAIL_REGEX.test(email)) + throw new ContentError('email is not valid') +} + +function validateCallback(callback) { + if (typeof callback !== 'function') + throw new TypeError('callback is not a function') +} + +function validateText(text, explain = 'text', maxLength = Infinity) { + if (typeof text !== 'string' || !text.length || text.length > maxLength) + throw new ContentError(`${explain} is not valid`) +} + +function validateUrl(url, explain = 'url') { + if (typeof url !== 'string' || !url.startsWith('http')) + throw new ContentError(`${explain} is not valid`) +} + +function validateId(id, explain = 'id') { + if (!ID_REGEX.test(id)) + throw new ContentError(`${explain} is not valid`) +} + +const validate = { + name: validateName, + username: validateUsername, + password: validatePassword, + passowrdsMatch: validatePasswordsMatch, + email: validateEmail, + callback: validateCallback, + text: validateText, + url: validateUrl, + id: validateId +} + +export default validate \ No newline at end of file From f7d59b3b8104417d18df245a51652e24f4e69e1b Mon Sep 17 00:00:00 2001 From: Pat Date: Wed, 26 Jun 2024 16:25:42 +0200 Subject: [PATCH 08/15] create logic in the app, and modified core and library #124 --- .../app/src/components/core/Button.jsx | 20 +++++++--- .../app/src/components/core/Field.jsx | 24 ++++++------ .../app/src/components/core/Time.css | 3 ++ .../app/src/components/core/Time.jsx | 2 + .../app/src/components/core/Title.jsx | 4 +- .../components/library/FormWithFeedback.jsx | 19 ++++++--- .../app/src/components/library/View.css | 12 +++++- .../app/src/components/library/View.jsx | 27 ++++++++++--- .../socialcode/app/src/logic/createPost.js | 37 ++++++++++++++++++ .../socialcode/app/src/logic/deletePost.js | 30 ++++++++++++++ .../socialcode/app/src/logic/getAllPosts.js | 32 +++++++++++++++ .../socialcode/app/src/logic/getUserName.js | 35 +++++++++++++++++ .../app/src/logic/getUserUsername.js | 9 +++++ .../socialcode/app/src/logic/index.js | 25 ++++++++++++ .../app/src/logic/isUserLoggedIn.js | 3 ++ .../socialcode/app/src/logic/loginUser.js | 39 +++++++++++++++++++ .../socialcode/app/src/logic/logoutUser.js | 3 ++ .../socialcode/app/src/logic/registerUser.js | 38 ++++++++++++++++++ 18 files changed, 330 insertions(+), 32 deletions(-) create mode 100644 staff/angel-patino/socialcode/app/src/components/core/Time.css create mode 100644 staff/angel-patino/socialcode/app/src/logic/createPost.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/deletePost.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/getAllPosts.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/getUserName.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/getUserUsername.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/index.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/isUserLoggedIn.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/loginUser.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/logoutUser.js create mode 100644 staff/angel-patino/socialcode/app/src/logic/registerUser.js diff --git a/staff/angel-patino/socialcode/app/src/components/core/Button.jsx b/staff/angel-patino/socialcode/app/src/components/core/Button.jsx index 933e04985..60aeca8e4 100644 --- a/staff/angel-patino/socialcode/app/src/components/core/Button.jsx +++ b/staff/angel-patino/socialcode/app/src/components/core/Button.jsx @@ -1,7 +1,15 @@ - import './Button.css' - - function Button ({ type, className, onClick, children }) { - return - } +import "./Button.css" - export default Button \ No newline at end of file +function Button({ type, className, onClick, children }) { + return ( + + ) +} + +export default Button diff --git a/staff/angel-patino/socialcode/app/src/components/core/Field.jsx b/staff/angel-patino/socialcode/app/src/components/core/Field.jsx index 956cd2726..d44f8a1f2 100644 --- a/staff/angel-patino/socialcode/app/src/components/core/Field.jsx +++ b/staff/angel-patino/socialcode/app/src/components/core/Field.jsx @@ -1,13 +1,15 @@ - import Label from './Label' - import Input from './Input' +import Label from "./Label" +import Input from "./Input" - import './Field.css' - - function Field({ id, type, placeholder, children}) { - return
- - -
- } +import "./Field.css" - export default Field \ No newline at end of file +function Field({ id, type, placeholder, children }) { + return ( +
+ + +
+ ) +} + +export default Field diff --git a/staff/angel-patino/socialcode/app/src/components/core/Time.css b/staff/angel-patino/socialcode/app/src/components/core/Time.css new file mode 100644 index 000000000..9b8cd7ce7 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/components/core/Time.css @@ -0,0 +1,3 @@ +.Time { + color: rgb(86, 119, 151); +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/components/core/Time.jsx b/staff/angel-patino/socialcode/app/src/components/core/Time.jsx index 09f3d0415..6f97f9903 100644 --- a/staff/angel-patino/socialcode/app/src/components/core/Time.jsx +++ b/staff/angel-patino/socialcode/app/src/components/core/Time.jsx @@ -1,3 +1,5 @@ +import "./Time.css" + function Time({ children: time }) { const formattedTime = new Date(time).toLocaleString() return diff --git a/staff/angel-patino/socialcode/app/src/components/core/Title.jsx b/staff/angel-patino/socialcode/app/src/components/core/Title.jsx index 72ae29490..291d79866 100644 --- a/staff/angel-patino/socialcode/app/src/components/core/Title.jsx +++ b/staff/angel-patino/socialcode/app/src/components/core/Title.jsx @@ -1,7 +1,7 @@ import Heading from "./Heading" -function Title ({ children }){ - return {children} +function Title({ children }) { + return {children} } export default Title diff --git a/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx b/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx index 2a73c9850..d6aed71c3 100644 --- a/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx +++ b/staff/angel-patino/socialcode/app/src/components/library/FormWithFeedback.jsx @@ -1,8 +1,15 @@ -import './FormWithFeedback.css' -import Form from '../core/Form.jsx' - -function FormWithFeedback ({ onSubmit, children }) { - return
{children}
+import Text from "../core/Text.jsx" +import Form from "../core/Form.jsx" + +import "./FormWithFeedback.css" + +function FormWithFeedback({ onSubmit, children, message, level = "erorr" }) { + return ( +
+ {children} + {message && {message}} +
+ ) } -export default FormWithFeedback \ No newline at end of file +export default FormWithFeedback diff --git a/staff/angel-patino/socialcode/app/src/components/library/View.css b/staff/angel-patino/socialcode/app/src/components/library/View.css index 860e35893..84c3bbc5b 100644 --- a/staff/angel-patino/socialcode/app/src/components/library/View.css +++ b/staff/angel-patino/socialcode/app/src/components/library/View.css @@ -1,6 +1,16 @@ .View { display: flex; +} + +.View.colum { flex-direction: column; - align-items: center; } +.View.row { + flex-direction: row; + gap: 1rem; +} + +.View.center { + align-items: center; +} diff --git a/staff/angel-patino/socialcode/app/src/components/library/View.jsx b/staff/angel-patino/socialcode/app/src/components/library/View.jsx index 67b702372..fd37c9b17 100644 --- a/staff/angel-patino/socialcode/app/src/components/library/View.jsx +++ b/staff/angel-patino/socialcode/app/src/components/library/View.jsx @@ -1,6 +1,21 @@ - import './View.css' - - function View({ tag: Tag = 'div', className, children }) { - return {children} - } - export default View \ No newline at end of file +import "./View.css" + +function View({ + tag: Tag = "div", + className, + children, + direction = "column", + align = "center", +}) { + return ( + + {children} + + ) +} + +export default View diff --git a/staff/angel-patino/socialcode/app/src/logic/createPost.js b/staff/angel-patino/socialcode/app/src/logic/createPost.js new file mode 100644 index 000000000..75722aa56 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/createPost.js @@ -0,0 +1,37 @@ +import errors from 'com/errors' +import validate from 'com/validate' + +const createPost = (title, image, description, callback) => { + validate.text(title, 'title', 50) + validate.url(image, 'image') + validate.text(description, 'description', 200) + validate.callback(callback) + + const xhr = new XMLHttpRequest + xhr.onload = () => { + if (xhr.status === 201) { + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors(error) + + callback(new constructor(message)) + } + + xhr.open('POST', `${import.meta.env.VITE_API_URL}/posts`) + + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) + + const body = { title, image, description } + + const json = JSON.stringify(body) + + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.send(json) +} + +export default createPost \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/deletePost.js b/staff/angel-patino/socialcode/app/src/logic/deletePost.js new file mode 100644 index 000000000..f60dbc20f --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/deletePost.js @@ -0,0 +1,30 @@ +import errors from 'com/errors' +import validate from 'com/validate' + +const deletePost = (postId, callback) => { + validate.id(postId, 'postId') + validate.callback(callback) + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 204) { + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = error[error] + + callback(new constructor(message)) + } + + xhr.open('DELETE', `${import.meta.env.VITE_API_URL}/posts/${postId}`) + + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) + xhr.send() +} + +export default deletePost \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/getAllPosts.js b/staff/angel-patino/socialcode/app/src/logic/getAllPosts.js new file mode 100644 index 000000000..5fbd5f611 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/getAllPosts.js @@ -0,0 +1,32 @@ +import errors from 'com/errors' +import validate from 'com/validate' + +const getAllPosts = callback => { + validate.callback(callback) + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 200) { + const posts = JSON.parse(xhr.response) + + callback(null, posts) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('GET', `${import.meta.env.VITE_API_URL}/posts`) + + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) + + xhr.send() +} + +export default getAllPosts \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/getUserName.js b/staff/angel-patino/socialcode/app/src/logic/getUserName.js new file mode 100644 index 000000000..651848bec --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/getUserName.js @@ -0,0 +1,35 @@ +import errors from 'com/errors' +import validate from 'com/validate' + +import extractPayloadFromJWT from '../utils/extractPayloadFromJWT' + +const getUserName = callback => { + validate.callback(callback) + + const { sub: username } = extractPayloadFromJWT(sessionStorage.token) + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 200) { + const name = JSON.parse(xhr.response) + + callback(null, name) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + xhr.open('GET', `${import.meta.env.VITE_API_URL}/users/${username}`) + + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) + xhr.send() +} + +export default getUserName \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/getUserUsername.js b/staff/angel-patino/socialcode/app/src/logic/getUserUsername.js new file mode 100644 index 000000000..da4f4dbbe --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/getUserUsername.js @@ -0,0 +1,9 @@ +import extractPayloadFromJWT from '../utils/extractPayloadFromJWT' + +const getUserUsername = () => { + const { sub: username } = extractPayloadFromJWT(sessionStorage.token) + + return username +} + +export default getUserUsername \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/index.js b/staff/angel-patino/socialcode/app/src/logic/index.js new file mode 100644 index 000000000..85953949e --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/index.js @@ -0,0 +1,25 @@ +import registerUser from './registerUser' +import loginUser from './loginUser' +import getUserName from './getUserName' +import isUserLoggedIn from './isUserLoggedIn' +import getUserUsername from './getUserUsername' +import logoutUser from './logoutUser' + +import getAllPosts from './getAllPosts' +import createPost from './createPost' +import deletePost from './deletePost' + +const logic = { + registerUser, + loginUser, + getUserName, + isUserLoggedIn, + getUserUsername, + logoutUser, + + getAllPosts, + createPost, + deletePost +} + +export default logic \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/isUserLoggedIn.js b/staff/angel-patino/socialcode/app/src/logic/isUserLoggedIn.js new file mode 100644 index 000000000..9029c562d --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/isUserLoggedIn.js @@ -0,0 +1,3 @@ +const isUserLoggedIn = () => !!sessionStorage.token + +export default isUserLoggedIn \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/loginUser.js b/staff/angel-patino/socialcode/app/src/logic/loginUser.js new file mode 100644 index 000000000..2f7ff0446 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/loginUser.js @@ -0,0 +1,39 @@ +import errors from 'com/errors' +import validate from 'com/validate' + +const loginUser = (username, password, callback) => { + valideta.username(username) + validate.password(password) + validate.callback(callback) + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 200) { + const token = JSON.parse(xhr.response) + + sessionStorage.token = token + + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructo = errors[error] + + callback(new constructor(message)) + } + + xhr.open('POST', `${import.meta.env.VITE_API_URL}/users/auth`) + + const body = { username, password } + + const json = JSON.stringify(body) + + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.send(json) +} + +export default loginUser \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/logoutUser.js b/staff/angel-patino/socialcode/app/src/logic/logoutUser.js new file mode 100644 index 000000000..54ff1ba32 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/logoutUser.js @@ -0,0 +1,3 @@ +const logoutUser = () => delete sessionStorage.token + +export default logoutUser \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic/registerUser.js b/staff/angel-patino/socialcode/app/src/logic/registerUser.js new file mode 100644 index 000000000..8f85530fb --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/logic/registerUser.js @@ -0,0 +1,38 @@ +import errors from 'com/errors' +import validate from 'com/validate' + +const registerUser = (name, surname, email, username, password, passwordRepeat, callback) => { + validate.name(name) + validate.surname(surname, 'surname') + validate.email(email) + validate.username(username) + validate.password(password) + validate.passwordMatch(password, passwordRepeat) + validate.callback(callback) + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 201) { + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + callback(new constructor(message)) + } + + xhr.open('POST', `${import.meta.env.VITE_API_URL}/users`) + + const body = { name, surname, email, username, password, passwordRepeat } + + const json = JSON.stringify(body) + + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.send(json) +} + +export default registerUser \ No newline at end of file From 25eda0a933c92a3a34cd64a734de6a3e1e393b22 Mon Sep 17 00:00:00 2001 From: Pat Date: Wed, 26 Jun 2024 17:09:20 +0200 Subject: [PATCH 09/15] modificated components, add utils folder #124 --- staff/angel-patino/socialcode/app/.env | 1 + staff/angel-patino/socialcode/app/.gitignore | 2 +- .../angel-patino/socialcode/app/package.json | 3 +- staff/angel-patino/socialcode/app/src/App.jsx | 50 ++-- .../angel-patino/socialcode/app/src/errors.js | 40 --- .../angel-patino/socialcode/app/src/logic.js | 236 ------------------ .../app/src/utils/extractPayloadFromJWT.js | 22 ++ .../socialcode/app/src/views/Home.jsx | 17 +- .../socialcode/app/src/views/Login.jsx | 68 ++--- .../socialcode/app/src/views/Register.jsx | 143 +++++++---- .../src/views/components/CreatePostForm.jsx | 74 +++--- .../app/src/views/components/Footer.css | 10 + .../app/src/views/components/Footer.jsx | 5 +- .../app/src/views/components/Post.jsx | 25 +- .../app/src/views/components/PostList.jsx | 9 +- .../src/views/components/createPostForm.css | 8 + 16 files changed, 273 insertions(+), 440 deletions(-) create mode 100644 staff/angel-patino/socialcode/app/.env delete mode 100644 staff/angel-patino/socialcode/app/src/errors.js delete mode 100644 staff/angel-patino/socialcode/app/src/logic.js create mode 100644 staff/angel-patino/socialcode/app/src/utils/extractPayloadFromJWT.js create mode 100644 staff/angel-patino/socialcode/app/src/views/components/createPostForm.css diff --git a/staff/angel-patino/socialcode/app/.env b/staff/angel-patino/socialcode/app/.env new file mode 100644 index 000000000..9efeddeb3 --- /dev/null +++ b/staff/angel-patino/socialcode/app/.env @@ -0,0 +1 @@ +VITE_API_URL = http://localhost:9010 \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/.gitignore b/staff/angel-patino/socialcode/app/.gitignore index a547bf36d..54f07af58 100644 --- a/staff/angel-patino/socialcode/app/.gitignore +++ b/staff/angel-patino/socialcode/app/.gitignore @@ -21,4 +21,4 @@ dist-ssr *.ntvs* *.njsproj *.sln -*.sw? +*.sw? \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/package.json b/staff/angel-patino/socialcode/app/package.json index 07d93d013..2359c8556 100644 --- a/staff/angel-patino/socialcode/app/package.json +++ b/staff/angel-patino/socialcode/app/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "dependencies": { + "com": "file:../com", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -24,4 +25,4 @@ "eslint-plugin-react-refresh": "^0.4.6", "vite": "^5.2.0" } -} +} \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/App.jsx b/staff/angel-patino/socialcode/app/src/App.jsx index 25e0f77b9..72f868aad 100644 --- a/staff/angel-patino/socialcode/app/src/App.jsx +++ b/staff/angel-patino/socialcode/app/src/App.jsx @@ -1,26 +1,38 @@ - import { useState } from 'react' - import Register from './views/Register.jsx' - import Login from './views/Login.jsx' - import Home from './views/Home.jsx' +import { useState } from "react" +import Register from "./views/Register.jsx" +import Login from "./views/Login.jsx" +import Home from "./views/Home.jsx" - function App() { - console.log('App -> paint') +import logic from "./logic.js" - const [view, setView] = useState('login') +function App() { + console.log("App -> paint") - const handleGoToLogin = () => setView('login') + const [view, setView] = useState(logic.isUserLoggedIn() ? "home" : "login") - const handleGoToHome = () => setView('home') + const handleGoToLogin = () => setView("login") - const handleGoToRegister = () => setView('register') + const handleGoToHome = () => setView("home") - return ( - <> - {view === 'register' && } - {view === 'login' && } - {view === 'home' && } - - ) - } + const handleGoToRegister = () => setView("register") - export default App \ No newline at end of file + return ( + <> + {view === "register" && ( + + )} + {view === "login" && ( + + )} + {view === "home" && } + + ) +} + +export default App diff --git a/staff/angel-patino/socialcode/app/src/errors.js b/staff/angel-patino/socialcode/app/src/errors.js deleted file mode 100644 index 4a846f80e..000000000 --- a/staff/angel-patino/socialcode/app/src/errors.js +++ /dev/null @@ -1,40 +0,0 @@ -class ContentError extends Error { - constructor(message) { - super(message) - - this.name = this.constructor.name - } -} - -class MatchError extends Error { - constructor(message) { - super(message) - - this.name = this.constructor.name - } -} - -class DuplicityError extends Error { - constructor(message) { - super(message) - - this.name = this.constructor.name - } -} - -class SystemError extends Error { - constructor(message) { - super(message) - - this.name = this.constructor.name - } -} - -const errors = { - ContentError, - MatchError, - DuplicityError, - SystemError -} - -export default errors \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/logic.js b/staff/angel-patino/socialcode/app/src/logic.js deleted file mode 100644 index b6163c1ca..000000000 --- a/staff/angel-patino/socialcode/app/src/logic.js +++ /dev/null @@ -1,236 +0,0 @@ -import errors from './errors' - -const { ContentError, MatchError } = errors - -const logic = {} - -const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ -const USERNAME_REGEX = /^[\w-]+$/ -const PASSWORD_REGEX = /^[\w-$%&=\[\]\{\}\<\>\(\)]{8,}$/ - -const NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ - -const ID_REGEX = /^[0-9]+-[0-9]+$/ - -logic.registerUser = (name, surname, email, username, password, passwordRepeat, callback) => { - if (!NAME_REGEX.test(name)) - throw new ContentError('name is not valid') - - if (!NAME_REGEX.test(surname)) - throw new ContentError('surname is not valid') - - if (!EMAIL_REGEX.test(email)) - throw new ContentError('email is not valid') - - if (!USERNAME_REGEX.test(username)) - throw new ContentError('username is not valid') - - if (!PASSWORD_REGEX.test(password)) - throw new ContentError('password is not valid') - - if (password !== passwordRepeat) - throw new MatchError('passwords don\'t match') - - if (typeof callback !== 'function') - throw new TypeError('callback is not a function') - - const xhr = new XMLHttpRequest - - xhr.onload = () => { - if (xhr.status === 201) { - callback(null) - - return - } - - const { error, message } = JSON.parse(xhr.response) - - const constructor = errors[error] - - callback(new constructor(message)) - } - - xhr.open('POST', 'http://localhost:8080/users') - - const body = { name, surname, email, username, password, passwordRepeat } - - const json = JSON.stringify(body) - - xhr.setRequestHeader('Content-Type', 'application/json') - xhr.send(json) -} - -logic.loginUser = (username, password, callback) => { - if (!USERNAME_REGEX.test(username)) - throw new ContentError('username is not valid') - - if (!PASSWORD_REGEX.test(password)) - throw new ContentError('password is not valid') - - if (typeof callback !== 'function') - throw new TypeError('callback is not a function') - - const xhr = new XMLHttpRequest - - xhr.onload = () => { - if (xhr.status === 200) { - sessionStorage.username = username - - callback(null) - - return - } - - const { error, message } = JSON.parse(xhr.response) - - const constructor = errors[error] - - callback(new constructor(message)) - } - - xhr.open('POST', 'http://localhost:8080/users/auth') - - const body = { username, password } - - const json = JSON.stringify(body) - - xhr.setRequestHeader('Content-Type', 'application/json') - xhr.send(json) -} - -logic.isUserLoggedIn = () => !!sessionStorage.username - -logic.logoutUser = () => delete sessionStorage.username - -logic.getUserName = callback => { - if (typeof callback !== 'function') - throw new TypeError('callback is not a function') - - const xhr = new XMLHttpRequest - - xhr.onload = () => { - if (xhr.status === 200) { - const name = JSON.parse(xhr.response) - - callback(null, name) - - return - } - - const { error, message } = JSON.parse(xhr.response) - - const constructor = errors[error] - - callback(new constructor(message)) - } - - xhr.open('GET', `http://localhost:8080/users/${sessionStorage.username}`) - - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) - xhr.send() -} - -logic.getAllPosts = callback => { - if (typeof callback !== 'function') - throw new TypeError('callback is not a function') - - const xhr = new XMLHttpRequest - - xhr.onload = () => { - if (xhr.status === 200) { - const posts = JSON.parse(xhr.response) - - callback(null, posts) - - return - } - - const { error, message } = JSON.parse(xhr.response) - - const constructor = errors[error] - - callback(new constructor(message)) - } - - xhr.open('GET', 'http://localhost:8080/posts') - - xhr.send() -} - -logic.createPost = (title, image, description, callback) => { - if (typeof title !== 'string' || !title.length || title.length > 50) - throw new ContentError('title is not valid') - - if (typeof image !== 'string' || !image.startsWith('http')) - throw new ContentError('image is not valid') - - if (typeof description !== 'string' || !description.length || description.length > 200) - throw new ContentError('description is not valid') - - if (typeof callback !== 'function') - throw new TypeError('callback is not a function') - - const xhr = new XMLHttpRequest - - xhr.onload = () => { - if (xhr.status === 201) { - callback(null) - - return - } - - const { error, message } = JSON.parse(xhr.response) - - const constructor = errors[error] - - callback(new constructor(message)) - } - - xhr.open('POST', 'http://localhost:8080/posts') - - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) - - const body = { - title, - image, - description - } - - const json = JSON.stringify(body) - - xhr.setRequestHeader('Content-Type', 'application/json') - xhr.send(json) -} - -logic.getLoggedInUsername = () => sessionStorage.username - -logic.deletePost = (postId, callback) => { - if (!ID_REGEX.test(postId)) - throw new ContentError('postId is not valid') - - if (typeof callback !== 'function') - throw new TypeError('callback is not a function') - - const xhr = new XMLHttpRequest - - xhr.onload = () => { - if (xhr.status === 204) { - callback(null) - - return - } - - const { error, message } = JSON.parse(xhr.response) - - const constructor = errors[error] - - callback(new constructor(message)) - } - - xhr.open('DELETE', `http://localhost:8080/posts/${postId}`) - - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) - xhr.send() -} - -export default logic \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/utils/extractPayloadFromJWT.js b/staff/angel-patino/socialcode/app/src/utils/extractPayloadFromJWT.js new file mode 100644 index 000000000..1aeaab581 --- /dev/null +++ b/staff/angel-patino/socialcode/app/src/utils/extractPayloadFromJWT.js @@ -0,0 +1,22 @@ +import errors from 'com/errors' + +const { ContentError, MatchError } = errors + +const JWT_REGEX = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/ + +function extractPayloadFromJWT(token) { + if (!JWT_REGEX.test(token)) throw new ContentError('invalid jwt') + + const [, payload64] = sessionStorage.token.split('.') + const payloadJSON = atob(payload64) + const payload = JSON.parse(payloadJSON) + + const { exp } = payload + const nowSeconds = Date.now() / 1000 + + if (nowSeconds >= exp) throw MatchError('token expired') + + return payload +} + +export default extractPayloadFromJWT \ No newline at end of file diff --git a/staff/angel-patino/socialcode/app/src/views/Home.jsx b/staff/angel-patino/socialcode/app/src/views/Home.jsx index 35ce59c5a..a46bd1295 100644 --- a/staff/angel-patino/socialcode/app/src/views/Home.jsx +++ b/staff/angel-patino/socialcode/app/src/views/Home.jsx @@ -1,13 +1,14 @@ import { useState, useEffect } from "react" + import PostList from "./components/PostList.jsx" import View from "../components/library/View.jsx" import Header from "./components/Header.jsx" import Button from "../components/core/Button.jsx" import Heading from "../components/core/Heading.jsx" import Footer from "./components/Footer.jsx" +import CreatePostForm from "./components/CreatePostForm" import logic from "../logic.js" -import CreatePostForm from "./components/CreatePostForm" function Home({ onUserLoggedOut }) { const [name, setName] = useState("") @@ -40,26 +41,34 @@ function Home({ onUserLoggedOut }) { }, []) const handleCreatePostClick = () => setView("create-post") + const handleCancelCreatePostClick = () => setView("") + const handlePostCreated = () => { + setPostListRefreshStamp(Date.now()) + + setView("") + } + return (
- {name} + {name}
- + {view === "create-post" && ( )} -
+