From 530b39c74901a40f53933dabab30358e69e633ed Mon Sep 17 00:00:00 2001 From: Victor Phan Date: Sun, 10 May 2026 22:03:32 +0700 Subject: [PATCH] init --- .gitignore | 41 + AGENTS.md | 5 + CLAUDE.md | 1 + .../.github/ISSUE_TEMPLATE/bug_report.md | 40 + _libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md | 19 + .../zca-js/.github/ISSUE_TEMPLATE/conduct.md | 55 + .../.github/ISSUE_TEMPLATE/feature_request.md | 19 + .../ISSUE_TEMPLATE/help-wanted_request.md | 16 + .../.github/ISSUE_TEMPLATE/performance.md | 19 + .../zca-js/.github/ISSUE_TEMPLATE/question.md | 16 + .../zca-js/.github/ISSUE_TEMPLATE/security.md | 74 + .../zca-js/.github/ISSUE_TEMPLATE/testing.md | 19 + _libs/zca-js/.github/pull_request_template.md | 18 + _libs/zca-js/.github/workflows/lint.yml | 22 + _libs/zca-js/.gitignore | 136 + _libs/zca-js/.husky/pre-commit | 1 + _libs/zca-js/.npmignore | 15 + _libs/zca-js/.prettierrc | 5 + _libs/zca-js/.vscode/settings.json | 3 + _libs/zca-js/CODE_OF_CONDUCT.md | 163 + _libs/zca-js/CONTRIBUTING.md | 470 + _libs/zca-js/LICENSE | 21 + _libs/zca-js/README.md | 225 + _libs/zca-js/SECURITY.md | 83 + _libs/zca-js/bun.lock | 546 ++ _libs/zca-js/eslint.config.js | 21 + _libs/zca-js/examples/echobot.ts | 56 + _libs/zca-js/examples/login.ts | 40 + _libs/zca-js/index.d.ts | 2 + _libs/zca-js/package-lock.json | 3505 ++++++++ _libs/zca-js/package.json | 78 + _libs/zca-js/rollup.config.js | 28 + _libs/zca-js/scripts/buildAPI.js | 72 + _libs/zca-js/src/Errors/ZaloApiError.ts | 10 + .../src/Errors/ZaloApiLoginQRAborted.ts | 8 + .../src/Errors/ZaloApiLoginQRDeclined.ts | 8 + .../ZaloApiMissingImageMetadataGetter.ts | 8 + _libs/zca-js/src/Errors/index.ts | 4 + _libs/zca-js/src/apis.ts | 455 + _libs/zca-js/src/apis/acceptFriendRequest.ts | 34 + .../zca-js/src/apis/addGroupBlockedMember.ts | 34 + _libs/zca-js/src/apis/addGroupDeputy.ts | 36 + _libs/zca-js/src/apis/addPollOptions.ts | 46 + _libs/zca-js/src/apis/addQuickMessage.ts | 79 + _libs/zca-js/src/apis/addReaction.ts | 321 + _libs/zca-js/src/apis/addUnreadMark.ts | 65 + _libs/zca-js/src/apis/addUserToGroup.ts | 43 + _libs/zca-js/src/apis/blockUser.ts | 34 + _libs/zca-js/src/apis/blockViewFeed.ts | 36 + _libs/zca-js/src/apis/changeAccountAvatar.ts | 65 + _libs/zca-js/src/apis/changeFriendAlias.ts | 33 + _libs/zca-js/src/apis/changeGroupAvatar.ts | 57 + _libs/zca-js/src/apis/changeGroupName.ts | 40 + _libs/zca-js/src/apis/changeGroupOwner.ts | 38 + _libs/zca-js/src/apis/createAutoReply.ts | 58 + _libs/zca-js/src/apis/createCatalog.ts | 41 + _libs/zca-js/src/apis/createGroup.ts | 77 + _libs/zca-js/src/apis/createNote.ts | 62 + _libs/zca-js/src/apis/createPoll.ts | 87 + _libs/zca-js/src/apis/createProductCatalog.ts | 86 + _libs/zca-js/src/apis/createReminder.ts | 84 + _libs/zca-js/src/apis/custom.ts | 19 + _libs/zca-js/src/apis/deleteAutoReply.ts | 38 + _libs/zca-js/src/apis/deleteAvatar.ts | 41 + _libs/zca-js/src/apis/deleteCatalog.ts | 34 + _libs/zca-js/src/apis/deleteChat.ts | 79 + _libs/zca-js/src/apis/deleteGroupInviteBox.ts | 41 + _libs/zca-js/src/apis/deleteMessage.ts | 72 + _libs/zca-js/src/apis/deleteProductCatalog.ts | 46 + _libs/zca-js/src/apis/disableGroupLink.ts | 30 + _libs/zca-js/src/apis/disperseGroup.ts | 34 + _libs/zca-js/src/apis/editNote.ts | 70 + _libs/zca-js/src/apis/editReminder.ts | 83 + _libs/zca-js/src/apis/enableGroupLink.ts | 35 + _libs/zca-js/src/apis/fetchAccountInfo.ts | 17 + _libs/zca-js/src/apis/findUser.ts | 52 + _libs/zca-js/src/apis/findUserByUsername.ts | 34 + _libs/zca-js/src/apis/forwardMessage.ts | 127 + _libs/zca-js/src/apis/getAliasList.ts | 39 + _libs/zca-js/src/apis/getAllFriends.ts | 44 + _libs/zca-js/src/apis/getAllGroups.ts | 20 + _libs/zca-js/src/apis/getArchivedChatList.ts | 32 + _libs/zca-js/src/apis/getAutoDeleteChat.ts | 34 + _libs/zca-js/src/apis/getAutoReplyList.ts | 34 + _libs/zca-js/src/apis/getAvatarList.ts | 44 + _libs/zca-js/src/apis/getAvatarUrlProfile.ts | 40 + _libs/zca-js/src/apis/getBizAccount.ts | 55 + _libs/zca-js/src/apis/getCatalogList.ts | 55 + _libs/zca-js/src/apis/getCloseFriends.ts | 27 + _libs/zca-js/src/apis/getContext.ts | 5 + _libs/zca-js/src/apis/getCookie.ts | 10 + _libs/zca-js/src/apis/getFriendBoardList.ts | 35 + _libs/zca-js/src/apis/getFriendOnlines.ts | 53 + .../src/apis/getFriendRecommendations.ts | 72 + .../zca-js/src/apis/getFriendRequestStatus.ts | 37 + _libs/zca-js/src/apis/getFullAvatar.ts | 34 + .../zca-js/src/apis/getGroupBlockedMember.ts | 56 + _libs/zca-js/src/apis/getGroupChatHistory.ts | 51 + _libs/zca-js/src/apis/getGroupInfo.ts | 55 + .../zca-js/src/apis/getGroupInviteBoxInfo.ts | 79 + .../zca-js/src/apis/getGroupInviteBoxList.ts | 73 + _libs/zca-js/src/apis/getGroupLinkDetail.ts | 38 + _libs/zca-js/src/apis/getGroupLinkInfo.ts | 66 + _libs/zca-js/src/apis/getGroupMembersInfo.ts | 46 + .../zca-js/src/apis/getHiddenConversations.ts | 38 + _libs/zca-js/src/apis/getLabels.ts | 44 + _libs/zca-js/src/apis/getListBoard.ts | 79 + _libs/zca-js/src/apis/getListReminder.ts | 88 + .../zca-js/src/apis/getMultiUsersByPhones.ts | 47 + _libs/zca-js/src/apis/getMute.ts | 40 + _libs/zca-js/src/apis/getOwnId.ts | 5 + .../zca-js/src/apis/getPendingGroupMembers.ts | 43 + _libs/zca-js/src/apis/getPinConversations.ts | 31 + _libs/zca-js/src/apis/getPollDetail.ts | 37 + .../zca-js/src/apis/getProductCatalogList.ts | 58 + _libs/zca-js/src/apis/getQR.ts | 37 + _libs/zca-js/src/apis/getQuickMessageList.ts | 35 + .../zca-js/src/apis/getRelatedFriendGroup.ts | 41 + _libs/zca-js/src/apis/getReminder.ts | 32 + _libs/zca-js/src/apis/getReminderResponses.ts | 33 + _libs/zca-js/src/apis/getSentFriendRequest.ts | 47 + _libs/zca-js/src/apis/getSettings.ts | 27 + .../src/apis/getStickerCategoryDetail.ts | 32 + _libs/zca-js/src/apis/getStickers.ts | 57 + _libs/zca-js/src/apis/getStickersDetail.ts | 49 + _libs/zca-js/src/apis/getUnreadMark.ts | 50 + _libs/zca-js/src/apis/getUserInfo.ts | 58 + _libs/zca-js/src/apis/inviteUserToGroups.ts | 44 + _libs/zca-js/src/apis/joinGroupInviteBox.ts | 31 + _libs/zca-js/src/apis/joinGroupLink.ts | 33 + _libs/zca-js/src/apis/keepAlive.ts | 28 + _libs/zca-js/src/apis/lastOnline.ts | 37 + _libs/zca-js/src/apis/leaveGroup.ts | 41 + _libs/zca-js/src/apis/listen.ts | 544 ++ _libs/zca-js/src/apis/lockPoll.ts | 34 + _libs/zca-js/src/apis/login.ts | 118 + _libs/zca-js/src/apis/loginQR.ts | 529 ++ _libs/zca-js/src/apis/parseLink.ts | 53 + _libs/zca-js/src/apis/rejectFriendRequest.ts | 33 + _libs/zca-js/src/apis/removeFriend.ts | 34 + _libs/zca-js/src/apis/removeFriendAlias.ts | 30 + .../src/apis/removeGroupBlockedMember.ts | 34 + _libs/zca-js/src/apis/removeGroupDeputy.ts | 36 + _libs/zca-js/src/apis/removeQuickMessage.ts | 37 + _libs/zca-js/src/apis/removeReminder.ts | 47 + _libs/zca-js/src/apis/removeUnreadMark.ts | 63 + _libs/zca-js/src/apis/removeUserFromGroup.ts | 42 + .../zca-js/src/apis/resetHiddenConversPin.ts | 26 + _libs/zca-js/src/apis/reuseAvatar.ts | 32 + .../src/apis/reviewPendingMemberRequest.ts | 51 + _libs/zca-js/src/apis/searchSticker.ts | 36 + _libs/zca-js/src/apis/sendBankCard.ts | 49 + _libs/zca-js/src/apis/sendCard.ts | 75 + _libs/zca-js/src/apis/sendDeliveredEvent.ts | 103 + _libs/zca-js/src/apis/sendFriendRequest.ts | 45 + _libs/zca-js/src/apis/sendLink.ts | 76 + _libs/zca-js/src/apis/sendMessage.ts | 674 ++ _libs/zca-js/src/apis/sendReport.ts | 72 + _libs/zca-js/src/apis/sendSeenEvent.ts | 108 + _libs/zca-js/src/apis/sendSticker.ts | 71 + _libs/zca-js/src/apis/sendTypingEvent.ts | 49 + _libs/zca-js/src/apis/sendVideo.ts | 161 + _libs/zca-js/src/apis/sendVoice.ts | 93 + .../zca-js/src/apis/setHiddenConversations.ts | 51 + _libs/zca-js/src/apis/setMute.ts | 82 + .../zca-js/src/apis/setPinnedConversations.ts | 44 + _libs/zca-js/src/apis/sharePoll.ts | 34 + _libs/zca-js/src/apis/unblockUser.ts | 34 + _libs/zca-js/src/apis/undo.ts | 53 + _libs/zca-js/src/apis/undoFriendRequest.ts | 33 + _libs/zca-js/src/apis/updateActiveStatus.ts | 35 + .../zca-js/src/apis/updateArchivedChatList.ts | 44 + _libs/zca-js/src/apis/updateAutoDeleteChat.ts | 46 + _libs/zca-js/src/apis/updateAutoReply.ts | 60 + _libs/zca-js/src/apis/updateCatalog.ts | 47 + _libs/zca-js/src/apis/updateGroupSettings.ts | 97 + .../zca-js/src/apis/updateHiddenConversPin.ts | 37 + _libs/zca-js/src/apis/updateLabels.ts | 54 + _libs/zca-js/src/apis/updateLang.ts | 36 + _libs/zca-js/src/apis/updateProductCatalog.ts | 89 + _libs/zca-js/src/apis/updateProfile.ts | 67 + _libs/zca-js/src/apis/updateProfileBio.ts | 33 + _libs/zca-js/src/apis/updateQuickMessage.ts | 80 + _libs/zca-js/src/apis/updateSettings.ts | 96 + .../src/apis/upgradeGroupToCommunity.ts | 33 + _libs/zca-js/src/apis/uploadAttachment.ts | 366 + _libs/zca-js/src/apis/uploadProductPhoto.ts | 76 + _libs/zca-js/src/apis/votePoll.ts | 38 + _libs/zca-js/src/context.ts | 216 + _libs/zca-js/src/index.ts | 163 + _libs/zca-js/src/models/Attachment.ts | 11 + _libs/zca-js/src/models/AutoReply.ts | 21 + _libs/zca-js/src/models/Board.ts | 64 + _libs/zca-js/src/models/Catalog.ts | 16 + _libs/zca-js/src/models/DeliveredMessage.ts | 44 + _libs/zca-js/src/models/Enum.ts | 255 + _libs/zca-js/src/models/FriendEvent.ts | 205 + _libs/zca-js/src/models/Group.ts | 146 + _libs/zca-js/src/models/GroupEvent.ts | 285 + _libs/zca-js/src/models/Label.ts | 10 + _libs/zca-js/src/models/Message.ts | 125 + _libs/zca-js/src/models/ProductCatalog.ts | 17 + _libs/zca-js/src/models/QuickMessage.ts | 25 + _libs/zca-js/src/models/Reaction.ts | 92 + _libs/zca-js/src/models/Reminder.ts | 53 + _libs/zca-js/src/models/SeenMessage.ts | 43 + _libs/zca-js/src/models/Sticker.ts | 28 + _libs/zca-js/src/models/Typing.ts | 41 + _libs/zca-js/src/models/Undo.ts | 45 + _libs/zca-js/src/models/User.ts | 65 + _libs/zca-js/src/models/ZBusiness.ts | 40 + _libs/zca-js/src/models/index.ts | 21 + _libs/zca-js/src/update.ts | 32 + _libs/zca-js/src/utils.ts | 791 ++ _libs/zca-js/src/zalo.ts | 163 + _libs/zca-js/test/a.png | Bin 0 -> 214492 bytes _libs/zca-js/test/credentials.example.json | 5 + _libs/zca-js/test/feat.ts | 176 + _libs/zca-js/test/test.ts | 100 + _libs/zca-js/tsconfig.json | 104 + data/monitor.db | Bin 0 -> 45056 bytes eslint.config.mjs | 18 + next.config.ts | 7 + package-lock.json | 7552 +++++++++++++++++ package.json | 39 + postcss.config.mjs | 7 + public/file.svg | 1 + public/globe.svg | 1 + public/next.svg | 1 + public/vercel.svg | 1 + public/window.svg | 1 + run.sh | 21 + src/app/api/login/route.ts | 13 + src/app/api/logs/route.ts | 7 + src/app/api/settings/route.ts | 20 + src/app/api/status/route.ts | 6 + src/app/favicon.ico | Bin 0 -> 25931 bytes src/app/globals.css | 26 + src/app/layout.tsx | 36 + src/app/page.tsx | 401 + src/instrumentation.ts | 13 + src/lib/cron.ts | 12 + src/lib/db.ts | 106 + src/lib/reporting.ts | 119 + src/lib/zalo.ts | 110 + tsconfig.json | 34 + 246 files changed, 27965 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/conduct.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/help-wanted_request.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/performance.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/question.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/security.md create mode 100644 _libs/zca-js/.github/ISSUE_TEMPLATE/testing.md create mode 100644 _libs/zca-js/.github/pull_request_template.md create mode 100644 _libs/zca-js/.github/workflows/lint.yml create mode 100644 _libs/zca-js/.gitignore create mode 100644 _libs/zca-js/.husky/pre-commit create mode 100644 _libs/zca-js/.npmignore create mode 100644 _libs/zca-js/.prettierrc create mode 100644 _libs/zca-js/.vscode/settings.json create mode 100644 _libs/zca-js/CODE_OF_CONDUCT.md create mode 100644 _libs/zca-js/CONTRIBUTING.md create mode 100644 _libs/zca-js/LICENSE create mode 100644 _libs/zca-js/README.md create mode 100644 _libs/zca-js/SECURITY.md create mode 100644 _libs/zca-js/bun.lock create mode 100644 _libs/zca-js/eslint.config.js create mode 100644 _libs/zca-js/examples/echobot.ts create mode 100644 _libs/zca-js/examples/login.ts create mode 100644 _libs/zca-js/index.d.ts create mode 100644 _libs/zca-js/package-lock.json create mode 100644 _libs/zca-js/package.json create mode 100644 _libs/zca-js/rollup.config.js create mode 100644 _libs/zca-js/scripts/buildAPI.js create mode 100644 _libs/zca-js/src/Errors/ZaloApiError.ts create mode 100644 _libs/zca-js/src/Errors/ZaloApiLoginQRAborted.ts create mode 100644 _libs/zca-js/src/Errors/ZaloApiLoginQRDeclined.ts create mode 100644 _libs/zca-js/src/Errors/ZaloApiMissingImageMetadataGetter.ts create mode 100644 _libs/zca-js/src/Errors/index.ts create mode 100644 _libs/zca-js/src/apis.ts create mode 100644 _libs/zca-js/src/apis/acceptFriendRequest.ts create mode 100644 _libs/zca-js/src/apis/addGroupBlockedMember.ts create mode 100644 _libs/zca-js/src/apis/addGroupDeputy.ts create mode 100644 _libs/zca-js/src/apis/addPollOptions.ts create mode 100644 _libs/zca-js/src/apis/addQuickMessage.ts create mode 100644 _libs/zca-js/src/apis/addReaction.ts create mode 100644 _libs/zca-js/src/apis/addUnreadMark.ts create mode 100644 _libs/zca-js/src/apis/addUserToGroup.ts create mode 100644 _libs/zca-js/src/apis/blockUser.ts create mode 100644 _libs/zca-js/src/apis/blockViewFeed.ts create mode 100644 _libs/zca-js/src/apis/changeAccountAvatar.ts create mode 100644 _libs/zca-js/src/apis/changeFriendAlias.ts create mode 100644 _libs/zca-js/src/apis/changeGroupAvatar.ts create mode 100644 _libs/zca-js/src/apis/changeGroupName.ts create mode 100644 _libs/zca-js/src/apis/changeGroupOwner.ts create mode 100644 _libs/zca-js/src/apis/createAutoReply.ts create mode 100644 _libs/zca-js/src/apis/createCatalog.ts create mode 100644 _libs/zca-js/src/apis/createGroup.ts create mode 100644 _libs/zca-js/src/apis/createNote.ts create mode 100644 _libs/zca-js/src/apis/createPoll.ts create mode 100644 _libs/zca-js/src/apis/createProductCatalog.ts create mode 100644 _libs/zca-js/src/apis/createReminder.ts create mode 100644 _libs/zca-js/src/apis/custom.ts create mode 100644 _libs/zca-js/src/apis/deleteAutoReply.ts create mode 100644 _libs/zca-js/src/apis/deleteAvatar.ts create mode 100644 _libs/zca-js/src/apis/deleteCatalog.ts create mode 100644 _libs/zca-js/src/apis/deleteChat.ts create mode 100644 _libs/zca-js/src/apis/deleteGroupInviteBox.ts create mode 100644 _libs/zca-js/src/apis/deleteMessage.ts create mode 100644 _libs/zca-js/src/apis/deleteProductCatalog.ts create mode 100644 _libs/zca-js/src/apis/disableGroupLink.ts create mode 100644 _libs/zca-js/src/apis/disperseGroup.ts create mode 100644 _libs/zca-js/src/apis/editNote.ts create mode 100644 _libs/zca-js/src/apis/editReminder.ts create mode 100644 _libs/zca-js/src/apis/enableGroupLink.ts create mode 100644 _libs/zca-js/src/apis/fetchAccountInfo.ts create mode 100644 _libs/zca-js/src/apis/findUser.ts create mode 100644 _libs/zca-js/src/apis/findUserByUsername.ts create mode 100644 _libs/zca-js/src/apis/forwardMessage.ts create mode 100644 _libs/zca-js/src/apis/getAliasList.ts create mode 100644 _libs/zca-js/src/apis/getAllFriends.ts create mode 100644 _libs/zca-js/src/apis/getAllGroups.ts create mode 100644 _libs/zca-js/src/apis/getArchivedChatList.ts create mode 100644 _libs/zca-js/src/apis/getAutoDeleteChat.ts create mode 100644 _libs/zca-js/src/apis/getAutoReplyList.ts create mode 100644 _libs/zca-js/src/apis/getAvatarList.ts create mode 100644 _libs/zca-js/src/apis/getAvatarUrlProfile.ts create mode 100644 _libs/zca-js/src/apis/getBizAccount.ts create mode 100644 _libs/zca-js/src/apis/getCatalogList.ts create mode 100644 _libs/zca-js/src/apis/getCloseFriends.ts create mode 100644 _libs/zca-js/src/apis/getContext.ts create mode 100644 _libs/zca-js/src/apis/getCookie.ts create mode 100644 _libs/zca-js/src/apis/getFriendBoardList.ts create mode 100644 _libs/zca-js/src/apis/getFriendOnlines.ts create mode 100644 _libs/zca-js/src/apis/getFriendRecommendations.ts create mode 100644 _libs/zca-js/src/apis/getFriendRequestStatus.ts create mode 100644 _libs/zca-js/src/apis/getFullAvatar.ts create mode 100644 _libs/zca-js/src/apis/getGroupBlockedMember.ts create mode 100644 _libs/zca-js/src/apis/getGroupChatHistory.ts create mode 100644 _libs/zca-js/src/apis/getGroupInfo.ts create mode 100644 _libs/zca-js/src/apis/getGroupInviteBoxInfo.ts create mode 100644 _libs/zca-js/src/apis/getGroupInviteBoxList.ts create mode 100644 _libs/zca-js/src/apis/getGroupLinkDetail.ts create mode 100644 _libs/zca-js/src/apis/getGroupLinkInfo.ts create mode 100644 _libs/zca-js/src/apis/getGroupMembersInfo.ts create mode 100644 _libs/zca-js/src/apis/getHiddenConversations.ts create mode 100644 _libs/zca-js/src/apis/getLabels.ts create mode 100644 _libs/zca-js/src/apis/getListBoard.ts create mode 100644 _libs/zca-js/src/apis/getListReminder.ts create mode 100644 _libs/zca-js/src/apis/getMultiUsersByPhones.ts create mode 100644 _libs/zca-js/src/apis/getMute.ts create mode 100644 _libs/zca-js/src/apis/getOwnId.ts create mode 100644 _libs/zca-js/src/apis/getPendingGroupMembers.ts create mode 100644 _libs/zca-js/src/apis/getPinConversations.ts create mode 100644 _libs/zca-js/src/apis/getPollDetail.ts create mode 100644 _libs/zca-js/src/apis/getProductCatalogList.ts create mode 100644 _libs/zca-js/src/apis/getQR.ts create mode 100644 _libs/zca-js/src/apis/getQuickMessageList.ts create mode 100644 _libs/zca-js/src/apis/getRelatedFriendGroup.ts create mode 100644 _libs/zca-js/src/apis/getReminder.ts create mode 100644 _libs/zca-js/src/apis/getReminderResponses.ts create mode 100644 _libs/zca-js/src/apis/getSentFriendRequest.ts create mode 100644 _libs/zca-js/src/apis/getSettings.ts create mode 100644 _libs/zca-js/src/apis/getStickerCategoryDetail.ts create mode 100644 _libs/zca-js/src/apis/getStickers.ts create mode 100644 _libs/zca-js/src/apis/getStickersDetail.ts create mode 100644 _libs/zca-js/src/apis/getUnreadMark.ts create mode 100644 _libs/zca-js/src/apis/getUserInfo.ts create mode 100644 _libs/zca-js/src/apis/inviteUserToGroups.ts create mode 100644 _libs/zca-js/src/apis/joinGroupInviteBox.ts create mode 100644 _libs/zca-js/src/apis/joinGroupLink.ts create mode 100644 _libs/zca-js/src/apis/keepAlive.ts create mode 100644 _libs/zca-js/src/apis/lastOnline.ts create mode 100644 _libs/zca-js/src/apis/leaveGroup.ts create mode 100644 _libs/zca-js/src/apis/listen.ts create mode 100644 _libs/zca-js/src/apis/lockPoll.ts create mode 100644 _libs/zca-js/src/apis/login.ts create mode 100644 _libs/zca-js/src/apis/loginQR.ts create mode 100644 _libs/zca-js/src/apis/parseLink.ts create mode 100644 _libs/zca-js/src/apis/rejectFriendRequest.ts create mode 100644 _libs/zca-js/src/apis/removeFriend.ts create mode 100644 _libs/zca-js/src/apis/removeFriendAlias.ts create mode 100644 _libs/zca-js/src/apis/removeGroupBlockedMember.ts create mode 100644 _libs/zca-js/src/apis/removeGroupDeputy.ts create mode 100644 _libs/zca-js/src/apis/removeQuickMessage.ts create mode 100644 _libs/zca-js/src/apis/removeReminder.ts create mode 100644 _libs/zca-js/src/apis/removeUnreadMark.ts create mode 100644 _libs/zca-js/src/apis/removeUserFromGroup.ts create mode 100644 _libs/zca-js/src/apis/resetHiddenConversPin.ts create mode 100644 _libs/zca-js/src/apis/reuseAvatar.ts create mode 100644 _libs/zca-js/src/apis/reviewPendingMemberRequest.ts create mode 100644 _libs/zca-js/src/apis/searchSticker.ts create mode 100644 _libs/zca-js/src/apis/sendBankCard.ts create mode 100644 _libs/zca-js/src/apis/sendCard.ts create mode 100644 _libs/zca-js/src/apis/sendDeliveredEvent.ts create mode 100644 _libs/zca-js/src/apis/sendFriendRequest.ts create mode 100644 _libs/zca-js/src/apis/sendLink.ts create mode 100644 _libs/zca-js/src/apis/sendMessage.ts create mode 100644 _libs/zca-js/src/apis/sendReport.ts create mode 100644 _libs/zca-js/src/apis/sendSeenEvent.ts create mode 100644 _libs/zca-js/src/apis/sendSticker.ts create mode 100644 _libs/zca-js/src/apis/sendTypingEvent.ts create mode 100644 _libs/zca-js/src/apis/sendVideo.ts create mode 100644 _libs/zca-js/src/apis/sendVoice.ts create mode 100644 _libs/zca-js/src/apis/setHiddenConversations.ts create mode 100644 _libs/zca-js/src/apis/setMute.ts create mode 100644 _libs/zca-js/src/apis/setPinnedConversations.ts create mode 100644 _libs/zca-js/src/apis/sharePoll.ts create mode 100644 _libs/zca-js/src/apis/unblockUser.ts create mode 100644 _libs/zca-js/src/apis/undo.ts create mode 100644 _libs/zca-js/src/apis/undoFriendRequest.ts create mode 100644 _libs/zca-js/src/apis/updateActiveStatus.ts create mode 100644 _libs/zca-js/src/apis/updateArchivedChatList.ts create mode 100644 _libs/zca-js/src/apis/updateAutoDeleteChat.ts create mode 100644 _libs/zca-js/src/apis/updateAutoReply.ts create mode 100644 _libs/zca-js/src/apis/updateCatalog.ts create mode 100644 _libs/zca-js/src/apis/updateGroupSettings.ts create mode 100644 _libs/zca-js/src/apis/updateHiddenConversPin.ts create mode 100644 _libs/zca-js/src/apis/updateLabels.ts create mode 100644 _libs/zca-js/src/apis/updateLang.ts create mode 100644 _libs/zca-js/src/apis/updateProductCatalog.ts create mode 100644 _libs/zca-js/src/apis/updateProfile.ts create mode 100644 _libs/zca-js/src/apis/updateProfileBio.ts create mode 100644 _libs/zca-js/src/apis/updateQuickMessage.ts create mode 100644 _libs/zca-js/src/apis/updateSettings.ts create mode 100644 _libs/zca-js/src/apis/upgradeGroupToCommunity.ts create mode 100644 _libs/zca-js/src/apis/uploadAttachment.ts create mode 100644 _libs/zca-js/src/apis/uploadProductPhoto.ts create mode 100644 _libs/zca-js/src/apis/votePoll.ts create mode 100644 _libs/zca-js/src/context.ts create mode 100644 _libs/zca-js/src/index.ts create mode 100644 _libs/zca-js/src/models/Attachment.ts create mode 100644 _libs/zca-js/src/models/AutoReply.ts create mode 100644 _libs/zca-js/src/models/Board.ts create mode 100644 _libs/zca-js/src/models/Catalog.ts create mode 100644 _libs/zca-js/src/models/DeliveredMessage.ts create mode 100644 _libs/zca-js/src/models/Enum.ts create mode 100644 _libs/zca-js/src/models/FriendEvent.ts create mode 100644 _libs/zca-js/src/models/Group.ts create mode 100644 _libs/zca-js/src/models/GroupEvent.ts create mode 100644 _libs/zca-js/src/models/Label.ts create mode 100644 _libs/zca-js/src/models/Message.ts create mode 100644 _libs/zca-js/src/models/ProductCatalog.ts create mode 100644 _libs/zca-js/src/models/QuickMessage.ts create mode 100644 _libs/zca-js/src/models/Reaction.ts create mode 100644 _libs/zca-js/src/models/Reminder.ts create mode 100644 _libs/zca-js/src/models/SeenMessage.ts create mode 100644 _libs/zca-js/src/models/Sticker.ts create mode 100644 _libs/zca-js/src/models/Typing.ts create mode 100644 _libs/zca-js/src/models/Undo.ts create mode 100644 _libs/zca-js/src/models/User.ts create mode 100644 _libs/zca-js/src/models/ZBusiness.ts create mode 100644 _libs/zca-js/src/models/index.ts create mode 100644 _libs/zca-js/src/update.ts create mode 100644 _libs/zca-js/src/utils.ts create mode 100644 _libs/zca-js/src/zalo.ts create mode 100644 _libs/zca-js/test/a.png create mode 100644 _libs/zca-js/test/credentials.example.json create mode 100644 _libs/zca-js/test/feat.ts create mode 100644 _libs/zca-js/test/test.ts create mode 100644 _libs/zca-js/tsconfig.json create mode 100644 data/monitor.db create mode 100644 eslint.config.mjs create mode 100644 next.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 public/file.svg create mode 100644 public/globe.svg create mode 100644 public/next.svg create mode 100644 public/vercel.svg create mode 100644 public/window.svg create mode 100755 run.sh create mode 100644 src/app/api/login/route.ts create mode 100644 src/app/api/logs/route.ts create mode 100644 src/app/api/settings/route.ts create mode 100644 src/app/api/status/route.ts create mode 100644 src/app/favicon.ico create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/page.tsx create mode 100644 src/instrumentation.ts create mode 100644 src/lib/cron.ts create mode 100644 src/lib/db.ts create mode 100644 src/lib/reporting.ts create mode 100644 src/lib/zalo.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8bd0e39 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ + +# This is NOT the Next.js you know + +This version has breaking changes โ€” APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/bug_report.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0914b29 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md new file mode 100644 index 0000000..d6fe758 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/ci_cd.md @@ -0,0 +1,19 @@ +--- +name: CI/CD +about: Report issues with CI/CD pipeline or suggest improvements +title: "[CI/CD]" +labels: ci-cd +assignees: "" +--- + +**Describe the CI/CD issue** +A clear and concise description of the CI/CD problem. + +**Current pipeline behavior** +Describe what's happening in the current pipeline. + +**Expected pipeline behavior** +Describe what should happen in the pipeline. + +**Additional context** +Add any other context about the CI/CD issue here. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/conduct.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/conduct.md new file mode 100644 index 0000000..2004556 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/conduct.md @@ -0,0 +1,55 @@ +--- +name: Code of Conduct Violation +about: Report a violation of our Code of Conduct +title: "[CONDUCT]" +labels: ["CONDUCT", "community"] +assignees: "" +--- + +## Code of Conduct Violation Report + +**IMPORTANT**: This issue will be handled privately and with respect for all parties involved. + +### Type of Violation + +- [ ] Harassment or bullying +- [ ] Inappropriate language or behavior +- [ ] Privacy violation (sharing personal information) +- [ ] Spam or trolling +- [ ] Discrimination +- [ ] Other (please describe) + +### Description + + +### When did this occur? + + +### Where did this occur? + +- [ ] GitHub Issues +- [ ] GitHub Discussions +- [ ] GitHub Pull Requests +- [ ] GitHub Comments +- [ ] External communication (Discord, email, etc.) +- [ ] Other (please specify) + +### People Involved + + +### Evidence + + + +### Impact + + +### Additional Context + + +### Preferred Resolution + + +--- + +**Note**: This report will be reviewed by the team and handled according to our [Code of Conduct](https://github.com/RFS-ADRENO/zca-js/blob/main/CODE_OF_CONDUCT.md). We are committed to maintaining a safe and welcoming community for everyone. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/feature_request.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..a8d1480 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEAT]" +labels: enhancement +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/help-wanted_request.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/help-wanted_request.md new file mode 100644 index 0000000..e9b137f --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/help-wanted_request.md @@ -0,0 +1,16 @@ +--- +name: Help Wanted request +about: Ask for help with something +title: "[HELP]" +labels: help wanted +assignees: "" +--- + +**Is your help wanted request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I need help with a problem [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the help wanted request here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/performance.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/performance.md new file mode 100644 index 0000000..0eb5360 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/performance.md @@ -0,0 +1,19 @@ +--- +name: Performance +about: Report performance issues or suggest optimizations +title: "[PERF]" +labels: performance +assignees: "" +--- + +**Describe the performance issue** +A clear and concise description of the performance problem. + +**Current behavior** +Describe the current performance metrics or behavior. + +**Expected behavior** +Describe what performance you expect to see. + +**Additional context** +Add any other context about the performance issue here. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/question.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..16ac159 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,16 @@ +--- +name: Question +about: Questions arising during use. +title: "[Question] Some question..." +labels: question +assignees: "" +--- + +**Ask the questions you want!** +A clear and concise question about what. Ex. When to use API [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to + +**Additional context** +Add any other context or screenshots about the question here. diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/security.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/security.md new file mode 100644 index 0000000..f14d23a --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/security.md @@ -0,0 +1,74 @@ +--- +name: Security Vulnerability Report +about: Report a security vulnerability +title: "[SECURITY]" +labels: security +assignees: "" +--- + +## Security Vulnerability Report + +**IMPORTANT**: This issue will be handled privately and with the highest priority. Please do not disclose details publicly until the issue is resolved. + +### Vulnerability Type + +- [ ] Authentication bypass +- [ ] Data exposure/information disclosure +- [ ] Code injection +- [ ] Cross-site scripting (XSS) +- [ ] Cross-site request forgery (CSRF) +- [ ] Denial of service (DoS) +- [ ] Privilege escalation +- [ ] Dependency vulnerability +- [ ] Other (please describe) + +### Severity Level + +- [ ] Critical (immediate action required) +- [ ] High (action required within 24 hours) +- [ ] Medium (action required within 1 week) +- [ ] Low (action required within 1 month) + +### Description + + +### Steps to Reproduce + +1. +2. +3. + +### Expected Behavior + + +### Actual Behavior + + +### Environment + +- **zca-js Version**: +- **Node.js Version**: +- **Operating System**: +- **Browser** (if applicable): + +### Impact Assessment + +- **Data at Risk**: +- **Users Affected**: +- **Business Impact**: + +### Suggested Fix + + +### Additional Information + + + +### Disclosure Timeline + + +--- + +**Note**: This report will be reviewed by the security team and handled according to our [Security Policy](https://github.com/RFS-ADRENO/zca-js/blob/main/SECURITY.md). We appreciate your responsible disclosure and will work to resolve this issue promptly. + +**โš ๏ธ Warning**: This is an unofficial API library. Please ensure your testing does not violate Zalo's terms of service or put any accounts at risk. \ No newline at end of file diff --git a/_libs/zca-js/.github/ISSUE_TEMPLATE/testing.md b/_libs/zca-js/.github/ISSUE_TEMPLATE/testing.md new file mode 100644 index 0000000..dcf7f64 --- /dev/null +++ b/_libs/zca-js/.github/ISSUE_TEMPLATE/testing.md @@ -0,0 +1,19 @@ +--- +name: Testing +about: Report testing issues or suggest test improvements +title: "[TEST]" +labels: testing +assignees: "" +--- + +**Describe the testing issue** +A clear and concise description of the testing problem. + +**Test case** +Describe the test case that's failing or needs improvement. + +**Expected test result** +Describe what the test should verify. + +**Additional context** +Add any other context about the testing issue here. \ No newline at end of file diff --git a/_libs/zca-js/.github/pull_request_template.md b/_libs/zca-js/.github/pull_request_template.md new file mode 100644 index 0000000..0fe9a9e --- /dev/null +++ b/_libs/zca-js/.github/pull_request_template.md @@ -0,0 +1,18 @@ +## What does this PR do? + + +## Reason for this PR + + +## How did you verify your code works? +- [ ] All related features have been fully tested +- [ ] Unit tests have been updated/created if needed +- [ ] No lint/format errors +- [ ] Documentation has been updated if needed +- [ ] Code changes + +## Related Issue (if any) + + +## Testing Instructions + \ No newline at end of file diff --git a/_libs/zca-js/.github/workflows/lint.yml b/_libs/zca-js/.github/workflows/lint.yml new file mode 100644 index 0000000..ce5938d --- /dev/null +++ b/_libs/zca-js/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +name: lint + +on: + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "18.x" + - run: npm install + - run: npm run build:clean && npm run build:esm && npm run build:cjs + - run: npm run lint diff --git a/_libs/zca-js/.gitignore b/_libs/zca-js/.gitignore new file mode 100644 index 0000000..67d4ad2 --- /dev/null +++ b/_libs/zca-js/.gitignore @@ -0,0 +1,136 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +bun.lockb +cookies.json +credentials.json +/.dev +test/**/*.test.* diff --git a/_libs/zca-js/.husky/pre-commit b/_libs/zca-js/.husky/pre-commit new file mode 100644 index 0000000..ea5a55b --- /dev/null +++ b/_libs/zca-js/.husky/pre-commit @@ -0,0 +1 @@ +bunx lint-staged diff --git a/_libs/zca-js/.npmignore b/_libs/zca-js/.npmignore new file mode 100644 index 0000000..bae4ecb --- /dev/null +++ b/_libs/zca-js/.npmignore @@ -0,0 +1,15 @@ +.dev +examples +src +test + +bun.lockb +bun.lock +package-lock.json +tsconfig.json + +.husky +.github +.vscode + +scripts diff --git a/_libs/zca-js/.prettierrc b/_libs/zca-js/.prettierrc new file mode 100644 index 0000000..86ef8dc --- /dev/null +++ b/_libs/zca-js/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 4, + "printWidth": 120, + "semi": true +} diff --git a/_libs/zca-js/.vscode/settings.json b/_libs/zca-js/.vscode/settings.json new file mode 100644 index 0000000..910a2a5 --- /dev/null +++ b/_libs/zca-js/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules\\typescript\\lib" +} diff --git a/_libs/zca-js/CODE_OF_CONDUCT.md b/_libs/zca-js/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..158f87c --- /dev/null +++ b/_libs/zca-js/CODE_OF_CONDUCT.md @@ -0,0 +1,163 @@ +# Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement through: +- **GitHub Issues**: Create a private issue with the `[CONDUCT]` label +- **GitHub Discussions**: Use the "Community" category for general concerns +- **Direct Contact**: Reach out to any team member through GitHub +- **Team Members**: + - [@RFS-ADRENO](https://github.com/RFS-ADRENO) + - [@truong9c2208](https://github.com/truong9c2208) + - [@JustKemForFun](https://github.com/JustKemForFun) + +All complaints will be reviewed and investigated promptly and fairly by the team. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. + +## Special Considerations for zca-js + +Given that zca-js is an unofficial API library for Zalo, we have additional +guidelines specific to our project: + +### Responsible Usage + +* **Respect Zalo's Terms of Service**: While this is an unofficial library, + users should be aware of and respect Zalo's terms of service +* **Account Safety**: Do not encourage or provide guidance on circumventing + Zalo's security measures +* **Privacy**: Respect user privacy and do not share personal information + obtained through the API +* **Rate Limiting**: Encourage responsible usage that doesn't overload Zalo's + servers + +### Technical Discussions + +* **Security**: When discussing security-related topics, use appropriate + channels and follow responsible disclosure practices +* **Bugs and Issues**: Report bugs and issues through proper channels +* **Documentation**: Help improve documentation and examples for the community + +--- + +**Note**: This Code of Conduct is a living document and may be updated as our +community grows and evolves. \ No newline at end of file diff --git a/_libs/zca-js/CONTRIBUTING.md b/_libs/zca-js/CONTRIBUTING.md new file mode 100644 index 0000000..03bd6ee --- /dev/null +++ b/_libs/zca-js/CONTRIBUTING.md @@ -0,0 +1,470 @@ +# Contributing to zca-js + +Thank you for your interest in contributing to zca-js! This project is maintained by the community and we welcome all contributions. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [Development Setup](#development-setup) +- [How to Contribute](#how-to-contribute) +- [Pull Request Process](#pull-request-process) +- [Code Style Guidelines](#code-style-guidelines) +- [Testing Guidelines](#testing-guidelines) +- [Documentation Guidelines](#documentation-guidelines) +- [Security Guidelines](#security-guidelines) +- [Release Process](#release-process) +- [Getting Help](#getting-help) + +## Code of Conduct + +This project adheres to our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you agree to abide by these rules. + +## Getting Started + +### Prerequisites + +- Node.js >= 18.0.0 +- Bun (recommended) or npm +- Git + +### Fork and Clone + +1. Fork this repository +2. Clone your fork: + ```bash + git clone https://github.com/YOUR_USERNAME/zca-js.git + cd zca-js + ``` +3. Add upstream remote: + ```bash + git remote add upstream https://github.com/RFS-ADRENO/zca-js.git + ``` + +## Development Setup + +### Install Dependencies + +```bash +# Using Bun (recommended) +bun install + +# Or using npm +npm install +``` + +### Build Project + +```bash +# Build both ESM and CJS +bun run build + +# Build ESM only +bun run build:esm + +# Build CJS only +bun run build:cjs +``` + +### Run Tests + +```bash +# Run feature tests +bun run test:feat + +# Run specific test file +bun run test/test.ts +``` + +### Code Formatting + +```bash +# Format code with Prettier +bun run prettier +``` + +## How to Contribute + +### Types of Contributions + +We welcome the following types of contributions: + +- ๐Ÿ› **Bug Reports**: Report bugs and issues +- โœจ **Feature Requests**: Suggest new features +- ๐Ÿ”ง **Code Contributions**: Fix bugs and add features +- ๐Ÿ“š **Documentation**: Improve docs and examples +- ๐Ÿงช **Tests**: Add or improve tests +- ๐Ÿ”’ **Security**: Report security vulnerabilities +- ๐ŸŒ **Translations**: Translate docs to other languages + +### Before You Start + +1. **Check existing issues**: Search for existing issues before creating new ones +2. **Discuss major changes**: Create an issue to discuss major changes +3. **Follow the roadmap**: Check the current roadmap and priorities + +## Pull Request Process + +### Creating a Pull Request + +1. **Create a feature branch**: + ```bash + git checkout -b feature/your-feature-name + # or + git checkout -b fix/your-bug-fix + ``` + +2. **Make your changes**: + - Follow code style guidelines + - Add tests for new functionality + - Update documentation if needed + +3. **Test your changes**: + ```bash + bun run build + bun run test:feat + ``` + +4. **Commit your changes**: + ```bash + git add . + git commit -m "feat: add new API method for group management" + ``` + +5. **Push to your fork**: + ```bash + git push origin feature/your-feature-name + ``` + +6. **Create a Pull Request**: + - Use the provided PR template + - Link related issues + - Provide clear description of changes + +### PR Review Process + +1. **Automated checks** must pass +2. **Code review** by at least one maintainer +3. **Security review** for security-related changes +4. **Documentation review** for API changes +5. **Final approval** before merge + +## Code Style Guidelines + +### TypeScript Guidelines + +- Use TypeScript strict mode +- Prefer interfaces over types for object shapes +- Use meaningful variable and function names +- Add JSDoc comments for public APIs +- Use async/await over Promises when possible + +### Code Organization + +The project follows a modular structure for better maintainability and organization: + +``` +zca-js/ +โ”œโ”€โ”€ src/ # Source code directory +โ”‚ โ”œโ”€โ”€ apis/ # API methods (100+ files) +โ”‚ โ”‚ โ”œโ”€โ”€ sendMessage.ts # Core messaging functionality +โ”‚ โ”‚ โ”œโ”€โ”€ login.ts # Authentication methods +โ”‚ โ”‚ โ”œโ”€โ”€ loginQR.ts # QR code login +โ”‚ โ”‚ โ”œโ”€โ”€ listen.ts # Event listening +โ”‚ โ”‚ โ”œโ”€โ”€ sendVideo.ts # Video sending +โ”‚ โ”‚ โ”œโ”€โ”€ sendVoice.ts # Voice sending +โ”‚ โ”‚ โ”œโ”€โ”€ sendSticker.ts # Sticker sending +โ”‚ โ”‚ โ”œโ”€โ”€ createGroup.ts # Group management +โ”‚ โ”‚ โ”œโ”€โ”€ addReaction.ts # Message reactions +โ”‚ โ”‚ โ”œโ”€โ”€ uploadAttachment.ts # File uploads +โ”‚ โ”‚ โ””โ”€โ”€ ... # 90+ other API methods +โ”‚ โ”œโ”€โ”€ models/ # TypeScript interfaces and types +โ”‚ โ”‚ โ”œโ”€โ”€ Message.ts # Message interface +โ”‚ โ”‚ โ”œโ”€โ”€ Attachment.ts # File attachment types +โ”‚ โ”‚ โ”œโ”€โ”€ Reaction.ts # Reaction types +โ”‚ โ”‚ โ”œโ”€โ”€ FriendEvent.ts # Friend event types +โ”‚ โ”‚ โ”œโ”€โ”€ GroupEvent.ts # Group event types +โ”‚ โ”‚ โ”œโ”€โ”€ Typing.ts # Typing indicators +โ”‚ โ”‚ โ”œโ”€โ”€ SeenMessage.ts # Message seen events +โ”‚ โ”‚ โ”œโ”€โ”€ DeliveredMessage.ts # Message delivery events +โ”‚ โ”‚ โ”œโ”€โ”€ Undo.ts # Undo functionality +โ”‚ โ”‚ โ”œโ”€โ”€ Enum.ts # Enumerations +โ”‚ โ”‚ โ””โ”€โ”€ index.ts # Model exports +โ”‚ โ”œโ”€โ”€ Errors/ # Error handling +โ”‚ โ”‚ โ”œโ”€โ”€ ZaloApiError.ts # Custom API error class +โ”‚ โ”‚ โ””โ”€โ”€ index.ts # Error exports +โ”‚ โ”œโ”€โ”€ context.ts # Context management and state +โ”‚ โ”œโ”€โ”€ utils.ts # Utility functions and helpers +โ”‚ โ”œโ”€โ”€ zalo.ts # Main Zalo class implementation +โ”‚ โ”œโ”€โ”€ update.ts # Update checking functionality +โ”‚ โ””โ”€โ”€ index.ts # Public API exports +โ”œโ”€โ”€ examples/ # Usage examples +โ”‚ โ””โ”€โ”€ echobot.ts # Echo bot example +โ”œโ”€โ”€ test/ # Test files +โ”‚ โ”œโ”€โ”€ feat.ts # Feature tests +โ”‚ โ”œโ”€โ”€ feat.test.ts # Feature test suite +โ”‚ โ”œโ”€โ”€ test.ts # General tests +โ”‚ โ””โ”€โ”€ a.png # Test assets +โ”œโ”€โ”€ .github/ # GitHub configuration +โ”‚ โ””โ”€โ”€ ISSUE_TEMPLATE/ # Issue templates +โ”œโ”€โ”€ .dev/ # Development tools (developer generated) +โ”œโ”€โ”€ dist/ # Build output (generated) +โ”œโ”€โ”€ node_modules/ # Dependencies (generated) +โ”œโ”€โ”€ package.json # Project configuration +โ”œโ”€โ”€ tsconfig.json # TypeScript configuration +โ”œโ”€โ”€ rollup.config.js # Build configuration +โ”œโ”€โ”€ README.md # Project documentation +โ”œโ”€โ”€ CONTRIBUTING.md # Contribution guidelines +โ”œโ”€โ”€ SECURITY.md # Security policy +โ”œโ”€โ”€ CODE_OF_CONDUCT.md # Community guidelines +โ””โ”€โ”€ LICENSE # MIT License +``` + +### Key Directories Explained + +- **`src/apis/`**: Contains all API method implementations (~100 files) + +- **`src/models/`**: TypeScript interfaces and type definitions + - Core data structures for messages, events, and API responses + - Ensures type safety across the application + +- **`src/Errors/`**: Custom error handling + - `ZaloApiError.ts`: Handles API-specific errors + - Provides consistent error handling across the library + +- **`examples/`**: Usage examples and demonstrations + - `echobot.ts`: Complete example of a Zalo bot implementation + +- **`test/`**: Test suites and test assets + - Feature tests for core functionality + - Integration tests for API methods + +### Naming Conventions + +- **Files**: camelCase (e.g., `sendMessage.ts`) +- **Classes**: PascalCase (e.g., `ZaloApiError`) +- **Functions**: camelCase (e.g., `sendMessage`) +- **Constants**: UPPER_SNAKE_CASE (e.g., `API_BASE_URL`) +- **Interfaces**: PascalCase with `I` prefix (e.g., `IMessage`) + +### Error Handling + +```typescript +// Good +try { + const result = await api.sendMessage(message); + return result; +} catch (error) { + if (error instanceof ZaloApiError) { + throw error; + } + throw new ZaloApiError(`Failed to send message: ${error.message}`); +} + +// Bad +try { + const result = await api.sendMessage(message); + return result; +} catch (error) { + console.error(error); + return null; +} +``` + +## Testing Guidelines + +### Test Structure + +```typescript +describe('API Method', () => { + beforeEach(() => { + // Setup + }); + + afterEach(() => { + // Cleanup + }); + + it('should handle success case', async () => { + // Test implementation + }); + + it('should handle error case', async () => { + // Test error handling + }); +}); +``` + +### Testing Best Practices + +- Test both success and failure scenarios +- Mock external dependencies +- Use descriptive test names +- Keep tests independent +- Test edge cases and error conditions + +### Running Tests + +```bash +# Run all tests +bun run test:feat + +# Run specific test +bun run test/test.ts + +# Run with coverage (if available) +bun run test:coverage +``` + +## Documentation Guidelines + +### API Documentation + +- Document all public methods with JSDoc +- Include parameter types and descriptions +- Provide usage examples +- Document error conditions + +```typescript +/** + * Sends a message to a specific thread + * @param message - The message object containing content and metadata + * @param threadId - The ID of the thread to send the message to + * @param threadType - The type of thread (User or Group) + * @returns Promise - The sent message object + * @throws {ZaloApiError} When the API request fails + * @example + * ```typescript + * const message = await api.sendMessage( + * { msg: "Hello, world!" }, + * "123456789", + * ThreadType.User + * ); + * ``` + */ +async sendMessage(message: IMessage, threadId: string, threadType: ThreadType): Promise +``` + +### README Updates + +- Update README.md for new features +- Add examples for new APIs +- Update installation instructions if needed +- Keep the table of contents updated + +## Security Guidelines + +### Security Best Practices + +- Never commit sensitive data (tokens, passwords, etc.) +- Use environment variables for configuration +- Validate all user inputs +- Follow the principle of least privilege +- Report security issues privately + +### Security Reporting + +If you discover a security vulnerability: + +1. **DO NOT** create a public issue +2. Use the [SECURITY.md](SECURITY.md) reporting process +3. Create a private issue with `[SECURITY]` label +4. Contact team members directly for urgent issues + +### Code Security + +```typescript +// Good - Validate inputs +function sendMessage(content: string, threadId: string) { + if (!content || typeof content !== 'string') { + throw new ZaloApiError('Content must be a non-empty string'); + } + if (!threadId || typeof threadId !== 'string') { + throw new ZaloApiError('ThreadId must be a non-empty string'); + } + // Implementation +} + +// Bad - No validation +function sendMessage(content: any, threadId: any) { + // Implementation without validation +} +``` + +## Release Process + +### Versioning + +We follow [Semantic Versioning](https://semver.org/): + +- **MAJOR**: Breaking changes +- **MINOR**: New features (backward compatible) +- **PATCH**: Bug fixes (backward compatible) + +### Release Checklist + +- [ ] All tests pass +- [ ] Documentation is updated +- [ ] Version is bumped in package.json +- [ ] Build is successful +- [ ] Release notes are prepared + +### Publishing + +```bash +# Build the project +bun run build + +# Run tests +bun run test:feat + +# Publish to npm +npm publish +``` + +## Getting Help + +### Communication Channels + +- **GitHub Issues**: For bug reports and feature requests +- **GitHub Discussions**: For questions and general discussion +- **Pull Requests**: For code contributions +- **Security Issues**: Use `[SECURITY]` label + +### Team Members + +- [@RFS-ADRENO](https://github.com/RFS-ADRENO) +- [@truong9c2208](https://github.com/truong9c2208) +- [@JustKemForFun](https://github.com/JustKemForFun) + +### Resources + +- [API Documentation](https://zca-js.tdung.com) +- [Examples](examples/) +- [Security Policy](SECURITY.md) +- [Code of Conduct](CODE_OF_CONDUCT.md) + +## Special Considerations for zca-js + +### Unofficial API Library + +> [!IMPORTANT] +> โš ๏ธ zca-js is an unofficial API library for Zalo. Please be aware of: + +- **Account Risk**: Using this API may result in account suspension +- **Terms of Service**: Respect Zalo's ToS in your contributions +- **Rate Limiting**: Be mindful of API usage limits +- **Privacy**: Protect user privacy and data + +### Responsible Development + +- Test changes thoroughly before submitting +- Avoid introducing features that could harm users +- Consider the impact on Zalo's infrastructure +- Document any risks or limitations + +--- + +**Thank you for contributing to zca-js!** ๐Ÿš€ + +Your contributions help make this library better for the entire community. diff --git a/_libs/zca-js/LICENSE b/_libs/zca-js/LICENSE new file mode 100644 index 0000000..75a1138 --- /dev/null +++ b/_libs/zca-js/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 - 2025 RFS-ADRENO, truong9c2208, JustKemForFun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/_libs/zca-js/README.md b/_libs/zca-js/README.md new file mode 100644 index 0000000..5cabdd3 --- /dev/null +++ b/_libs/zca-js/README.md @@ -0,0 +1,225 @@ +# ZCA-JS + +> [!NOTE] +> This is an unofficial Zalo API for personal account. It work by simulating the browser to interact with Zalo Web. + +> [!WARNING] +> Using this API could get your account locked or banned. We are not responsible for any issues that may happen. Use it at your own risk. + +--- + +## Table of Contents + +- [Installation](#installation) + - [Migrate to V2](#migrate-to-v2) +- [Documentation](#documentation) +- [Basic Usages](#basic-usages) + - [Login](#login) + - [Listen for new messages](#listen-for-new-messages) + - [Send a message](#send-a-message) + - [Get/Send a sticker](#getsend-a-sticker) +- [Example](#example) +- [Projects & Useful Resources](#projects--useful-resources) +- [Contributing](#contributing) +- [License](#license) +- [Support Us](#support-us) + +## Installation + +```bash +bun add zca-js # or npm install zca-js +``` + +### Migrate to V2 + +Since official version 2.0.0, `zca-js` has removed sharp dependency for image metadata extraction. It now requires users to provide their own `imageMetadataGetter` function when initializing the `Zalo` class if they want to send images/gifs by file path. + +Example of custom `imageMetadataGetter` using `sharp`: + +```bash +bun add sharp # or npm install sharp +``` + +```javascript +import { Zalo } from "zca-js"; +import sharp from "sharp"; +import fs from "node:fs"; + +async function imageMetadataGetter(filePath) { + const data = await fs.promises.readFile(filePath); + const metadata = await sharp(data).metadata(); + return { + height: metadata.height, + width: metadata.width, + size: metadata.size || data.length, + }; +} + +const zalo = new Zalo({ + imageMetadataGetter, +}); +``` + +--- + +## Documentation + +See [API Documentation](https://zca-js.tdung.com) for more details. + +--- + +## Basic Usages + +### Login + +```javascript +import { Zalo } from "zca-js"; + +const zalo = new Zalo(); +const api = await zalo.loginQR(); +``` + +### Listen for new messages + +```javascript +import { Zalo, ThreadType } from "zca-js"; + +const zalo = new Zalo(); +const api = await zalo.loginQR(); + +api.listener.on("message", (message) => { + const isPlainText = typeof message.data.content === "string"; + + switch (message.type) { + case ThreadType.User: { + if (isPlainText) { + // received plain text direct message + } + break; + } + case ThreadType.Group: { + if (isPlainText) { + // received plain text group message + } + break; + } + } +}); + +api.listener.start(); +``` + +> [!IMPORTANT] +> Only one web listener can run per account at a time. If you open Zalo in the browser while the listener is active, the listener will be automatically stopped. + +### Send a message + +```javascript +import { Zalo, ThreadType } from "zca-js"; + +const zalo = new Zalo(); +const api = await zalo.loginQR(); + +// Echo bot +api.listener.on("message", (message) => { + const isPlainText = typeof message.data.content === "string"; + if (message.isSelf || !isPlainText) return; + + switch (message.type) { + case ThreadType.User: { + api.sendMessage( + { + msg: "echo: " + message.data.content, + quote: message.data, // the message to reply to (optional) + }, + message.threadId, + message.type, // ThreadType.User + ); + break; + } + case ThreadType.Group: { + api.sendMessage( + { + msg: "echo: " + message.data.content, + quote: message.data, // the message to reply to (optional) + }, + message.threadId, + message.type, // ThreadType.Group + ); + break; + } + } +}); + +api.listener.start(); +``` + +### Get/Send a sticker + +```javascript +api.getStickers("hello").then(async (stickerIds) => { + // Get the first sticker + const stickerObject = await api.getStickersDetail(stickerIds[0]); + api.sendMessageSticker( + stickerObject, + message.threadId, + message.type, // ThreadType.User or ThreadType.Group + ); +}); +``` + +--- + +## Example + +See [examples](examples) folder for more details. + +--- + +## Projects & Useful Resources + +
+ +| Repository | Description | +| :--- | :--- | +| [**ZaloDataExtractor**](https://github.com/JustKemForFun/ZaloDataExtractor) | A browser `Extension` to extract IMEI, cookies, and user agent from Zalo Web. | +| [**MultiZlogin**](https://github.com/ChickenAI/multizlogin) | A multi-account Zalo management system that lets you log in to and manage multiple accounts simultaneously, with proxy and webhook integration. | +| [**n8n-nodes-zalo-tools**](https://github.com/ChickenAI/zalo-node) | N8N node for personal Zalo account. | +| [**Zalo-F12**](https://github.com/ElectroHeavenVN/Zalo-F12) | A collection of JavaScript code snippets to paste into DevTools to change how Zalo Web/PC works. | +| [**Zalo-F12-Tools**](https://github.com/JustKemForFun/Zalo-F12-Tools) | Toggle hidden modes for Zalo Web. | + +
+ +--- + +## Contributing + +We welcome contributions from the community! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to: + +- ๐Ÿ› Report bugs and issues +- โœจ Suggest new features +- ๐Ÿ”ง Submit code contributions +- ๐Ÿ“š Improve documentation +- ๐Ÿงช Add or improve tests +- ๐Ÿ”’ Report security vulnerabilities + +For more information, please read our [Code of Conduct](CODE_OF_CONDUCT.md) and [Security Policy](SECURITY.md) before participating. + +--- + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +--- + +## **Support Us** + +- โญ **Star our repositories** if you find them useful! +- ๐Ÿ”„ **Share** with your network to help us grow +- ๐Ÿ’ก **Contribute** your ideas and code +- โ˜• **A coffee**: + - [Buy Me a Coffee](https://ko-fi.com/grosse) + - [Paypal](https://www.paypal.com/paypalme/dungto213) + - [VietQR](https://github.com/user-attachments/assets/e1f319d6-9d11-4082-8248-55b55e645caa) + - [Momo](https://me.momo.vn/gMIMulsaUqsbf6iAiXt3) diff --git a/_libs/zca-js/SECURITY.md b/_libs/zca-js/SECURITY.md new file mode 100644 index 0000000..6180eda --- /dev/null +++ b/_libs/zca-js/SECURITY.md @@ -0,0 +1,83 @@ +# Security Policy + +## Supported Versions + +We actively maintain and provide security updates for the following versions: + +| Version | Supported | +| ------- | ------------------ | +| 2.0.x | :white_check_mark: | +| 1.x.x | :x: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +We take security vulnerabilities seriously. If you discover a security vulnerability in zca-js, please follow these steps: + +### 1. **DO NOT** create a public GitHub issue +Security vulnerabilities should be reported privately to prevent potential exploitation. + +### 2. Report the vulnerability +Send an email to the maintainers with the following information: +- **Subject**: `[SECURITY] zca-js vulnerability report` +- **Description**: Detailed description of the vulnerability +- **Steps to reproduce**: Clear steps to reproduce the issue +- **Impact assessment**: Potential impact of the vulnerability +- **Suggested fix** (if available): Any suggestions for fixing the issue + +### 3. Response timeline +- **Initial response**: Within 48 hours +- **Status update**: Within 1 week +- **Resolution**: As soon as possible, typically within 30 days + +## Security Considerations + +### Important Warnings + +โš ๏ธ **This is an unofficial API library** that simulates browser interactions with Zalo Web. Please be aware of the following security considerations: + +1. **Account Risk**: Using this API may result in your Zalo account being locked or banned. Use at your own risk. + +2. **Authentication**: The library handles sensitive authentication data. Ensure proper security measures when storing or transmitting this information. + +3. **Rate Limiting**: Respect Zalo's rate limits to avoid triggering security measures. + +4. **Data Privacy**: Be mindful of user privacy and comply with relevant data protection regulations. + +### Best Practices + +1. **Environment Variables**: Store sensitive configuration in environment variables, not in code +2. **HTTPS Only**: Always use HTTPS when transmitting data +3. **Input Validation**: Validate all inputs before processing +4. **Error Handling**: Implement proper error handling to avoid information disclosure +5. **Regular Updates**: Keep the library updated to the latest version + +### Known Limitations + +- This library is not officially supported by Zalo +- API endpoints and behavior may change without notice +- No guarantee of service availability or stability +- May break with Zalo Web updates + +## Security Updates + +Security updates will be released as patch versions (e.g., 2.0.1, 2.0.2) and will be clearly marked in the changelog. + +## Contact Information + +For security-related issues, please contact: +- **GitHub Issues**: Create a private issue with the `[SECURITY]` label +- **GitHub Discussions**: Use the "Security" category for general security questions +- **Team Members**: + - [@RFS-ADRENO](https://github.com/RFS-ADRENO) + - [@truong9c2208](https://github.com/truong9c2208) + - [@JustKemForFun](https://github.com/JustKemForFun) +- **Alternative**: Contact any team member through GitHub for urgent security matters + +## Acknowledgments + +We appreciate security researchers and community members who responsibly disclose vulnerabilities. Contributors will be acknowledged in our security advisories unless they prefer to remain anonymous. + +--- + +**Note**: This security policy is subject to change. Please check back regularly for updates. \ No newline at end of file diff --git a/_libs/zca-js/bun.lock b/_libs/zca-js/bun.lock new file mode 100644 index 0000000..b1c9140 --- /dev/null +++ b/_libs/zca-js/bun.lock @@ -0,0 +1,546 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "zca-js", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0", + }, + "devDependencies": { + "@eslint/js": "^9.34.0", + "@rollup/plugin-typescript": "^12.1.0", + "@types/bun": "^1.1.14", + "@types/crypto-js": "^4.2.2", + "@types/json-bigint": "^1.0.4", + "@types/node": "^20.14.10", + "@types/pako": "^2.0.3", + "@types/semver": "^7.5.8", + "@types/spark-md5": "^3.0.4", + "@types/ws": "^8.5.11", + "eslint": "^9.34.0", + "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.1.5", + "prettier": "^3.3.3", + "rimraf": "^5.0.10", + "rollup": "^4.24.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.41.0", + }, + }, + }, + "packages": { + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="], + + "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.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" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + + "@eslint/js": ["@eslint/js@9.34.0", "", {}, "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "@rollup/plugin-typescript": ["@rollup/plugin-typescript@12.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.14.0||^3.0.0||^4.0.0", "tslib": "*", "typescript": ">=3.7.0" } }, "sha512-Kzs8KGJofe7cfTRODsnG1jNGxSvU8gVoNNd7Z/QaY25AYwe2LSSUpx/kPxqF38NYkpR8de3m51r9uwJpDlz6dg=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.1.2", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^2.3.1" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" } }, "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.24.0", "", { "os": "android", "cpu": "arm" }, "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.24.0", "", { "os": "android", "cpu": "arm64" }, "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.24.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.24.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.24.0", "", { "os": "linux", "cpu": "arm" }, "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.24.0", "", { "os": "linux", "cpu": "arm" }, "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw=="], + + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.24.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.24.0", "", { "os": "linux", "cpu": "none" }, "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.24.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.24.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.24.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.24.0", "", { "os": "win32", "cpu": "x64" }, "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw=="], + + "@types/bun": ["@types/bun@1.1.14", "", { "dependencies": { "bun-types": "1.1.37" } }, "sha512-opVYiFGtO2af0dnWBdZWlioLBoxSdDO5qokaazLhq8XQtGZbY4pY3/JxY8Zdf/hEwGubbp7ErZXoN1+h2yesxA=="], + + "@types/crypto-js": ["@types/crypto-js@4.2.2", "", {}, "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ=="], + + "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], + + "@types/json-bigint": ["@types/json-bigint@1.0.4", "", {}, "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@20.14.10", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ=="], + + "@types/pako": ["@types/pako@2.0.3", "", {}, "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q=="], + + "@types/semver": ["@types/semver@7.5.8", "", {}, "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ=="], + + "@types/spark-md5": ["@types/spark-md5@3.0.4", "", {}, "sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q=="], + + "@types/ws": ["@types/ws@8.5.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.41.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/type-utils": "8.41.0", "@typescript-eslint/utils": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.41.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.41.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.41.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.41.0", "@typescript-eslint/types": "^8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0" } }, "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.41.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.41.0", "", {}, "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.41.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.41.0", "@typescript-eslint/tsconfig-utils": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.41.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], + + "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "bun-types": ["bun-types@1.1.37", "", { "dependencies": { "@types/node": "~20.12.8", "@types/ws": "~8.5.10" } }, "sha512-C65lv6eBr3LPJWFZ2gswyrGZ82ljnH8flVE03xeXxKhi2ZGtFiO4isRKTKnitbSqtRAcaqYSR6djt1whI66AbA=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "crypto-js": ["crypto-js@4.2.0", "", {}, "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.34.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="], + + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "is-core-module": ["is-core-module@2.15.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + + "lint-staged": ["lint-staged@16.1.5", "", { "dependencies": { "chalk": "^5.5.0", "commander": "^14.0.0", "debug": "^4.4.1", "lilconfig": "^3.1.3", "listr2": "^9.0.1", "micromatch": "^4.0.8", "nano-spawn": "^1.0.2", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A=="], + + "listr2": ["listr2@9.0.2", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-VVd7cS6W+vLJu2wmq4QmfVj14Iep7cz4r/OWNk36Aq5ZOY7G8/BfCrQFexcwB1OIxB3yERiePfE/REBjEFulag=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nano-spawn": ["nano-spawn@1.0.2", "", {}, "sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "optionator": ["optionator@0.9.4", "", { "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" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.3.3", "", { "bin": "bin/prettier.cjs" }, "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": "dist/esm/bin.mjs" }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], + + "rollup": ["rollup@4.24.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.24.0", "@rollup/rollup-android-arm64": "4.24.0", "@rollup/rollup-darwin-arm64": "4.24.0", "@rollup/rollup-darwin-x64": "4.24.0", "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", "@rollup/rollup-linux-arm-musleabihf": "4.24.0", "@rollup/rollup-linux-arm64-gnu": "4.24.0", "@rollup/rollup-linux-arm64-musl": "4.24.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", "@rollup/rollup-linux-riscv64-gnu": "4.24.0", "@rollup/rollup-linux-s390x-gnu": "4.24.0", "@rollup/rollup-linux-x64-gnu": "4.24.0", "@rollup/rollup-linux-x64-musl": "4.24.0", "@rollup/rollup-win32-arm64-msvc": "4.24.0", "@rollup/rollup-win32-ia32-msvc": "4.24.0", "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "semver": ["semver@7.6.3", "", { "bin": "bin/semver.js" }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + + "spark-md5": ["spark-md5@3.0.2", "", {}, "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tldts": ["tldts@6.1.64", "", { "dependencies": { "tldts-core": "^6.1.64" }, "bin": "bin/cli.js" }, "sha512-ph4AE5BXWIOsSy9stpoeo7bYe/Cy7VfpciIH4RhVZUPItCJmhqWCN0EVzxd8BOHiyNb42vuJc6NWTjJkg91Tuw=="], + + "tldts-core": ["tldts-core@6.1.64", "", {}, "sha512-uqnl8vGV16KsyflHOzqrYjjArjfXaU6rMPXYy2/ZWoRKCkXtghgB4VwTDXUG+t0OTGeSewNAG31/x1gCTfLt+Q=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "tough-cookie": ["tough-cookie@5.0.0", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "tslib": ["tslib@2.6.3", "", {}, "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.5.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ=="], + + "typescript-eslint": ["typescript-eslint@8.41.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.41.0", "@typescript-eslint/parser": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw=="], + + "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "bun-types/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "lint-staged/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.0.0", "", { "dependencies": { "get-east-asian-width": "^1.0.0" } }, "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/_libs/zca-js/eslint.config.js b/_libs/zca-js/eslint.config.js new file mode 100644 index 0000000..491ef6a --- /dev/null +++ b/_libs/zca-js/eslint.config.js @@ -0,0 +1,21 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; +import { defineConfig, globalIgnores } from "eslint/config"; + +export default defineConfig([ + { + files: ["**/*.{js,ts}"], + plugins: { js }, + extends: ["js/recommended"], + languageOptions: { globals: globals.node }, + }, + tseslint.configs.recommended, + { + rules: { + "@typescript-eslint/consistent-type-imports": "error", + "no-extra-boolean-cast": "off", + }, + }, + globalIgnores(["dist/*"]), +]); diff --git a/_libs/zca-js/examples/echobot.ts b/_libs/zca-js/examples/echobot.ts new file mode 100644 index 0000000..4620b4a --- /dev/null +++ b/_libs/zca-js/examples/echobot.ts @@ -0,0 +1,56 @@ +import fs from "node:fs"; +import type { API, Credentials} from "../src/index.js"; +import { ThreadType, Zalo } from "../src/index.js"; + +const zalo = new Zalo(); + +const credentials: Partial = { + cookie: JSON.parse(fs.readFileSync("./examples/cookies.json", "utf-8")), + imei: "", + userAgent: "", // Same userAgent is recommended +}; + +const isValid = (credentials: Partial): credentials is Credentials => { + return !!credentials.cookie && !!credentials.imei && !!credentials.userAgent; +}; + +let api: API | undefined; + +if (isValid(credentials)) { + api = await zalo.login(credentials); +} else { + api = await zalo.loginQR({ qrPath: "./qr.png", userAgent: "" }, () => { + console.log("QR Code has been generated"); + }); + const context = api.getContext(); + + // Save credentials for later use + credentials.imei = context.imei; + credentials.userAgent = context.userAgent; + credentials.cookie = context.cookie.toJSON()?.cookies; +} + +const { listener } = api; + +listener.on("message", (message) => { + const isDirectMessage = message.type === ThreadType.User; + + if (isDirectMessage && !message.isSelf && typeof message.data.content === "string") { + console.log("Message:", message.data.content, message.threadId); + api.sendMessage(message.data.content, message.threadId, message.type); + } +}); + +listener.onConnected(() => { + console.log("Connected"); +}); + +listener.onClosed(() => { + console.log("Closed"); +}); + +listener.onError((error) => { + console.error("Error:", error); +}); + +listener.start(); diff --git a/_libs/zca-js/examples/login.ts b/_libs/zca-js/examples/login.ts new file mode 100644 index 0000000..4c5b4b8 --- /dev/null +++ b/_libs/zca-js/examples/login.ts @@ -0,0 +1,40 @@ +import type { Credentials } from "../src"; + +import { Zalo } from "../src"; +import fs from "node:fs"; + +const zalo = new Zalo(); +const credentialsPath = "./examples/credentials.json"; + +function validateCredentials(credentials: Partial): credentials is Credentials { + return !!credentials.cookie && !!credentials.imei && !!credentials.userAgent; +} + +function getCredentials() { + if (!fs.existsSync(credentialsPath)) { + return null; + } + + const raw = fs.readFileSync(credentialsPath, "utf-8") || "{}"; + const credentials = JSON.parse(raw); + return validateCredentials(credentials) ? credentials : null; +} + +const credentials = getCredentials(); + +const isValidCredentials = credentials !== null; +const api = isValidCredentials ? await zalo.login(credentials) : await zalo.loginQR(); + +if (!isValidCredentials) { + const apiContext = api.getContext(); + const credentials: Credentials = { + cookie: apiContext.cookie.toJSON()?.cookies || [], + imei: apiContext.imei, + userAgent: apiContext.userAgent, + }; + + fs.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), "utf-8"); +} + +const myProfile = await api.fetchAccountInfo(); +console.log("My profile:", myProfile); diff --git a/_libs/zca-js/index.d.ts b/_libs/zca-js/index.d.ts new file mode 100644 index 0000000..964ed8c --- /dev/null +++ b/_libs/zca-js/index.d.ts @@ -0,0 +1,2 @@ +export * from "./dist"; +export as namespace Zalo; diff --git a/_libs/zca-js/package-lock.json b/_libs/zca-js/package-lock.json new file mode 100644 index 0000000..9f029a6 --- /dev/null +++ b/_libs/zca-js/package-lock.json @@ -0,0 +1,3505 @@ +{ + "name": "zca-js", + "version": "2.1.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zca-js", + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@eslint/js": "^9.34.0", + "@rollup/plugin-typescript": "^12.1.0", + "@types/bun": "^1.1.14", + "@types/crypto-js": "^4.2.2", + "@types/json-bigint": "^1.0.4", + "@types/node": "^20.14.10", + "@types/pako": "^2.0.3", + "@types/semver": "^7.5.8", + "@types/spark-md5": "^3.0.4", + "@types/ws": "^8.5.11", + "eslint": "^9.34.0", + "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.1.5", + "prettier": "^3.3.3", + "rimraf": "^5.0.10", + "rollup": "^4.24.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.41.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/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, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.0.tgz", + "integrity": "sha512-Kzs8KGJofe7cfTRODsnG1jNGxSvU8gVoNNd7Z/QaY25AYwe2LSSUpx/kPxqF38NYkpR8de3m51r9uwJpDlz6dg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/bun": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.14.tgz", + "integrity": "sha512-opVYiFGtO2af0dnWBdZWlioLBoxSdDO5qokaazLhq8XQtGZbY4pY3/JxY8Zdf/hEwGubbp7ErZXoN1+h2yesxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.1.37" + } + }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.4.tgz", + "integrity": "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pako": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz", + "integrity": "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/spark-md5": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/spark-md5/-/spark-md5-3.0.4.tgz", + "integrity": "sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.11", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", + "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", + "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/type-utils": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.41.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz", + "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", + "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.41.0", + "@typescript-eslint/types": "^8.41.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", + "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", + "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", + "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz", + "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", + "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.41.0", + "@typescript-eslint/tsconfig-utils": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", + "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "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-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "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/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bun-types": { + "version": "1.1.37", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.37.tgz", + "integrity": "sha512-C65lv6eBr3LPJWFZ2gswyrGZ82ljnH8flVE03xeXxKhi2ZGtFiO4isRKTKnitbSqtRAcaqYSR6djt1whI66AbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "~20.12.8", + "@types/ws": "~8.5.10" + } + }, + "node_modules/bun-types/node_modules/@types/node": { + "version": "20.12.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz", + "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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, + "license": "MIT", + "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/chalk/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", + "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "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, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "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, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "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, + "license": "BSD-2-Clause", + "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, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "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, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "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": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "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, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "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/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.5.tgz", + "integrity": "sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.5.0", + "commander": "^14.0.0", + "debug": "^4.4.1", + "lilconfig": "^3.1.3", + "listr2": "^9.0.1", + "micromatch": "^4.0.8", + "nano-spawn": "^1.0.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.2.tgz", + "integrity": "sha512-VVd7cS6W+vLJu2wmq4QmfVj14Iep7cz4r/OWNk36Aq5ZOY7G8/BfCrQFexcwB1OIxB3yERiePfE/REBjEFulag==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/nano-spawn": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.2.tgz", + "integrity": "sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=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, + "license": "MIT" + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=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, + "license": "MIT", + "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" + } + ], + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "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, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.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" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/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-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "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-ansi-cjs/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/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, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/tldts": { + "version": "6.1.64", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.64.tgz", + "integrity": "sha512-ph4AE5BXWIOsSy9stpoeo7bYe/Cy7VfpciIH4RhVZUPItCJmhqWCN0EVzxd8BOHiyNb42vuJc6NWTjJkg91Tuw==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.64" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.64", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.64.tgz", + "integrity": "sha512-uqnl8vGV16KsyflHOzqrYjjArjfXaU6rMPXYy2/ZWoRKCkXtghgB4VwTDXUG+t0OTGeSewNAG31/x1gCTfLt+Q==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", + "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.41.0", + "@typescript-eslint/parser": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "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, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/_libs/zca-js/package.json b/_libs/zca-js/package.json new file mode 100644 index 0000000..ededf22 --- /dev/null +++ b/_libs/zca-js/package.json @@ -0,0 +1,78 @@ +{ + "name": "zca-js", + "version": "2.1.2", + "description": "Unofficial Zalo API for JavaScript", + "main": "dist/index.js", + "type": "module", + "exports": { + ".": { + "require": "./dist/cjs/index.cjs", + "types": "./index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "prebuild": "bun run scripts/buildAPI.js", + "build:clean": "rimraf dist", + "build:esm": "tsc", + "build:cjs": "rollup -c rollup.config.js", + "build": "bun run build:clean && bun run build:esm && bun run build:cjs", + "test:feat": "bun run test/feat.ts", + "prettier": "prettier --write .", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "prepare": "husky" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/RFS-ADRENO/zca-js.git" + }, + "homepage": "https://github.com/RFS-ADRENO/zca-js", + "bugs": { + "url": "https://github.com/RFS-ADRENO/zca-js/issues" + }, + "engines": { + "node": ">=18.0.0" + }, + "keywords": [ + "chatbot", + "zalo", + "api" + ], + "author": "RFS-ADRENO, truong9c2208, JustKemForFun", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@eslint/js": "^9.34.0", + "@rollup/plugin-typescript": "^12.1.0", + "@types/bun": "^1.1.14", + "@types/crypto-js": "^4.2.2", + "@types/json-bigint": "^1.0.4", + "@types/node": "^20.14.10", + "@types/pako": "^2.0.3", + "@types/semver": "^7.5.8", + "@types/spark-md5": "^3.0.4", + "@types/ws": "^8.5.11", + "eslint": "^9.34.0", + "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.1.5", + "prettier": "^3.3.3", + "rimraf": "^5.0.10", + "rollup": "^4.24.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.41.0" + }, + "lint-staged": { + "*.{js,ts}": "bunx eslint" + } +} diff --git a/_libs/zca-js/rollup.config.js b/_libs/zca-js/rollup.config.js new file mode 100644 index 0000000..4a3e3d0 --- /dev/null +++ b/_libs/zca-js/rollup.config.js @@ -0,0 +1,28 @@ +// rollup.config.js + +export default { + input: "dist/index.js", + output: { + dir: "dist/cjs", + entryFileNames: "[name].cjs", + format: "cjs", + preserveModules: true, + }, + external: [ + "crypto-js", + "events", + "form-data", + "fs", + "node:crypto", + "node:fs", + "node:fs/promises", + "node:path", + "pako", + "semver", + "sharp", + "spark-md5", + "tough-cookie", + "ws", + "json-bigint" + ], +}; diff --git a/_libs/zca-js/scripts/buildAPI.js b/_libs/zca-js/scripts/buildAPI.js new file mode 100644 index 0000000..40f7f31 --- /dev/null +++ b/_libs/zca-js/scripts/buildAPI.js @@ -0,0 +1,72 @@ +import fs from "fs"; +import path from "path"; + +const apisDir = path.join(process.cwd(), "src", "apis"); +const outputFile = path.join(process.cwd(), "src", "apis.ts"); + +function getAllApiFiles(dir) { + const ignoreFiles = ["listen.ts", "login.ts", "loginQR.ts", "custom.ts"]; + return fs + .readdirSync(dir) + .filter((file) => file.endsWith(".ts") && !ignoreFiles.includes(file)) + .map((file) => { + const nonExtension = file.slice(0, -3); // Remove .ts extension + + return { + name: nonExtension, + factoryName: `${nonExtension}Factory`, + }; + }); +} + +function generateAPIsFile() { + const allApiFiles = getAllApiFiles(apisDir); + + const importLines = allApiFiles.map((file) => { + return `import { ${file.factoryName} } from "./apis/${file.name}.js";`; + }); + + const propertyLines = allApiFiles.map((file) => { + return ` public ${file.name}: ReturnType;`; + }); + + const constructorLines = allApiFiles.map((file) => { + return ` this.${file.name} = ${file.factoryName}(ctx, this);`; + }); + + const emptyNewLine = "\n"; + + const content = + "" + + 'import { Listener } from "./apis/listen.js";\n' + + emptyNewLine + + importLines.join("\n") + + emptyNewLine + + 'import { customFactory } from "./apis/custom.js";\n' + + 'import type { ZPWServiceMap, ContextSession } from "./context.js";\n' + + emptyNewLine + + "export class API {\n" + + " public zpwServiceMap: ZPWServiceMap;\n" + + " public listener: Listener;\n" + + emptyNewLine + + propertyLines.join("\n") + + emptyNewLine + + emptyNewLine + + " public custom: ReturnType;\n" + + emptyNewLine + + " constructor(ctx: ContextSession, zpwServiceMap: ZPWServiceMap, wsUrls: string[]) {\n" + + " this.zpwServiceMap = zpwServiceMap;\n" + + " this.listener = new Listener(ctx, wsUrls);\n" + + emptyNewLine + + constructorLines.join("\n") + + emptyNewLine + + emptyNewLine + + " this.custom = customFactory(ctx, this);\n" + + " }\n" + + "}\n"; + + fs.writeFileSync(outputFile, content, "utf-8"); + console.log(`\nAPIs file generated at ${outputFile}\n`); +} + +generateAPIsFile(); diff --git a/_libs/zca-js/src/Errors/ZaloApiError.ts b/_libs/zca-js/src/Errors/ZaloApiError.ts new file mode 100644 index 0000000..bd2adcb --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiError.ts @@ -0,0 +1,10 @@ +export class ZaloApiError extends Error { + public code: number | null; + + constructor(message: string, code?: number) { + super(message); + + this.name = "ZcaApiError"; + this.code = code || null; + } +} diff --git a/_libs/zca-js/src/Errors/ZaloApiLoginQRAborted.ts b/_libs/zca-js/src/Errors/ZaloApiLoginQRAborted.ts new file mode 100644 index 0000000..1ed55d7 --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiLoginQRAborted.ts @@ -0,0 +1,8 @@ +import { ZaloApiError } from "./ZaloApiError.js"; + +export class ZaloApiLoginQRAborted extends ZaloApiError { + constructor(message: string = "Operation aborted") { + super(message); + this.name = "ZaloApiLoginQRAborted"; + } +} diff --git a/_libs/zca-js/src/Errors/ZaloApiLoginQRDeclined.ts b/_libs/zca-js/src/Errors/ZaloApiLoginQRDeclined.ts new file mode 100644 index 0000000..845b8fd --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiLoginQRDeclined.ts @@ -0,0 +1,8 @@ +import { ZaloApiError } from "./ZaloApiError.js"; + +export class ZaloApiLoginQRDeclined extends ZaloApiError { + constructor(message: string = "Login QR request declined") { + super(message); + this.name = "ZaloApiLoginQRDeclined"; + } +} diff --git a/_libs/zca-js/src/Errors/ZaloApiMissingImageMetadataGetter.ts b/_libs/zca-js/src/Errors/ZaloApiMissingImageMetadataGetter.ts new file mode 100644 index 0000000..2db29b4 --- /dev/null +++ b/_libs/zca-js/src/Errors/ZaloApiMissingImageMetadataGetter.ts @@ -0,0 +1,8 @@ +import { ZaloApiError } from "./ZaloApiError.js"; + +export class ZaloApiMissingImageMetadataGetter extends ZaloApiError { + constructor() { + super("Missing `imageMetadataGetter`. Please provide it in the Zalo object options."); + this.name = "ZaloApiMissingImageMetadataGetter"; + } +} diff --git a/_libs/zca-js/src/Errors/index.ts b/_libs/zca-js/src/Errors/index.ts new file mode 100644 index 0000000..dc73921 --- /dev/null +++ b/_libs/zca-js/src/Errors/index.ts @@ -0,0 +1,4 @@ +export * from "./ZaloApiError.js"; +export * from "./ZaloApiMissingImageMetadataGetter.js"; +export * from "./ZaloApiLoginQRAborted.js"; +export * from "./ZaloApiLoginQRDeclined.js"; diff --git a/_libs/zca-js/src/apis.ts b/_libs/zca-js/src/apis.ts new file mode 100644 index 0000000..a3c914a --- /dev/null +++ b/_libs/zca-js/src/apis.ts @@ -0,0 +1,455 @@ +import { Listener } from "./apis/listen.js"; + +import { acceptFriendRequestFactory } from "./apis/acceptFriendRequest.js"; +import { addGroupBlockedMemberFactory } from "./apis/addGroupBlockedMember.js"; +import { addGroupDeputyFactory } from "./apis/addGroupDeputy.js"; +import { addPollOptionsFactory } from "./apis/addPollOptions.js"; +import { addQuickMessageFactory } from "./apis/addQuickMessage.js"; +import { addReactionFactory } from "./apis/addReaction.js"; +import { addUnreadMarkFactory } from "./apis/addUnreadMark.js"; +import { addUserToGroupFactory } from "./apis/addUserToGroup.js"; +import { blockUserFactory } from "./apis/blockUser.js"; +import { blockViewFeedFactory } from "./apis/blockViewFeed.js"; +import { changeAccountAvatarFactory } from "./apis/changeAccountAvatar.js"; +import { changeFriendAliasFactory } from "./apis/changeFriendAlias.js"; +import { changeGroupAvatarFactory } from "./apis/changeGroupAvatar.js"; +import { changeGroupNameFactory } from "./apis/changeGroupName.js"; +import { changeGroupOwnerFactory } from "./apis/changeGroupOwner.js"; +import { createAutoReplyFactory } from "./apis/createAutoReply.js"; +import { createCatalogFactory } from "./apis/createCatalog.js"; +import { createGroupFactory } from "./apis/createGroup.js"; +import { createNoteFactory } from "./apis/createNote.js"; +import { createPollFactory } from "./apis/createPoll.js"; +import { createProductCatalogFactory } from "./apis/createProductCatalog.js"; +import { createReminderFactory } from "./apis/createReminder.js"; +import { deleteAutoReplyFactory } from "./apis/deleteAutoReply.js"; +import { deleteAvatarFactory } from "./apis/deleteAvatar.js"; +import { deleteCatalogFactory } from "./apis/deleteCatalog.js"; +import { deleteChatFactory } from "./apis/deleteChat.js"; +import { deleteGroupInviteBoxFactory } from "./apis/deleteGroupInviteBox.js"; +import { deleteMessageFactory } from "./apis/deleteMessage.js"; +import { deleteProductCatalogFactory } from "./apis/deleteProductCatalog.js"; +import { disableGroupLinkFactory } from "./apis/disableGroupLink.js"; +import { disperseGroupFactory } from "./apis/disperseGroup.js"; +import { editNoteFactory } from "./apis/editNote.js"; +import { editReminderFactory } from "./apis/editReminder.js"; +import { enableGroupLinkFactory } from "./apis/enableGroupLink.js"; +import { fetchAccountInfoFactory } from "./apis/fetchAccountInfo.js"; +import { findUserFactory } from "./apis/findUser.js"; +import { findUserByUsernameFactory } from "./apis/findUserByUsername.js"; +import { forwardMessageFactory } from "./apis/forwardMessage.js"; +import { getAliasListFactory } from "./apis/getAliasList.js"; +import { getAllFriendsFactory } from "./apis/getAllFriends.js"; +import { getAllGroupsFactory } from "./apis/getAllGroups.js"; +import { getArchivedChatListFactory } from "./apis/getArchivedChatList.js"; +import { getAutoDeleteChatFactory } from "./apis/getAutoDeleteChat.js"; +import { getAutoReplyListFactory } from "./apis/getAutoReplyList.js"; +import { getAvatarListFactory } from "./apis/getAvatarList.js"; +import { getAvatarUrlProfileFactory } from "./apis/getAvatarUrlProfile.js"; +import { getBizAccountFactory } from "./apis/getBizAccount.js"; +import { getCatalogListFactory } from "./apis/getCatalogList.js"; +import { getCloseFriendsFactory } from "./apis/getCloseFriends.js"; +import { getContextFactory } from "./apis/getContext.js"; +import { getCookieFactory } from "./apis/getCookie.js"; +import { getFriendBoardListFactory } from "./apis/getFriendBoardList.js"; +import { getFriendOnlinesFactory } from "./apis/getFriendOnlines.js"; +import { getFriendRecommendationsFactory } from "./apis/getFriendRecommendations.js"; +import { getFriendRequestStatusFactory } from "./apis/getFriendRequestStatus.js"; +import { getFullAvatarFactory } from "./apis/getFullAvatar.js"; +import { getGroupBlockedMemberFactory } from "./apis/getGroupBlockedMember.js"; +import { getGroupChatHistoryFactory } from "./apis/getGroupChatHistory.js"; +import { getGroupInfoFactory } from "./apis/getGroupInfo.js"; +import { getGroupInviteBoxInfoFactory } from "./apis/getGroupInviteBoxInfo.js"; +import { getGroupInviteBoxListFactory } from "./apis/getGroupInviteBoxList.js"; +import { getGroupLinkDetailFactory } from "./apis/getGroupLinkDetail.js"; +import { getGroupLinkInfoFactory } from "./apis/getGroupLinkInfo.js"; +import { getGroupMembersInfoFactory } from "./apis/getGroupMembersInfo.js"; +import { getHiddenConversationsFactory } from "./apis/getHiddenConversations.js"; +import { getLabelsFactory } from "./apis/getLabels.js"; +import { getListBoardFactory } from "./apis/getListBoard.js"; +import { getListReminderFactory } from "./apis/getListReminder.js"; +import { getMultiUsersByPhonesFactory } from "./apis/getMultiUsersByPhones.js"; +import { getMuteFactory } from "./apis/getMute.js"; +import { getOwnIdFactory } from "./apis/getOwnId.js"; +import { getPendingGroupMembersFactory } from "./apis/getPendingGroupMembers.js"; +import { getPinConversationsFactory } from "./apis/getPinConversations.js"; +import { getPollDetailFactory } from "./apis/getPollDetail.js"; +import { getProductCatalogListFactory } from "./apis/getProductCatalogList.js"; +import { getQRFactory } from "./apis/getQR.js"; +import { getQuickMessageListFactory } from "./apis/getQuickMessageList.js"; +import { getRelatedFriendGroupFactory } from "./apis/getRelatedFriendGroup.js"; +import { getReminderFactory } from "./apis/getReminder.js"; +import { getReminderResponsesFactory } from "./apis/getReminderResponses.js"; +import { getSentFriendRequestFactory } from "./apis/getSentFriendRequest.js"; +import { getSettingsFactory } from "./apis/getSettings.js"; +import { getStickerCategoryDetailFactory } from "./apis/getStickerCategoryDetail.js"; +import { getStickersFactory } from "./apis/getStickers.js"; +import { getStickersDetailFactory } from "./apis/getStickersDetail.js"; +import { getUnreadMarkFactory } from "./apis/getUnreadMark.js"; +import { getUserInfoFactory } from "./apis/getUserInfo.js"; +import { inviteUserToGroupsFactory } from "./apis/inviteUserToGroups.js"; +import { joinGroupInviteBoxFactory } from "./apis/joinGroupInviteBox.js"; +import { joinGroupLinkFactory } from "./apis/joinGroupLink.js"; +import { keepAliveFactory } from "./apis/keepAlive.js"; +import { lastOnlineFactory } from "./apis/lastOnline.js"; +import { leaveGroupFactory } from "./apis/leaveGroup.js"; +import { lockPollFactory } from "./apis/lockPoll.js"; +import { parseLinkFactory } from "./apis/parseLink.js"; +import { rejectFriendRequestFactory } from "./apis/rejectFriendRequest.js"; +import { removeFriendFactory } from "./apis/removeFriend.js"; +import { removeFriendAliasFactory } from "./apis/removeFriendAlias.js"; +import { removeGroupBlockedMemberFactory } from "./apis/removeGroupBlockedMember.js"; +import { removeGroupDeputyFactory } from "./apis/removeGroupDeputy.js"; +import { removeQuickMessageFactory } from "./apis/removeQuickMessage.js"; +import { removeReminderFactory } from "./apis/removeReminder.js"; +import { removeUnreadMarkFactory } from "./apis/removeUnreadMark.js"; +import { removeUserFromGroupFactory } from "./apis/removeUserFromGroup.js"; +import { resetHiddenConversPinFactory } from "./apis/resetHiddenConversPin.js"; +import { reuseAvatarFactory } from "./apis/reuseAvatar.js"; +import { reviewPendingMemberRequestFactory } from "./apis/reviewPendingMemberRequest.js"; +import { searchStickerFactory } from "./apis/searchSticker.js"; +import { sendBankCardFactory } from "./apis/sendBankCard.js"; +import { sendCardFactory } from "./apis/sendCard.js"; +import { sendDeliveredEventFactory } from "./apis/sendDeliveredEvent.js"; +import { sendFriendRequestFactory } from "./apis/sendFriendRequest.js"; +import { sendLinkFactory } from "./apis/sendLink.js"; +import { sendMessageFactory } from "./apis/sendMessage.js"; +import { sendReportFactory } from "./apis/sendReport.js"; +import { sendSeenEventFactory } from "./apis/sendSeenEvent.js"; +import { sendStickerFactory } from "./apis/sendSticker.js"; +import { sendTypingEventFactory } from "./apis/sendTypingEvent.js"; +import { sendVideoFactory } from "./apis/sendVideo.js"; +import { sendVoiceFactory } from "./apis/sendVoice.js"; +import { setHiddenConversationsFactory } from "./apis/setHiddenConversations.js"; +import { setMuteFactory } from "./apis/setMute.js"; +import { setPinnedConversationsFactory } from "./apis/setPinnedConversations.js"; +import { sharePollFactory } from "./apis/sharePoll.js"; +import { unblockUserFactory } from "./apis/unblockUser.js"; +import { undoFactory } from "./apis/undo.js"; +import { undoFriendRequestFactory } from "./apis/undoFriendRequest.js"; +import { updateActiveStatusFactory } from "./apis/updateActiveStatus.js"; +import { updateArchivedChatListFactory } from "./apis/updateArchivedChatList.js"; +import { updateAutoDeleteChatFactory } from "./apis/updateAutoDeleteChat.js"; +import { updateAutoReplyFactory } from "./apis/updateAutoReply.js"; +import { updateCatalogFactory } from "./apis/updateCatalog.js"; +import { updateGroupSettingsFactory } from "./apis/updateGroupSettings.js"; +import { updateHiddenConversPinFactory } from "./apis/updateHiddenConversPin.js"; +import { updateLabelsFactory } from "./apis/updateLabels.js"; +import { updateLangFactory } from "./apis/updateLang.js"; +import { updateProductCatalogFactory } from "./apis/updateProductCatalog.js"; +import { updateProfileFactory } from "./apis/updateProfile.js"; +import { updateProfileBioFactory } from "./apis/updateProfileBio.js"; +import { updateQuickMessageFactory } from "./apis/updateQuickMessage.js"; +import { updateSettingsFactory } from "./apis/updateSettings.js"; +import { upgradeGroupToCommunityFactory } from "./apis/upgradeGroupToCommunity.js"; +import { uploadAttachmentFactory } from "./apis/uploadAttachment.js"; +import { uploadProductPhotoFactory } from "./apis/uploadProductPhoto.js"; +import { votePollFactory } from "./apis/votePoll.js"; +import { customFactory } from "./apis/custom.js"; +import type { ZPWServiceMap, ContextSession } from "./context.js"; + +export class API { + public zpwServiceMap: ZPWServiceMap; + public listener: Listener; + + public acceptFriendRequest: ReturnType; + public addGroupBlockedMember: ReturnType; + public addGroupDeputy: ReturnType; + public addPollOptions: ReturnType; + public addQuickMessage: ReturnType; + public addReaction: ReturnType; + public addUnreadMark: ReturnType; + public addUserToGroup: ReturnType; + public blockUser: ReturnType; + public blockViewFeed: ReturnType; + public changeAccountAvatar: ReturnType; + public changeFriendAlias: ReturnType; + public changeGroupAvatar: ReturnType; + public changeGroupName: ReturnType; + public changeGroupOwner: ReturnType; + public createAutoReply: ReturnType; + public createCatalog: ReturnType; + public createGroup: ReturnType; + public createNote: ReturnType; + public createPoll: ReturnType; + public createProductCatalog: ReturnType; + public createReminder: ReturnType; + public deleteAutoReply: ReturnType; + public deleteAvatar: ReturnType; + public deleteCatalog: ReturnType; + public deleteChat: ReturnType; + public deleteGroupInviteBox: ReturnType; + public deleteMessage: ReturnType; + public deleteProductCatalog: ReturnType; + public disableGroupLink: ReturnType; + public disperseGroup: ReturnType; + public editNote: ReturnType; + public editReminder: ReturnType; + public enableGroupLink: ReturnType; + public fetchAccountInfo: ReturnType; + public findUser: ReturnType; + public findUserByUsername: ReturnType; + public forwardMessage: ReturnType; + public getAliasList: ReturnType; + public getAllFriends: ReturnType; + public getAllGroups: ReturnType; + public getArchivedChatList: ReturnType; + public getAutoDeleteChat: ReturnType; + public getAutoReplyList: ReturnType; + public getAvatarList: ReturnType; + public getAvatarUrlProfile: ReturnType; + public getBizAccount: ReturnType; + public getCatalogList: ReturnType; + public getCloseFriends: ReturnType; + public getContext: ReturnType; + public getCookie: ReturnType; + public getFriendBoardList: ReturnType; + public getFriendOnlines: ReturnType; + public getFriendRecommendations: ReturnType; + public getFriendRequestStatus: ReturnType; + public getFullAvatar: ReturnType; + public getGroupBlockedMember: ReturnType; + public getGroupChatHistory: ReturnType; + public getGroupInfo: ReturnType; + public getGroupInviteBoxInfo: ReturnType; + public getGroupInviteBoxList: ReturnType; + public getGroupLinkDetail: ReturnType; + public getGroupLinkInfo: ReturnType; + public getGroupMembersInfo: ReturnType; + public getHiddenConversations: ReturnType; + public getLabels: ReturnType; + public getListBoard: ReturnType; + public getListReminder: ReturnType; + public getMultiUsersByPhones: ReturnType; + public getMute: ReturnType; + public getOwnId: ReturnType; + public getPendingGroupMembers: ReturnType; + public getPinConversations: ReturnType; + public getPollDetail: ReturnType; + public getProductCatalogList: ReturnType; + public getQR: ReturnType; + public getQuickMessageList: ReturnType; + public getRelatedFriendGroup: ReturnType; + public getReminder: ReturnType; + public getReminderResponses: ReturnType; + public getSentFriendRequest: ReturnType; + public getSettings: ReturnType; + public getStickerCategoryDetail: ReturnType; + public getStickers: ReturnType; + public getStickersDetail: ReturnType; + public getUnreadMark: ReturnType; + public getUserInfo: ReturnType; + public inviteUserToGroups: ReturnType; + public joinGroupInviteBox: ReturnType; + public joinGroupLink: ReturnType; + public keepAlive: ReturnType; + public lastOnline: ReturnType; + public leaveGroup: ReturnType; + public lockPoll: ReturnType; + public parseLink: ReturnType; + public rejectFriendRequest: ReturnType; + public removeFriend: ReturnType; + public removeFriendAlias: ReturnType; + public removeGroupBlockedMember: ReturnType; + public removeGroupDeputy: ReturnType; + public removeQuickMessage: ReturnType; + public removeReminder: ReturnType; + public removeUnreadMark: ReturnType; + public removeUserFromGroup: ReturnType; + public resetHiddenConversPin: ReturnType; + public reuseAvatar: ReturnType; + public reviewPendingMemberRequest: ReturnType; + public searchSticker: ReturnType; + public sendBankCard: ReturnType; + public sendCard: ReturnType; + public sendDeliveredEvent: ReturnType; + public sendFriendRequest: ReturnType; + public sendLink: ReturnType; + public sendMessage: ReturnType; + public sendReport: ReturnType; + public sendSeenEvent: ReturnType; + public sendSticker: ReturnType; + public sendTypingEvent: ReturnType; + public sendVideo: ReturnType; + public sendVoice: ReturnType; + public setHiddenConversations: ReturnType; + public setMute: ReturnType; + public setPinnedConversations: ReturnType; + public sharePoll: ReturnType; + public unblockUser: ReturnType; + public undo: ReturnType; + public undoFriendRequest: ReturnType; + public updateActiveStatus: ReturnType; + public updateArchivedChatList: ReturnType; + public updateAutoDeleteChat: ReturnType; + public updateAutoReply: ReturnType; + public updateCatalog: ReturnType; + public updateGroupSettings: ReturnType; + public updateHiddenConversPin: ReturnType; + public updateLabels: ReturnType; + public updateLang: ReturnType; + public updateProductCatalog: ReturnType; + public updateProfile: ReturnType; + public updateProfileBio: ReturnType; + public updateQuickMessage: ReturnType; + public updateSettings: ReturnType; + public upgradeGroupToCommunity: ReturnType; + public uploadAttachment: ReturnType; + public uploadProductPhoto: ReturnType; + public votePoll: ReturnType; + + public custom: ReturnType; + + constructor(ctx: ContextSession, zpwServiceMap: ZPWServiceMap, wsUrls: string[]) { + this.zpwServiceMap = zpwServiceMap; + this.listener = new Listener(ctx, wsUrls); + + this.acceptFriendRequest = acceptFriendRequestFactory(ctx, this); + this.addGroupBlockedMember = addGroupBlockedMemberFactory(ctx, this); + this.addGroupDeputy = addGroupDeputyFactory(ctx, this); + this.addPollOptions = addPollOptionsFactory(ctx, this); + this.addQuickMessage = addQuickMessageFactory(ctx, this); + this.addReaction = addReactionFactory(ctx, this); + this.addUnreadMark = addUnreadMarkFactory(ctx, this); + this.addUserToGroup = addUserToGroupFactory(ctx, this); + this.blockUser = blockUserFactory(ctx, this); + this.blockViewFeed = blockViewFeedFactory(ctx, this); + this.changeAccountAvatar = changeAccountAvatarFactory(ctx, this); + this.changeFriendAlias = changeFriendAliasFactory(ctx, this); + this.changeGroupAvatar = changeGroupAvatarFactory(ctx, this); + this.changeGroupName = changeGroupNameFactory(ctx, this); + this.changeGroupOwner = changeGroupOwnerFactory(ctx, this); + this.createAutoReply = createAutoReplyFactory(ctx, this); + this.createCatalog = createCatalogFactory(ctx, this); + this.createGroup = createGroupFactory(ctx, this); + this.createNote = createNoteFactory(ctx, this); + this.createPoll = createPollFactory(ctx, this); + this.createProductCatalog = createProductCatalogFactory(ctx, this); + this.createReminder = createReminderFactory(ctx, this); + this.deleteAutoReply = deleteAutoReplyFactory(ctx, this); + this.deleteAvatar = deleteAvatarFactory(ctx, this); + this.deleteCatalog = deleteCatalogFactory(ctx, this); + this.deleteChat = deleteChatFactory(ctx, this); + this.deleteGroupInviteBox = deleteGroupInviteBoxFactory(ctx, this); + this.deleteMessage = deleteMessageFactory(ctx, this); + this.deleteProductCatalog = deleteProductCatalogFactory(ctx, this); + this.disableGroupLink = disableGroupLinkFactory(ctx, this); + this.disperseGroup = disperseGroupFactory(ctx, this); + this.editNote = editNoteFactory(ctx, this); + this.editReminder = editReminderFactory(ctx, this); + this.enableGroupLink = enableGroupLinkFactory(ctx, this); + this.fetchAccountInfo = fetchAccountInfoFactory(ctx, this); + this.findUser = findUserFactory(ctx, this); + this.findUserByUsername = findUserByUsernameFactory(ctx, this); + this.forwardMessage = forwardMessageFactory(ctx, this); + this.getAliasList = getAliasListFactory(ctx, this); + this.getAllFriends = getAllFriendsFactory(ctx, this); + this.getAllGroups = getAllGroupsFactory(ctx, this); + this.getArchivedChatList = getArchivedChatListFactory(ctx, this); + this.getAutoDeleteChat = getAutoDeleteChatFactory(ctx, this); + this.getAutoReplyList = getAutoReplyListFactory(ctx, this); + this.getAvatarList = getAvatarListFactory(ctx, this); + this.getAvatarUrlProfile = getAvatarUrlProfileFactory(ctx, this); + this.getBizAccount = getBizAccountFactory(ctx, this); + this.getCatalogList = getCatalogListFactory(ctx, this); + this.getCloseFriends = getCloseFriendsFactory(ctx, this); + this.getContext = getContextFactory(ctx, this); + this.getCookie = getCookieFactory(ctx, this); + this.getFriendBoardList = getFriendBoardListFactory(ctx, this); + this.getFriendOnlines = getFriendOnlinesFactory(ctx, this); + this.getFriendRecommendations = getFriendRecommendationsFactory(ctx, this); + this.getFriendRequestStatus = getFriendRequestStatusFactory(ctx, this); + this.getFullAvatar = getFullAvatarFactory(ctx, this); + this.getGroupBlockedMember = getGroupBlockedMemberFactory(ctx, this); + this.getGroupChatHistory = getGroupChatHistoryFactory(ctx, this); + this.getGroupInfo = getGroupInfoFactory(ctx, this); + this.getGroupInviteBoxInfo = getGroupInviteBoxInfoFactory(ctx, this); + this.getGroupInviteBoxList = getGroupInviteBoxListFactory(ctx, this); + this.getGroupLinkDetail = getGroupLinkDetailFactory(ctx, this); + this.getGroupLinkInfo = getGroupLinkInfoFactory(ctx, this); + this.getGroupMembersInfo = getGroupMembersInfoFactory(ctx, this); + this.getHiddenConversations = getHiddenConversationsFactory(ctx, this); + this.getLabels = getLabelsFactory(ctx, this); + this.getListBoard = getListBoardFactory(ctx, this); + this.getListReminder = getListReminderFactory(ctx, this); + this.getMultiUsersByPhones = getMultiUsersByPhonesFactory(ctx, this); + this.getMute = getMuteFactory(ctx, this); + this.getOwnId = getOwnIdFactory(ctx, this); + this.getPendingGroupMembers = getPendingGroupMembersFactory(ctx, this); + this.getPinConversations = getPinConversationsFactory(ctx, this); + this.getPollDetail = getPollDetailFactory(ctx, this); + this.getProductCatalogList = getProductCatalogListFactory(ctx, this); + this.getQR = getQRFactory(ctx, this); + this.getQuickMessageList = getQuickMessageListFactory(ctx, this); + this.getRelatedFriendGroup = getRelatedFriendGroupFactory(ctx, this); + this.getReminder = getReminderFactory(ctx, this); + this.getReminderResponses = getReminderResponsesFactory(ctx, this); + this.getSentFriendRequest = getSentFriendRequestFactory(ctx, this); + this.getSettings = getSettingsFactory(ctx, this); + this.getStickerCategoryDetail = getStickerCategoryDetailFactory(ctx, this); + this.getStickers = getStickersFactory(ctx, this); + this.getStickersDetail = getStickersDetailFactory(ctx, this); + this.getUnreadMark = getUnreadMarkFactory(ctx, this); + this.getUserInfo = getUserInfoFactory(ctx, this); + this.inviteUserToGroups = inviteUserToGroupsFactory(ctx, this); + this.joinGroupInviteBox = joinGroupInviteBoxFactory(ctx, this); + this.joinGroupLink = joinGroupLinkFactory(ctx, this); + this.keepAlive = keepAliveFactory(ctx, this); + this.lastOnline = lastOnlineFactory(ctx, this); + this.leaveGroup = leaveGroupFactory(ctx, this); + this.lockPoll = lockPollFactory(ctx, this); + this.parseLink = parseLinkFactory(ctx, this); + this.rejectFriendRequest = rejectFriendRequestFactory(ctx, this); + this.removeFriend = removeFriendFactory(ctx, this); + this.removeFriendAlias = removeFriendAliasFactory(ctx, this); + this.removeGroupBlockedMember = removeGroupBlockedMemberFactory(ctx, this); + this.removeGroupDeputy = removeGroupDeputyFactory(ctx, this); + this.removeQuickMessage = removeQuickMessageFactory(ctx, this); + this.removeReminder = removeReminderFactory(ctx, this); + this.removeUnreadMark = removeUnreadMarkFactory(ctx, this); + this.removeUserFromGroup = removeUserFromGroupFactory(ctx, this); + this.resetHiddenConversPin = resetHiddenConversPinFactory(ctx, this); + this.reuseAvatar = reuseAvatarFactory(ctx, this); + this.reviewPendingMemberRequest = reviewPendingMemberRequestFactory(ctx, this); + this.searchSticker = searchStickerFactory(ctx, this); + this.sendBankCard = sendBankCardFactory(ctx, this); + this.sendCard = sendCardFactory(ctx, this); + this.sendDeliveredEvent = sendDeliveredEventFactory(ctx, this); + this.sendFriendRequest = sendFriendRequestFactory(ctx, this); + this.sendLink = sendLinkFactory(ctx, this); + this.sendMessage = sendMessageFactory(ctx, this); + this.sendReport = sendReportFactory(ctx, this); + this.sendSeenEvent = sendSeenEventFactory(ctx, this); + this.sendSticker = sendStickerFactory(ctx, this); + this.sendTypingEvent = sendTypingEventFactory(ctx, this); + this.sendVideo = sendVideoFactory(ctx, this); + this.sendVoice = sendVoiceFactory(ctx, this); + this.setHiddenConversations = setHiddenConversationsFactory(ctx, this); + this.setMute = setMuteFactory(ctx, this); + this.setPinnedConversations = setPinnedConversationsFactory(ctx, this); + this.sharePoll = sharePollFactory(ctx, this); + this.unblockUser = unblockUserFactory(ctx, this); + this.undo = undoFactory(ctx, this); + this.undoFriendRequest = undoFriendRequestFactory(ctx, this); + this.updateActiveStatus = updateActiveStatusFactory(ctx, this); + this.updateArchivedChatList = updateArchivedChatListFactory(ctx, this); + this.updateAutoDeleteChat = updateAutoDeleteChatFactory(ctx, this); + this.updateAutoReply = updateAutoReplyFactory(ctx, this); + this.updateCatalog = updateCatalogFactory(ctx, this); + this.updateGroupSettings = updateGroupSettingsFactory(ctx, this); + this.updateHiddenConversPin = updateHiddenConversPinFactory(ctx, this); + this.updateLabels = updateLabelsFactory(ctx, this); + this.updateLang = updateLangFactory(ctx, this); + this.updateProductCatalog = updateProductCatalogFactory(ctx, this); + this.updateProfile = updateProfileFactory(ctx, this); + this.updateProfileBio = updateProfileBioFactory(ctx, this); + this.updateQuickMessage = updateQuickMessageFactory(ctx, this); + this.updateSettings = updateSettingsFactory(ctx, this); + this.upgradeGroupToCommunity = upgradeGroupToCommunityFactory(ctx, this); + this.uploadAttachment = uploadAttachmentFactory(ctx, this); + this.uploadProductPhoto = uploadProductPhotoFactory(ctx, this); + this.votePoll = votePollFactory(ctx, this); + + this.custom = customFactory(ctx, this); + } +} diff --git a/_libs/zca-js/src/apis/acceptFriendRequest.ts b/_libs/zca-js/src/apis/acceptFriendRequest.ts new file mode 100644 index 0000000..0d1f9a9 --- /dev/null +++ b/_libs/zca-js/src/apis/acceptFriendRequest.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AcceptFriendRequestResponse = ""; + +export const acceptFriendRequestFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/accept`); + + /** + * Accept a friend request from a User + * + * @param friendId The friend ID to user request is accept + * + * @throws {ZaloApiError} + */ + return async function acceptFriendRequest(friendId: string) { + const params = { + fid: friendId, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addGroupBlockedMember.ts b/_libs/zca-js/src/apis/addGroupBlockedMember.ts new file mode 100644 index 0000000..6e514fd --- /dev/null +++ b/_libs/zca-js/src/apis/addGroupBlockedMember.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AddGroupBlockedMemberResponse = ""; + +export const addGroupBlockedMemberFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/blockedmems/add`); + + /** + * Add group blocked member + * + * @param memberId member id(s) + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function addGroupBlockedMember(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addGroupDeputy.ts b/_libs/zca-js/src/apis/addGroupDeputy.ts new file mode 100644 index 0000000..fd73a81 --- /dev/null +++ b/_libs/zca-js/src/apis/addGroupDeputy.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AddGroupDeputyResponse = ""; + +export const addGroupDeputyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/admins/add`); + + /** + * Add group deputy + * + * @param memberId user Id or list of user Ids + * @param groupId group Id + * + * @throws {ZaloApiError} + * + */ + return async function addGroupDeputy(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addPollOptions.ts b/_libs/zca-js/src/apis/addPollOptions.ts new file mode 100644 index 0000000..8a8be15 --- /dev/null +++ b/_libs/zca-js/src/apis/addPollOptions.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { PollOptions } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddPollOptionsOption = { + voted: boolean; + content: string; +}; + +export type AddPollOptionsPayload = { + pollId: number; + options: AddPollOptionsOption[]; + votedOptionIds: number[]; +}; + +export type AddPollOptionsResponse = { + options: PollOptions[]; +}; + +export const addPollOptionsFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/option/add`); + + /** + * Add new option to poll + * + * @param payload + * + * @throws {ZaloApiError} + */ + return async function addPollOptions(payload: AddPollOptionsPayload) { + const params = { + poll_id: payload.pollId, + new_options: JSON.stringify(payload.options), + voted_option_ids: payload.votedOptionIds, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addQuickMessage.ts b/_libs/zca-js/src/apis/addQuickMessage.ts new file mode 100644 index 0000000..df6c5c6 --- /dev/null +++ b/_libs/zca-js/src/apis/addQuickMessage.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { QuickMessage, AttachmentSource } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddQuickMessagePayload = { + keyword: string; + title: string; + media?: AttachmentSource; +}; + +export type AddQuickMessageResponse = { + item: QuickMessage; + version: number; +}; + +export const addQuickMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/create`); + + /** + * Add quick message + * + * @param addPayload - The payload containing data to add the quick message + * + * @note Zalo might throw an error with code 821 if you have reached the limit of quick messages. + * + * @throws {ZaloApiError} + */ + return async function addQuickMessage(addPayload: AddQuickMessagePayload) { + const isType = !addPayload.media ? 0 : 1; + + const params: Record = { + keyword: addPayload.keyword, + message: { + title: addPayload.title, + params: "", + }, + type: isType, + imei: ctx.imei, + }; + + if (isType === 1) { + if (!addPayload.media) throw new ZaloApiError("Media is required"); + const uploadMedia = await api.uploadProductPhoto({ + file: addPayload.media, + }); + + const photoId = uploadMedia.photoId; + const thumbUrl = uploadMedia.thumbUrl; + const normalUrl = uploadMedia.normalUrl; + const hdUrl = uploadMedia.hdUrl; + + params.media = { + items: [ + { + type: 0, + photoId: photoId, + title: "", + width: "", + height: "", + previewThumb: thumbUrl, + rawUrl: normalUrl || hdUrl, + thumbUrl: thumbUrl, + normalUrl: normalUrl || hdUrl, + hdUrl: hdUrl || normalUrl, + }, + ], + }; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/addReaction.ts b/_libs/zca-js/src/apis/addReaction.ts new file mode 100644 index 0000000..64a684f --- /dev/null +++ b/_libs/zca-js/src/apis/addReaction.ts @@ -0,0 +1,321 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType, Reactions } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddReactionResponse = { + msgIds: number[]; +}; + +export type CustomReaction = { + rType: number; + source: number; + icon: string; +}; + +export type AddReactionDestination = { + data: { + msgId: string; + cliMsgId: string; + }; + threadId: string; + type: ThreadType; +}; + +export const addReactionFactory = apiFactory()((api, ctx, utils) => { + const serviceURLs = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.reaction[0]}/api/message/reaction`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.reaction[0]}/api/group/reaction`), + }; + + /** + * Add reaction to a message + * + * @param icon Reaction icon + * @param dest Destination data including message IDs and thread information + * + * @throws {ZaloApiError} + */ + return async function addReaction(icon: Reactions | CustomReaction, dest: AddReactionDestination) { + const serviceURL = serviceURLs[dest.type]; + let rType, source; + + if (typeof icon == "object") { + rType = icon.rType; + source = icon.source; + } else + switch (icon) { + case Reactions.HAHA: + rType = 0; + source = 6; + break; + case Reactions.LIKE: + rType = 3; + source = 6; + break; + case Reactions.HEART: + rType = 5; + source = 6; + break; + case Reactions.WOW: + rType = 32; + source = 6; + break; + case Reactions.CRY: + rType = 2; + source = 6; + break; + case Reactions.ANGRY: + rType = 20; + source = 6; + break; + case Reactions.KISS: + rType = 8; + source = 6; + break; + case Reactions.TEARS_OF_JOY: + rType = 7; + source = 6; + break; + case Reactions.SHIT: + rType = 66; + source = 6; + break; + case Reactions.ROSE: + rType = 120; + source = 6; + break; + case Reactions.BROKEN_HEART: + rType = 65; + source = 6; + break; + case Reactions.DISLIKE: + rType = 4; + source = 6; + break; + case Reactions.LOVE: + rType = 29; + source = 6; + break; + case Reactions.CONFUSED: + rType = 51; + source = 6; + break; + case Reactions.WINK: + rType = 45; + source = 6; + break; + case Reactions.FADE: + rType = 121; + source = 6; + break; + case Reactions.SUN: + rType = 67; + source = 6; + break; + case Reactions.BIRTHDAY: + rType = 126; + source = 6; + break; + case Reactions.BOMB: + rType = 127; + source = 6; + break; + case Reactions.OK: + rType = 68; + source = 6; + break; + case Reactions.PEACE: + rType = 69; + source = 6; + break; + case Reactions.THANKS: + rType = 70; + source = 6; + break; + case Reactions.PUNCH: + rType = 71; + source = 6; + break; + case Reactions.SHARE: + rType = 72; + source = 6; + break; + case Reactions.PRAY: + rType = 73; + source = 6; + break; + case Reactions.NO: + rType = 131; + source = 6; + break; + case Reactions.BAD: + rType = 132; + source = 6; + break; + case Reactions.LOVE_YOU: + rType = 133; + source = 6; + break; + case Reactions.SAD: + rType = 1; + source = 6; + break; + case Reactions.VERY_SAD: + rType = 16; + source = 6; + break; + case Reactions.COOL: + rType = 21; + source = 6; + break; + case Reactions.NERD: + rType = 22; + source = 6; + break; + case Reactions.BIG_SMILE: + rType = 23; + source = 6; + break; + case Reactions.SUNGLASSES: + rType = 26; + source = 6; + break; + case Reactions.NEUTRAL: + rType = 30; + source = 6; + break; + case Reactions.SAD_FACE: + rType = 35; + source = 6; + break; + case Reactions.BYE: + rType = 36; + source = 6; + break; + case Reactions.SLEEPY: + rType = 38; + source = 6; + break; + case Reactions.WIPE: + rType = 39; + source = 6; + break; + case Reactions.DIG: + rType = 42; + source = 6; + break; + case Reactions.ANGUISH: + rType = 44; + source = 6; + break; + case Reactions.HANDCLAP: + rType = 46; + source = 6; + break; + case Reactions.ANGRY_FACE: + rType = 47; + source = 6; + break; + case Reactions.F_CHAIR: + rType = 48; + source = 6; + break; + case Reactions.L_CHAIR: + rType = 49; + source = 6; + break; + case Reactions.R_CHAIR: + rType = 50; + source = 6; + break; + case Reactions.SILENT: + rType = 52; + source = 6; + break; + case Reactions.SURPRISE: + rType = 53; + source = 6; + break; + case Reactions.EMBARRASSED: + rType = 54; + source = 6; + break; + case Reactions.AFRAID: + rType = 60; + source = 6; + break; + case Reactions.SAD2: + rType = 61; + source = 6; + break; + case Reactions.BIG_LAUGH: + rType = 62; + source = 6; + break; + case Reactions.RICH: + rType = 63; + source = 6; + break; + case Reactions.BEER: + rType = 99; + source = 6; + break; + default: + rType = -1; + source = 6; + } + + const rIcon = typeof icon == "object" ? icon.icon : icon; + + if (rType == undefined || source == undefined || rIcon == undefined) { + throw new ZaloApiError("Invalid reaction"); + } + + const params: Record = { + react_list: [ + { + message: JSON.stringify({ + rMsg: [ + { + gMsgID: parseInt(dest.data.msgId), + cMsgID: parseInt(dest.data.cliMsgId), + msgType: 1, + }, + ], + rIcon, + rType, + source, + }), + clientId: Date.now(), + }, + ], + }; + + if (dest.type == ThreadType.User) { + params.toid = dest.threadId; + } else { + params.grid = dest.threadId; + params.imei = ctx.imei; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + if (typeof (result.data as { msgIds: unknown }).msgIds === "string") { + return { + msgIds: JSON.parse((result.data as { msgIds: string }).msgIds), + }; + } + + return result.data as AddReactionResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/addUnreadMark.ts b/_libs/zca-js/src/apis/addUnreadMark.ts new file mode 100644 index 0000000..583bc74 --- /dev/null +++ b/_libs/zca-js/src/apis/addUnreadMark.ts @@ -0,0 +1,65 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type AddUnreadMarkResponse = { + data: { + updateId: number; + }; + status: number; +}; + +export const addUnreadMarkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/addUnreadMark`); + + /** + * Add unread mark to conversation + * + * @param threadId Thread ID + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function addUnreadMark(threadId: string, type: ThreadType = ThreadType.User) { + const timestamp = Date.now(); + const timestampString = timestamp.toString(); + const isGroup = type === ThreadType.Group; + + const requestParams = { + param: JSON.stringify({ + [isGroup ? "convsGroup" : "convsUser"]: [ + { + id: threadId, + cliMsgId: timestampString, + fromUid: "0", + ts: timestamp, + }, + ], + [isGroup ? "convsUser" : "convsGroup"]: [], + imei: ctx.imei, + }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + const data = result.data as { data: unknown; status: number }; + if (typeof data.data === "string") { + return { + data: JSON.parse(data.data), + status: data.status, + }; + } + + return result.data as AddUnreadMarkResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/addUserToGroup.ts b/_libs/zca-js/src/apis/addUserToGroup.ts new file mode 100644 index 0000000..f50c85d --- /dev/null +++ b/_libs/zca-js/src/apis/addUserToGroup.ts @@ -0,0 +1,43 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type AddUserToGroupResponse = { + errorMembers: string[]; + error_data: Record; +}; + +export const addUserToGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/invite/v2`); + + /** + * Add user to existing group + * + * @param memberId User ID or list of user IDs to add + * @param groupId Group ID + * + * @throws {ZaloApiError} + */ + return async function addUserToGroup(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + memberTypes: memberId.map(() => -1), + imei: ctx.imei, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/blockUser.ts b/_libs/zca-js/src/apis/blockUser.ts new file mode 100644 index 0000000..166c3ad --- /dev/null +++ b/_libs/zca-js/src/apis/blockUser.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type BlockUserResponse = ""; + +export const blockUserFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/block`); + + /** + * Block a User + * + * @param userId The ID of the User to block + * + * @throws {ZaloApiError} + */ + return async function blockUser(userId: string) { + const params = { + fid: userId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/blockViewFeed.ts b/_libs/zca-js/src/apis/blockViewFeed.ts new file mode 100644 index 0000000..e5a0223 --- /dev/null +++ b/_libs/zca-js/src/apis/blockViewFeed.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type BlockViewFeedResponse = ""; + +export const blockViewFeedFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/feed/block`); + + /** + * Block/Unblock friend view feed by ID + * + * @param isBlockFeed Boolean to block/unblock view feed + * @param userId User ID to block/unblock view feed + * + * @throws {ZaloApiError} + */ + return async function blockViewFeed(isBlockFeed: boolean, userId: string) { + const params = { + fid: userId, + isBlockFeed: isBlockFeed ? 1 : 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeAccountAvatar.ts b/_libs/zca-js/src/apis/changeAccountAvatar.ts new file mode 100644 index 0000000..2be9960 --- /dev/null +++ b/_libs/zca-js/src/apis/changeAccountAvatar.ts @@ -0,0 +1,65 @@ +import FormData from "form-data"; +import fs from "node:fs"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory, formatTime, getImageMetaData } from "../utils.js"; + +export type ChangeAccountAvatarResponse = ""; + +export const changeAccountAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/profile/upavatar`); + + /** + * Change account avatar + * + * @param avatarSource Attachment source, can be a file path or an Attachment object + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function changeAccountAvatar(avatarSource: AttachmentSource) { + const isSourceFilePath = typeof avatarSource == "string"; + const imageMetaData = isSourceFilePath ? await getImageMetaData(ctx, avatarSource) : avatarSource.metadata; + + const fileSize = imageMetaData.totalSize || 0; + + const params = { + avatarSize: 120, + clientId: String(ctx.uid + formatTime("%H:%M %d/%m/%Y")), + language: ctx.language, + metaData: JSON.stringify({ + origin: { + width: imageMetaData.width || 1080, + height: imageMetaData.height || 1080, + }, + processed: { + width: imageMetaData.width || 1080, + height: imageMetaData.height || 1080, + size: fileSize, + }, + }), + }; + + const avatarData = isSourceFilePath ? fs.readFileSync(avatarSource) : avatarSource.data; + const formData = new FormData(); + formData.append("fileContent", avatarData, { + filename: "blob", + contentType: "image/jpeg", + }); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeFriendAlias.ts b/_libs/zca-js/src/apis/changeFriendAlias.ts new file mode 100644 index 0000000..899cf68 --- /dev/null +++ b/_libs/zca-js/src/apis/changeFriendAlias.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ChangeFriendAliasResponse = ""; + +export const changeFriendAliasFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.alias[0]}/api/alias/update`); + + /** + * Change friend's alias + * + * @param alias new alias (nickname - bietdanh) + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function changeFriendAlias(alias: string, friendId: string) { + const params = { + friendId: friendId, + alias: alias, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeGroupAvatar.ts b/_libs/zca-js/src/apis/changeGroupAvatar.ts new file mode 100644 index 0000000..e9301a1 --- /dev/null +++ b/_libs/zca-js/src/apis/changeGroupAvatar.ts @@ -0,0 +1,57 @@ +import FormData from "form-data"; +import fs from "node:fs"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory, getFullTimeFromMillisecond, getImageMetaData } from "../utils.js"; + +export type ChangeGroupAvatarResponse = ""; + +export const changeGroupAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/upavatar`); + + /** + * Change group avatar + * + * @param avatarSource Attachment source, can be a file path or an Attachment object + * @param groupId Group ID + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function changeGroupAvatar(avatarSource: AttachmentSource, groupId: string) { + const params: Record = { + grid: groupId, + avatarSize: 120, + clientId: `g${groupId}${getFullTimeFromMillisecond(new Date().getTime())}`, + imei: ctx.imei, + }; + + const isSourceFilePath = typeof avatarSource == "string"; + const imageMetaData = isSourceFilePath ? await getImageMetaData(ctx, avatarSource) : avatarSource.metadata; + + params.originWidth = imageMetaData.width || 1080; + params.originHeight = imageMetaData.height || 1080; + + const avatarData = isSourceFilePath ? fs.readFileSync(avatarSource) : avatarSource.data; + const formData = new FormData(); + formData.append("fileContent", avatarData, { + filename: "blob", + contentType: "image/jpeg", + }); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeGroupName.ts b/_libs/zca-js/src/apis/changeGroupName.ts new file mode 100644 index 0000000..0719d87 --- /dev/null +++ b/_libs/zca-js/src/apis/changeGroupName.ts @@ -0,0 +1,40 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ChangeGroupNameResponse = { + status: number; +}; + +export const changeGroupNameFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/updateinfo`); + + /** + * Change group name + * + * @param name New group name + * @param groupId Group ID + * + * @throws {ZaloApiError} + */ + return async function changeGroupName(name: string, groupId: string) { + if (name.length == 0) name = Date.now().toString(); + + const params = { + grid: groupId, + gname: name, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/changeGroupOwner.ts b/_libs/zca-js/src/apis/changeGroupOwner.ts new file mode 100644 index 0000000..ba48207 --- /dev/null +++ b/_libs/zca-js/src/apis/changeGroupOwner.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ChangeGroupOwnerResponse = { + time: number; +}; + +export const changeGroupOwnerFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/change-owner`); + + /** + * Change group owner + * + * @param memberId User Id of new group owner + * @param groupId Group Id + * @note Be careful when changing the key, as it will result in losing group admin rights + * + * @throws {ZaloApiError} + * + */ + return async function changeGroupOwner(memberId: string, groupId: string) { + const params = { + grid: groupId, + newAdminId: memberId, + imei: ctx.imei, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createAutoReply.ts b/_libs/zca-js/src/apis/createAutoReply.ts new file mode 100644 index 0000000..2c071ac --- /dev/null +++ b/_libs/zca-js/src/apis/createAutoReply.ts @@ -0,0 +1,58 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AutoReplyItem, AutoReplyScope } from "../models/index.js"; + +export type CreateAutoReplyPayload = { + content: string; + isEnable: boolean; + startTime: number; + endTime: number; + scope: AutoReplyScope; + uids?: string | string[]; +}; + +export type CreateAutoReplyResponse = { + item: AutoReplyItem; + version: number; +}; + +export const createAutoReplyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/create`); + + /** + * Create auto reply + * + * @param payload payload + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function createAutoReply(payload: CreateAutoReplyPayload) { + const uids = Array.isArray(payload.uids) ? payload.uids : [payload.uids]; + const resultUids = (payload.scope === 2 || payload.scope === 3) ? uids : []; + + const params = { + cliLang: ctx.language, + enable: payload.isEnable, + content: payload.content, + startTime: payload.startTime, + endTime: payload.endTime, + recurrence: ["RRULE:FREQ=DAILY;"], + scope: payload.scope, + uids: resultUids, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createCatalog.ts b/_libs/zca-js/src/apis/createCatalog.ts new file mode 100644 index 0000000..a031d7a --- /dev/null +++ b/_libs/zca-js/src/apis/createCatalog.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { CatalogItem } from "../models/index.js"; + +export type CreateCatalogResponse = { + item: CatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const createCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/create`); + + /** + * Create catalog? + * + * @param catalogName catalog name + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function createCatalog(catalogName: string) { + const params = { + catalog_name: catalogName, + catalog_photo: "", + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createGroup.ts b/_libs/zca-js/src/apis/createGroup.ts new file mode 100644 index 0000000..8b56fcc --- /dev/null +++ b/_libs/zca-js/src/apis/createGroup.ts @@ -0,0 +1,77 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type CreateGroupResponse = { + groupType: number; + sucessMembers: string[]; + groupId: string; + errorMembers: string[]; + error_data: Record; +}; + +export type CreateGroupOptions = { + /** + * Group name + */ + name?: string; + /** + * List of member IDs to add to the group + */ + members: string[]; + /** + * Avatar source, can be a file path or an Attachment object + */ + avatarSource?: AttachmentSource; + /** + * Path to the avatar image file + * @deprecated Use `avatarSource` instead + */ + avatarPath?: string; +}; + +export const createGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/create/v2`); + + /** + * Create a new group + * + * @param options Group options + * + * @throws {ZaloApiError} + */ + return async function createGroup(options: CreateGroupOptions) { + if (options.members.length == 0) throw new ZaloApiError("Group must have at least one member"); + + const params = { + clientId: Date.now(), + gname: String(Date.now()), + gdesc: null, + members: options.members, + membersTypes: options.members.map(() => -1), + nameChanged: 0, + createLink: 1, + clientLang: ctx.language, + imei: ctx.imei, + zsource: 601, + }; + + if (options.name && options.name.length > 0) { + params.gname = options.name; + params.nameChanged = 1; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "POST", + }); + + const data = await utils.resolve(response); + options.avatarSource = options.avatarSource || options.avatarPath; + if (options.avatarSource) await api.changeGroupAvatar(options.avatarSource, data.groupId).catch(utils.logger.error); + + return data; + }; +}); diff --git a/_libs/zca-js/src/apis/createNote.ts b/_libs/zca-js/src/apis/createNote.ts new file mode 100644 index 0000000..af8d37f --- /dev/null +++ b/_libs/zca-js/src/apis/createNote.ts @@ -0,0 +1,62 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { NoteDetail } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type CreateNoteOptions = { + title: string; + pinAct?: boolean; +}; + +export type CreateNoteResponse = NoteDetail; + +export const createNoteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/createv2`); + + /** + * Create a note in a group + * + * @param options note options + * @param options.title note title + * @param options.pinAct pin action (pin note) + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function createNote(options: CreateNoteOptions, groupId: string) { + const params = { + grid: groupId, + type: 0, + color: -16777216, + emoji: "", + startTime: -1, + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + repeat: 0, + src: 1, + imei: ctx.imei, + pinAct: options.pinAct ? 1 : 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + if (typeof (result.data as { params: unknown }).params === "string") { + (result.data as CreateNoteResponse).params = JSON.parse( + (result.data as { params: string }).params, + ); + } + + return result.data as CreateNoteResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/createPoll.ts b/_libs/zca-js/src/apis/createPoll.ts new file mode 100644 index 0000000..343e2da --- /dev/null +++ b/_libs/zca-js/src/apis/createPoll.ts @@ -0,0 +1,87 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { PollDetail } from "../models/index.js"; + +/** + * Options for creating a poll. + */ +export type CreatePollOptions = { + /** + * Question for the poll. + */ + question: string; + + /** + * List of options for the poll. + */ + options: string[]; + + /** + * Poll expiration time in milliseconds (0 = no expiration). + */ + expiredTime?: number; + + /** + * Allows multiple choices in the poll. + */ + allowMultiChoices?: boolean; + + /** + * Allows members to add new options to the poll. + */ + allowAddNewOption?: boolean; + + /** + * Hides voting results until the user has voted. + */ + hideVotePreview?: boolean; + + /** + * Hides poll voters (anonymous poll). + */ + isAnonymous?: boolean; +}; + +export type CreatePollResponse = PollDetail; + +export const createPollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/create`); + + /** + * Create a poll in a group. + * + * @param options Poll options + * @param groupId Group ID to create poll from + * + * @throws {ZaloApiError} + */ + return async function createPoll(options: CreatePollOptions, groupId: string) { + const params = { + group_id: groupId, + question: options.question, + options: options.options, + expired_time: options.expiredTime ?? 0, + pinAct: false, + allow_multi_choices: !!options.allowMultiChoices, + allow_add_new_option: !!options.allowAddNewOption, + is_hide_vote_preview: !!options.hideVotePreview, + is_anonymous: !!options.isAnonymous, + poll_type: 0, + src: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createProductCatalog.ts b/_libs/zca-js/src/apis/createProductCatalog.ts new file mode 100644 index 0000000..8588a58 --- /dev/null +++ b/_libs/zca-js/src/apis/createProductCatalog.ts @@ -0,0 +1,86 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AttachmentSource, ProductCatalogItem } from "../models/index.js"; + +export type CreateProductCatalogPayload = { + catalogId: string; + + productName: string; + price: string; + description: string; + + /** + * Upto 5 media files are allowed, will be ignored if product_photos is provided + */ + files?: AttachmentSource[]; + /** + * List of product photo URLs, upto 5 + * + * You can manually get the URL using `uploadProductPhoto` api + */ + product_photos?: string[]; +}; + +export type CreateProductCatalogResponse = { + item: ProductCatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const createProductCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/create`); + + /** + * Create product catalog? + * + * @param payload payload + * + * @note this API is used for zBussiness - Maximum 5 media files are supported + * @throws {ZaloApiError} + */ + return async function createProductCatalog(payload: CreateProductCatalogPayload) { + const productPhoto = payload.product_photos || []; + + if (payload.files && payload.files.length == 0) { + if (payload.files.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + for (const mediaFile of payload.files) { + const uploadMedia = await api.uploadProductPhoto({ + file: mediaFile, + }); + + const url = uploadMedia.normalUrl || uploadMedia.hdUrl; + productPhoto.push(url); + } + } + + if (productPhoto.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + const params = { + product_name: payload.productName, + price: payload.price, + description: payload.description, + product_photos: productPhoto, + catalog_id: payload.catalogId, + currency_unit: "โ‚ซ", // $ + create_time: Date.now(), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/createReminder.ts b/_libs/zca-js/src/apis/createReminder.ts new file mode 100644 index 0000000..9cb535f --- /dev/null +++ b/_libs/zca-js/src/apis/createReminder.ts @@ -0,0 +1,84 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderGroup, ReminderUser} from "../models/index.js"; +import { ReminderRepeatMode, ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type CreateReminderOptions = { + title: string; + emoji?: string; + startTime?: number; + repeat?: ReminderRepeatMode; +}; + +export type CreateReminderUser = ReminderUser; +export type CreateReminderGroup = Omit; + +export type CreateReminderResponse = CreateReminderUser | CreateReminderGroup; + +export const createReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/create`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/createv2`), + }; + + /** + * Create a reminder in a group + * + * @param options reminder options + * @param threadId Group ID to create note from + * @param type Thread type (User or Group) + * + * @throws {ZaloApiError} + */ + return async function createReminder( + options: CreateReminderOptions, + threadId: string, + type: ThreadType = ThreadType.User, + ): Promise { + const params = + type === ThreadType.User + ? { + objectData: JSON.stringify({ + toUid: threadId, + type: 0, + color: -16245706, + emoji: options.emoji ?? "โฐ", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: { title: options.title }, + needPin: false, + repeat: options.repeat ?? ReminderRepeatMode.None, + creatorUid: ctx.uid, // Note: for some reason, you can put any valid UID here instead of your own and it still works, atleast for mobile + src: 1, + }), + imei: ctx.imei, + } + : { + grid: threadId, + type: 0, + color: -16245706, + emoji: options.emoji ?? "โฐ", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + repeat: options.repeat ?? ReminderRepeatMode.None, + src: 1, + imei: ctx.imei, + pinAct: 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/custom.ts b/_libs/zca-js/src/apis/custom.ts new file mode 100644 index 0000000..5ce4382 --- /dev/null +++ b/_libs/zca-js/src/apis/custom.ts @@ -0,0 +1,19 @@ +import { type ContextSession } from "../context.js"; +import { apiFactory, type FactoryUtils } from "../utils.js"; + +export type CustomAPIProps = { ctx: ContextSession; utils: FactoryUtils; props: K }; +export type CustomAPICallback = (props: CustomAPIProps) => T | Promise; + +/* eslint-disable */ +export const customFactory = apiFactory()((api, ctx, utils) => { + return function custom(name: string, callback: CustomAPICallback) { + Object.defineProperty(api, name, { + value: function (props: K) { + return callback({ ctx, utils, props }); + }, + writable: false, + enumerable: false, + configurable: false, + }); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteAutoReply.ts b/_libs/zca-js/src/apis/deleteAutoReply.ts new file mode 100644 index 0000000..e02dac2 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteAutoReply.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteAutoReplyResponse = { + item: number; + version: number; +}; + +export const deleteAutoReplyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/delete`); + + /** + * Delete auto reply + * + * @param id id of auto reply + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function deleteAutoReply(id: number) { + const params = { + cliLang: ctx.language, + id: id, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteAvatar.ts b/_libs/zca-js/src/apis/deleteAvatar.ts new file mode 100644 index 0000000..1e4afd9 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteAvatar.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteAvatarResponse = { + delPhotoIds: string[]; + errMap: { + [key: string]: { + err: number; + }; + }; +}; + +export const deleteAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/del-avatars`); + + /** + * Delete avatar from avatar list + * + * @param photoId avatar photo ID(s) to delete - can be a single string or array of strings + * + * @throws {ZaloApiError} + */ + return async function deleteAvatar(photoId: string | string[]) { + const photoIds = Array.isArray(photoId) ? photoId : [photoId]; + const delPhotos = photoIds.map((id) => ({ photoId: id })); + + const params = { + delPhotos: JSON.stringify(delPhotos), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteCatalog.ts b/_libs/zca-js/src/apis/deleteCatalog.ts new file mode 100644 index 0000000..0f913b4 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteCatalog.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteCatalogResponse = ""; + +export const deleteCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/delete`); + + /** + * Delete catalog? + * + * @param catalogId catalog id + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function deleteCatalog(catalogId: string) { + const params = { + catalog_id: catalogId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteChat.ts b/_libs/zca-js/src/apis/deleteChat.ts new file mode 100644 index 0000000..0daff36 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteChat.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteChatResponse = { + status: number; +}; + +export type DeleteChatLastMessage = { + /** + * Last message owner ID to delete backwards + */ + ownerId: string; + /** + * Last message client ID to delete backwards + */ + cliMsgId: string; + /** + * Last message global ID to delete backwards + */ + globalMsgId: string; +}; + +export const deleteChatFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/deleteconver`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/deleteconver`, { + nretry: 0, + }), + }; + + /** + * Delete chat + * + * @param lastMessage Last message info + * @param threadId Thread ID + * @param type Thread type + * + * @throws {ZaloApiError} + */ + return async function deleteChat( + lastMessage: DeleteChatLastMessage, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + const timestampString = Date.now().toString(); + + const params = + type === ThreadType.User + ? { + toid: threadId, + cliMsgId: timestampString, + conver: lastMessage, + onlyMe: 1, + imei: ctx.imei, + } + : { + grid: threadId, + cliMsgId: timestampString, + conver: lastMessage, + onlyMe: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteGroupInviteBox.ts b/_libs/zca-js/src/apis/deleteGroupInviteBox.ts new file mode 100644 index 0000000..e7bfabc --- /dev/null +++ b/_libs/zca-js/src/apis/deleteGroupInviteBox.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteGroupInviteBoxResponse = { + delInvitaionIds: string[]; + errMap: { + [groupId: string]: { + err: number; + }; + }; +}; + +export const deleteGroupInviteBoxFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/mdel-inv`); + + /** + * Delete group invite box + * + * @param groupId - The group id + * @param blockFutureInvite - Whether to block future invites from this group + * + * @throws {ZaloApiError} + */ + return async function deleteGroupInviteBox(groupId: string | string[], blockFutureInvite: boolean = false) { + const grids = Array.isArray(groupId) ? groupId : [groupId]; + + const params = { + invitations: JSON.stringify(grids.map((grid) => ({ grid }))), + block: blockFutureInvite ? 1 : 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteMessage.ts b/_libs/zca-js/src/apis/deleteMessage.ts new file mode 100644 index 0000000..2226b74 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteMessage.ts @@ -0,0 +1,72 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteMessageResponse = { + status: number; +}; + +export type DeleteMessageDestination = { + data: { + cliMsgId: string; + msgId: string; + uidFrom: string; + }; + threadId: string; + type?: ThreadType; +}; + +export const deleteMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/delete`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/deletemsg`), + }; + /** + * Delete a message + * + * @param dest Delete target + * @param onlyMe Delete message for only you + * + * @throws {ZaloApiError} + */ + return async function deleteMessage(dest: DeleteMessageDestination, onlyMe = false) { + const { threadId, type = ThreadType.User, data } = dest; + const isGroup = type === ThreadType.Group; + const isSelf = ctx.uid == data.uidFrom; + + if (isSelf && onlyMe === false) + throw new ZaloApiError("To delete your message for everyone, use undo api instead"); + + if (!isGroup && onlyMe === false) throw new ZaloApiError("Can't delete message for everyone in a private chat"); + + const params = { + [isGroup ? "grid" : "toid"]: threadId, + cliMsgId: Date.now(), + msgs: [ + { + cliMsgId: data.cliMsgId, + globalMsgId: data.msgId, + ownerId: data.uidFrom, + destId: threadId, + }, + ], + onlyMe: onlyMe ? 1 : 0, + }; + + if (!isGroup) { + params.imei = ctx.imei; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/deleteProductCatalog.ts b/_libs/zca-js/src/apis/deleteProductCatalog.ts new file mode 100644 index 0000000..e7de226 --- /dev/null +++ b/_libs/zca-js/src/apis/deleteProductCatalog.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DeleteProductCatalogPayload = { + productIds: string | string[]; + catalogId: string; +}; + +export type DeleteProductCatalogResponse = { + item: number[]; + version_ls_catalog: number; + version_catalog: number; +}; + +export const deleteProductCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/mdelete`); + + /** + * Delete product catalog? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function deleteProductCatalog(payload: DeleteProductCatalogPayload) { + if (!Array.isArray(payload.productIds)) payload.productIds = [payload.productIds]; + + const params = { + product_ids: payload.productIds, + catalog_id: payload.catalogId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/disableGroupLink.ts b/_libs/zca-js/src/apis/disableGroupLink.ts new file mode 100644 index 0000000..b9f5431 --- /dev/null +++ b/_libs/zca-js/src/apis/disableGroupLink.ts @@ -0,0 +1,30 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DisableGroupLinkResponse = ""; + +export const disableGroupLinkFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/disable`); + + /** + * Disable group link + * + * @param groupId The group id + * + * @throws {ZaloApiError} + */ + return async function disableGroupLink(groupId: string) { + const params = { + grid: groupId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/disperseGroup.ts b/_libs/zca-js/src/apis/disperseGroup.ts new file mode 100644 index 0000000..c46c943 --- /dev/null +++ b/_libs/zca-js/src/apis/disperseGroup.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type DisperseGroupResponse = ""; + +export const disperseGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/disperse`); + + /** + * Disperse Group + * + * @param groupId Group ID to disperse Group from + * + * @throws {ZaloApiError} + */ + return async function disperseGroup(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/editNote.ts b/_libs/zca-js/src/apis/editNote.ts new file mode 100644 index 0000000..6b17a60 --- /dev/null +++ b/_libs/zca-js/src/apis/editNote.ts @@ -0,0 +1,70 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { NoteDetail } from "../models/index.js"; + +export type EditNoteOptions = { + /** + * New note title + */ + title: string; + /** + * Topic ID to edit note from + */ + topicId: string; + /** + * Should the note be pinned? + */ + pinAct?: boolean; +}; + +export type EditNoteResponse = NoteDetail; + +export const editNoteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/updatev2`); + + /** + * Edit an existing note in a group + * + * @param options Options for editing the note + * @param groupId Group ID to create note from + * + * @throws {ZaloApiError} + */ + return async function editNote(options: EditNoteOptions, groupId: string) { + const params = { + grid: groupId, + type: 0, + color: -16777216, + emoji: "", + startTime: -1, + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + topicId: options.topicId, + repeat: 0, + imei: ctx.imei, + pinAct: options.pinAct ? 1 : 2, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + const data = result.data as Omit & { params: unknown }; + if (typeof data.params == "string") { + data.params = JSON.parse(data.params); + } + + return data as NoteDetail; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/editReminder.ts b/_libs/zca-js/src/apis/editReminder.ts new file mode 100644 index 0000000..3a39cff --- /dev/null +++ b/_libs/zca-js/src/apis/editReminder.ts @@ -0,0 +1,83 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderRepeatMode, ReminderGroup, ReminderUser} from "../models/index.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type EditReminderOptions = { + title: string; + topicId: string; + emoji?: string; + startTime?: number; + repeat?: ReminderRepeatMode; +}; + +export type EditReminderUser = ReminderUser; +export type EditReminderGroup = Omit; + +export type EditReminderResponse = EditReminderUser | EditReminderGroup; + +export const editReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/update`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/updatev2`), + }; + + /** + * Edit an existing reminder + * + * @param options Reminder parameters + * @param threadId Thread ID + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function editReminder( + options: EditReminderOptions, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + const requestParams = + type === ThreadType.User + ? { + objectData: JSON.stringify({ + toUid: threadId, + type: 0, + color: -16777216, + emoji: options.emoji ?? "", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: { title: options.title }, + needPin: false, + reminderId: options.topicId, + repeat: options.repeat ?? 0, + }), + } + : { + grid: threadId, + type: 0, + color: -16777216, + emoji: options.emoji ?? "", + startTime: options.startTime ?? Date.now(), + duration: -1, + params: JSON.stringify({ + title: options.title, + }), + topicId: options.topicId, + repeat: options.repeat ?? 0, + imei: ctx.imei, + pinAct: 2, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/enableGroupLink.ts b/_libs/zca-js/src/apis/enableGroupLink.ts new file mode 100644 index 0000000..6c40100 --- /dev/null +++ b/_libs/zca-js/src/apis/enableGroupLink.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type EnableGroupLinkResponse = { + link: string; + expiration_date: number; + enabled: number; +}; + +export const enableGroupLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/new`); + + /** + * Enable and create new group link + * + * @param groupId The group id + * + * @throws {ZaloApiError} + */ + return async function enableGroupLink(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/fetchAccountInfo.ts b/_libs/zca-js/src/apis/fetchAccountInfo.ts new file mode 100644 index 0000000..307d41d --- /dev/null +++ b/_libs/zca-js/src/apis/fetchAccountInfo.ts @@ -0,0 +1,17 @@ +import { apiFactory } from "../utils.js"; + +import type { User } from "../models/index.js"; + +export type FetchAccountInfoResponse = { profile: User }; + +export const fetchAccountInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/me-v2`); + + return async function fetchAccountInfo() { + const response = await utils.request(serviceURL, { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/findUser.ts b/_libs/zca-js/src/apis/findUser.ts new file mode 100644 index 0000000..cfa3ccf --- /dev/null +++ b/_libs/zca-js/src/apis/findUser.ts @@ -0,0 +1,52 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type UserBasic } from "../models/index.js"; + +export type FindUserResponse = UserBasic; + +export const findUserFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/profile/get`); + + /** + * Find user by phone number + * + * @param phoneNumber Phone number + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function findUser(phoneNumber: string, avatarSize: AvatarSize = AvatarSize.Large) { + if (!phoneNumber) throw new ZaloApiError("Missing phoneNumber"); + if (phoneNumber.startsWith("0")) { + if (ctx.language == "vi") phoneNumber = "84" + phoneNumber.slice(1); + } + + const params = { + phone: phoneNumber, + avatar_size: avatarSize, + language: ctx.language, + imei: ctx.imei, + reqSrc: 40, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const finalServiceUrl = new URL(serviceURL); + finalServiceUrl.searchParams.append("params", encryptedParams); + + const response = await utils.request( + utils.makeURL(finalServiceUrl.toString(), { + params: encryptedParams, + }), + ); + + return utils.resolve(response, (result) => { + if (result.error && result.error.code != 216) + throw new ZaloApiError(result.error.message, result.error.code); + + return result.data as FindUserResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/findUserByUsername.ts b/_libs/zca-js/src/apis/findUserByUsername.ts new file mode 100644 index 0000000..964a67c --- /dev/null +++ b/_libs/zca-js/src/apis/findUserByUsername.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type UserBasic } from "../models/index.js"; + +export type FindUserByUsernameResponse = UserBasic; + +export const findUserByUsernameFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/search/by-user-name`); + + /** + * Find user by username + * + * @param username username for find + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function findUserByUsername(username: string, avatarSize: AvatarSize = AvatarSize.Large) { + const params = { + user_name: username, + avatar_size: avatarSize, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/forwardMessage.ts b/_libs/zca-js/src/apis/forwardMessage.ts new file mode 100644 index 0000000..7259388 --- /dev/null +++ b/_libs/zca-js/src/apis/forwardMessage.ts @@ -0,0 +1,127 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type ForwardMessagePayload = { + message: string; + ttl?: number; + reference?: { + id: string; + ts: number; + logSrcType: number; + fwLvl: number; + }; +}; + +export type ForwardMessageSuccess = { + clientId: string; + msgId: string; +}; + +export type ForwardMessageFail = { + clientId: string; + error_code: string; +}; + +export type ForwardMessageResponse = { + success: ForwardMessageSuccess[]; + fail: ForwardMessageFail[]; +}; + +export const forwardMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/mforward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/mforward`), + }; + + /** + * Forward message to multiple threads + * + * @param payload Forward message payload + * @param threadId Thread ID(s) + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function forwardMessage( + payload: ForwardMessagePayload, + threadIds: string[], + type: ThreadType = ThreadType.User, + ) { + if (!payload.message) throw new ZaloApiError("Missing message content"); + if (!threadIds || threadIds.length === 0) throw new ZaloApiError("Missing thread IDs"); + + const timestamp = Date.now(); + const clientId = timestamp.toString(); + + const msgInfo = { + message: payload.message, + reference: payload.reference + ? JSON.stringify({ + type: 3, + data: JSON.stringify(payload.reference), + }) + : undefined, + }; + + const decorLog = payload.reference + ? { + fw: { + pmsg: { + st: 1, + ts: payload.reference.ts, + id: payload.reference.id, + }, + rmsg: { + st: 1, + ts: payload.reference.ts, + id: payload.reference.id, + }, + fwLvl: payload.reference.fwLvl, + }, + } + : null; + + let params; + if (type === ThreadType.User) { + params = { + toIds: threadIds.map((threadId) => ({ + clientId, + toUid: threadId, + ttl: payload.ttl ?? 0, + })), + imei: ctx.imei, + ttl: payload.ttl ?? 0, + msgType: "1", + totalIds: threadIds.length, + msgInfo: JSON.stringify(msgInfo), + decorLog: JSON.stringify(decorLog), + }; + } else { + params = { + grids: threadIds.map((threadId) => ({ + clientId, + grid: threadId, + ttl: payload.ttl ?? 0, + })), + ttl: payload.ttl ?? 0, + msgType: "1", + totalIds: threadIds.length, + msgInfo: JSON.stringify(msgInfo), + decorLog: JSON.stringify(decorLog), + }; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAliasList.ts b/_libs/zca-js/src/apis/getAliasList.ts new file mode 100644 index 0000000..8e24d73 --- /dev/null +++ b/_libs/zca-js/src/apis/getAliasList.ts @@ -0,0 +1,39 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetAliasListResponse = { + items: { + userId: string; + alias: string; + }[]; + updateTime: string; +}; + +export const getAliasListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.alias[0]}/api/alias/list`); + + /** + * Get alias list + * + * @param count Page size (default: 100) + * @param page Page number (default: 1) + * + * @throws {ZaloApiError} + */ + return async function getAliasList(count: number = 100, page: number = 1) { + const params = { + page, + count, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAllFriends.ts b/_libs/zca-js/src/apis/getAllFriends.ts new file mode 100644 index 0000000..9fbe138 --- /dev/null +++ b/_libs/zca-js/src/apis/getAllFriends.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type User } from "../models/index.js"; + +export type GetAllFriendsResponse = User[]; + +export const getAllFriendsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getfriends`); + + /** + * Get all friends + * + * @param count Page size (default: 20000) + * @param page Page number (default: 1) + * @param avatarSize Avatar size (default: AvatarSize.Small) + * + * @throws {ZaloApiError} + */ + return async function getAllFriends(count: number = 20000, page: number = 1, avatarSize: AvatarSize = AvatarSize.Small) { + const params = { + incInvalid: 1, + page, + count, + avatar_size: avatarSize, + actiontime: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "GET", + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAllGroups.ts b/_libs/zca-js/src/apis/getAllGroups.ts new file mode 100644 index 0000000..6899ec0 --- /dev/null +++ b/_libs/zca-js/src/apis/getAllGroups.ts @@ -0,0 +1,20 @@ +import { apiFactory } from "../utils.js"; + +export type GetAllGroupsResponse = { + version: string; + gridVerMap: { + [groupId: string]: string; + }; +}; + +export const getAllGroupsFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_poll[0]}/api/group/getlg/v4`); + + return async function getAllGroups() { + const response = await utils.request(serviceURL, { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getArchivedChatList.ts b/_libs/zca-js/src/apis/getArchivedChatList.ts new file mode 100644 index 0000000..fac826a --- /dev/null +++ b/_libs/zca-js/src/apis/getArchivedChatList.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetArchivedChatListResponse = { + items: unknown[]; // @TODO: check type + version: number; +}; + +export const getArchivedChatListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/archivedchat/list`); + + /** + * Get arcnived chat list + * + * @throws {ZaloApiError} + */ + return async function getArchivedChatList() { + const params = { + version: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAutoDeleteChat.ts b/_libs/zca-js/src/apis/getAutoDeleteChat.ts new file mode 100644 index 0000000..eeff5fa --- /dev/null +++ b/_libs/zca-js/src/apis/getAutoDeleteChat.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetAutoDeleteChatResponse = { + convers: { + destId: string; + isGroup: boolean; + ttl: number; + createdAt: number; + }[]; +}; + +export const getAutoDeleteChatFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/autodelete/getConvers`); + + /** + * Get auto delete chat + * + * @throws {ZaloApiError} + * + */ + return async function getAutoDeleteChat() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAutoReplyList.ts b/_libs/zca-js/src/apis/getAutoReplyList.ts new file mode 100644 index 0000000..3ad3916 --- /dev/null +++ b/_libs/zca-js/src/apis/getAutoReplyList.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AutoReplyItem } from "../models/index.js"; + +export type GetAutoReplyListResponse = { + item: AutoReplyItem[]; + version: number; +}; +export const getAutoReplyListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/list`); + + /** + * Get auto reply list + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function getAutoReplyList() { + const params = { + version: 0, + cliLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAvatarList.ts b/_libs/zca-js/src/apis/getAvatarList.ts new file mode 100644 index 0000000..efe331b --- /dev/null +++ b/_libs/zca-js/src/apis/getAvatarList.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetAvatarListResponse = { + albumId: string; + nextPhotoId: string; + hasMore: number; + photos: { + photoId: string; + thumbnail: string; + url: string; + bkUrl: string; + }[]; +}; + +export const getAvatarListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/avatar-list`); + + /** + * Get avatar list + * + * @param count The number of avatars to fetch (default: 50) + * @param page The page number to fetch (default: 1) + * + * @throws {ZaloApiError} + */ + return async function getAvatarList(count: number = 50, page: number = 1) { + const params = { + page, + albumId: "0", + count, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getAvatarUrlProfile.ts b/_libs/zca-js/src/apis/getAvatarUrlProfile.ts new file mode 100644 index 0000000..8e5689a --- /dev/null +++ b/_libs/zca-js/src/apis/getAvatarUrlProfile.ts @@ -0,0 +1,40 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { AvatarSize } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetAvatarUrlProfileResponse = { + [userId: string]: { + avatar: string; + }; +}; + +export const getAvatarUrlProfileFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/avatar-url`); + + /** + * Get avatar url profile + * + * @param friendId friend id(s) + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function getAvatarUrlProfile(friendIds: string | string[], avatarSize: AvatarSize = AvatarSize.Large) { + if (!Array.isArray(friendIds)) friendIds = [friendIds]; + + const params = { + friend_ids: friendIds, + avatar_size: avatarSize, + srcReq: -1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getBizAccount.ts b/_libs/zca-js/src/apis/getBizAccount.ts new file mode 100644 index 0000000..c625723 --- /dev/null +++ b/_libs/zca-js/src/apis/getBizAccount.ts @@ -0,0 +1,55 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { BusinessCategory } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetBizAccountResponse = { + biz?: { + desc: string | null; + cate: BusinessCategory; + addr: string; + website: string; + email: string; + }; + setting_start_page?: { + enable_biz_label: number; + enable_cate: number; + enable_add: number; + cta_profile: number; + /** + * Relative path used to build the catalog URL. + * + * Example: https://catalog.zalo.me/${cta_catalog} + */ + cta_catalog: string | null; + } | null; + pkgId: number; +}; + +export const getBizAccountFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/get-bizacc`); + + /** + * Get biz account + * + * @param friendId The friend ID to get biz account + * + * @throws {ZaloApiError} + */ + return async function getBizAccount(friendId: string) { + const params = { + fid: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getCatalogList.ts b/_libs/zca-js/src/apis/getCatalogList.ts new file mode 100644 index 0000000..7757199 --- /dev/null +++ b/_libs/zca-js/src/apis/getCatalogList.ts @@ -0,0 +1,55 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { CatalogItem } from "../models/index.js"; + +export type GetCatalogListPayload = { + /** + * Number of items to retrieve (default: 20) + */ + limit?: number; + lastProductId?: number; + /** + * Page number (default: 0) + */ + page?: number; +}; + +export type GetCatalogListResponse = { + items: CatalogItem[]; + version: number; + has_more: number; +}; + +export const getCatalogListFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/list`); + + /** + * Get catalog list? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function getCatalogList(payload?: GetCatalogListPayload) { + const params = { + version_list_catalog: 0, + limit: payload?.limit ?? 20, + last_product_id: payload?.lastProductId ?? -1, + page: payload?.page ?? 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getCloseFriends.ts b/_libs/zca-js/src/apis/getCloseFriends.ts new file mode 100644 index 0000000..658964d --- /dev/null +++ b/_libs/zca-js/src/apis/getCloseFriends.ts @@ -0,0 +1,27 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { User } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetCloseFriendsResponse = User[]; + +export const getCloseFriendsFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getclosedfriends`); + + /** + * Get close friends + * + * @throws {ZaloApiError} + */ + return async function getCloseFriends() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getContext.ts b/_libs/zca-js/src/apis/getContext.ts new file mode 100644 index 0000000..e224ad7 --- /dev/null +++ b/_libs/zca-js/src/apis/getContext.ts @@ -0,0 +1,5 @@ +import { apiFactory } from "../utils.js"; + +export const getContextFactory = apiFactory()((_, ctx) => { + return () => ctx; +}); diff --git a/_libs/zca-js/src/apis/getCookie.ts b/_libs/zca-js/src/apis/getCookie.ts new file mode 100644 index 0000000..913cec6 --- /dev/null +++ b/_libs/zca-js/src/apis/getCookie.ts @@ -0,0 +1,10 @@ +import { apiFactory } from "../utils.js"; + +export const getCookieFactory = apiFactory()((_, ctx) => { + /** + * Get the current cookie string + */ + return function getCookie() { + return ctx.cookie; + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendBoardList.ts b/_libs/zca-js/src/apis/getFriendBoardList.ts new file mode 100644 index 0000000..7cd20de --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendBoardList.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFriendBoardListResponse = { + data: string[]; + version: number; +}; + +export const getFriendBoardListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend_board[0]}/api/friendboard/list`); + + /** + * Get friend board list + * + * @param conversationId conversation id + * + * @throws {ZaloApiError} + */ + return async function getFriendBoardList(conversationId: string) { + const params = { + conversationId: conversationId, + version: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendOnlines.ts b/_libs/zca-js/src/apis/getFriendOnlines.ts new file mode 100644 index 0000000..2707d43 --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendOnlines.ts @@ -0,0 +1,53 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFriendOnlinesStatus = { + userId: string; + status: string; +}; + + +export type GetFriendOnlinesResponse = { + predefine: string[]; + ownerStatus: string; + onlines: GetFriendOnlinesStatus[]; +}; + +export const getFriendOnlinesFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/onlines`); + + /** + * Get friend onlines? + * + * @throws {ZaloApiError} + */ + return async function getFriendOnlines() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as GetFriendOnlinesResponse; + + if (Array.isArray(data.onlines)) { + for (const online of data.onlines) { + if (typeof online.status === "string") { + const parsed = JSON.parse(online.status); + if (parsed && typeof (parsed as { status?: unknown }).status === "string") { + online.status = (parsed as { status: string }).status; + } + } + } + } + + return data as GetFriendOnlinesResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendRecommendations.ts b/_libs/zca-js/src/apis/getFriendRecommendations.ts new file mode 100644 index 0000000..7bf8ab0 --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendRecommendations.ts @@ -0,0 +1,72 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { Gender, ZBusinessPackage } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export enum FriendRecommendationsType { + RecommendedFriend = 1, + ReceivedFriendRequest = 2, +} + +export type FriendRecommendationsCollapseMsgListConfig = { + collapseId: number; + collapseXItem: number; + collapseYItem: number; +}; + +export type FriendRecommendationsDataInfo = { + userId: string; + zaloName: string; + displayName: string; + avatar: string; + phoneNumber: string; + status: string; + gender: Gender; + dob: number; + type: number; + recommType: FriendRecommendationsType; + recommSrc: number; + recommTime: number; + recommInfo: { + suggestWay: number; + source: number; + message: string; + customText: string | null; + }; + bizPkg: ZBusinessPackage; + isSeenFriendReq: boolean; +}; + +export type FriendRecommendationsRecommItem = { + recommItemType: number; + dataInfo: FriendRecommendationsDataInfo; +}; + +export type GetFriendRecommendationsResponse = { + expiredDuration: number; + collapseMsgListConfig: FriendRecommendationsCollapseMsgListConfig; + recommItems: FriendRecommendationsRecommItem[]; +}; + +export const getFriendRecommendationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/recommendsv2/list`); + + /** + * Get friend recommendations/received friend requests + * + * @throws {ZaloApiError} + */ + return async function getFriendRecommendations() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getFriendRequestStatus.ts b/_libs/zca-js/src/apis/getFriendRequestStatus.ts new file mode 100644 index 0000000..db34533 --- /dev/null +++ b/_libs/zca-js/src/apis/getFriendRequestStatus.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFriendRequestStatusResponse = { + addFriendPrivacy: number; + isSeenFriendReq: boolean; + is_friend: number; + is_requested: number; + is_requesting: number; +}; + +export const getFriendRequestStatusFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/reqstatus`); + + /** + * Get friend request status + * + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function getFriendRequestStatus(friendId: string) { + const params = { + fid: friendId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getFullAvatar.ts b/_libs/zca-js/src/apis/getFullAvatar.ts new file mode 100644 index 0000000..013fa54 --- /dev/null +++ b/_libs/zca-js/src/apis/getFullAvatar.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetFullAvatarResponse = { + bk_full_avatar: string; + full_avatar: string; +}; + +export const getFullAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/avatar`); + + /** + * Get full avatar + * + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function getFullAvatar(friendId: string) { + const params = { + fid: friendId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupBlockedMember.ts b/_libs/zca-js/src/apis/getGroupBlockedMember.ts new file mode 100644 index 0000000..b704f96 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupBlockedMember.ts @@ -0,0 +1,56 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupBlockedMemberPayload = { + /** + * Page number (default: 1) + */ + page?: number; + /** + * Number of items to retrieve (default: 50) + */ + count?: number; +}; + +export type GetGroupBlockedMemberResponse = { + blocked_members: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }[]; + has_more: number; +}; + +export const getGroupBlockedMemberFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/blockedmems/list`); + + /** + * Get group blocked member + * + * @param payload payload + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function getGroupBlockedMember(payload: GetGroupBlockedMemberPayload, groupId: string) { + const params = { + grid: groupId, + page: payload.page ?? 1, + count: payload.count ?? 50, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupChatHistory.ts b/_libs/zca-js/src/apis/getGroupChatHistory.ts new file mode 100644 index 0000000..a75698f --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupChatHistory.ts @@ -0,0 +1,51 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { GroupMessage, type TGroupMessage } from "../models/index.js"; + +export type GetGroupChatHistoryResponse = { + lastActionId: string; + lastActionIdOther: string; + more: number; + groupMsgs: GroupMessage[]; +}; + +export const getGroupChatHistoryFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/history`); + + /** + * Get group chat history + * + * @param groupId group id + * @param count count of messages to return (default: 50) + * + * @throws {ZaloApiError} + */ + return async function getGroupChatHistory(groupId: string, count: number = 50) { + const params = { + grid: groupId, + count: count, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + let data = result.data as unknown as GetGroupChatHistoryResponse | string; + + if (typeof data === "string") { + data = JSON.parse(data) as GetGroupChatHistoryResponse; + } + + for (let i = 0; i < data.groupMsgs.length; i++) { + data.groupMsgs[i] = new GroupMessage(ctx.uid, data.groupMsgs[i] as unknown as TGroupMessage); + } + + return data; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupInfo.ts b/_libs/zca-js/src/apis/getGroupInfo.ts new file mode 100644 index 0000000..ad8b0cf --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupInfo.ts @@ -0,0 +1,55 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { GroupInfo } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GroupInfoResponse = { + removedsGroup: string[]; + unchangedsGroup: string[]; + gridInfoMap: { + [groupId: string]: GroupInfo & { + memVerList: string[]; + pendingApprove: GroupInfoPendingApprove; + }; + }; +}; + +export type GroupInfoPendingApprove = { + time: number; + uids: string[] | null; +}; + +export const getGroupInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/getmg-v2`); + + /** + * Get group information + * + * @param groupId Group ID or list of group IDs + * + * @throws {ZaloApiError} + */ + return async function getGroupInfo(groupId: string | string[]) { + if (!Array.isArray(groupId)) groupId = [groupId]; + + const params = { + gridVerMap: JSON.stringify( + groupId.reduce>((acc, id) => { + acc[id] = 0; + return acc; + }, {}), + ), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupInviteBoxInfo.ts b/_libs/zca-js/src/apis/getGroupInviteBoxInfo.ts new file mode 100644 index 0000000..dd203e4 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupInviteBoxInfo.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { GroupInfo, GroupTopic } from "../models/index.js"; + +export type GetGroupInviteBoxInfoPayload = { + groupId: string; + mpage?: number; + mcount?: number; +}; + +export type GetGroupInviteBoxInfoResponse = { + groupInfo: GroupInfo & { + topic?: Omit; + }; + inviterInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + grCreatorInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + expiredTs: string; + type: number; +}; + +export const getGroupInviteBoxInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/inv-info`); + + /** + * Get group invite box info + * + * @param payload - The payload of the request + * + * @throws {ZaloApiError} + */ + return async function getGroupInviteBoxInfo(payload: GetGroupInviteBoxInfoPayload) { + const params = { + grId: payload.groupId, + mcount: payload.mcount ?? 10, + mpage: payload.mpage ?? 1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as GetGroupInviteBoxInfoResponse; + const topic = data.groupInfo.topic as GetGroupInviteBoxInfoResponse["groupInfo"]["topic"] & { + params: unknown; + }; + if (typeof topic.params == "string") { + const params = JSON.parse(topic.params); + if (typeof params.extra == "string") { + params.extra = JSON.parse(params.extra); + } + + topic.params = params; + } + + return data as GetGroupInviteBoxInfoResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupInviteBoxList.ts b/_libs/zca-js/src/apis/getGroupInviteBoxList.ts new file mode 100644 index 0000000..2f21908 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupInviteBoxList.ts @@ -0,0 +1,73 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { GroupInfo } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupInviteBoxListPayload = { + mpage?: number; + page?: number; + invPerPage?: number; + mcount?: number; +}; + +export type GetGroupInviteBoxListResponse = { + invitations: { + groupInfo: GroupInfo; + inviterInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + grCreatorInfo: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }; + /** + * Expired timestamp max 7 days + */ + expiredTs: string; + type: number; + }[]; + total: number; + hasMore: boolean; +}; + +export const getGroupInviteBoxListFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/list`); + + /** + * Get group invite box list + * + * @param payload - The payload of the request + * + * @throws {ZaloApiError} + */ + return async function getGroupInviteBoxList(payload?: GetGroupInviteBoxListPayload) { + const params = { + mpage: payload?.mpage ?? 1, + page: payload?.page ?? 0, + invPerPage: payload?.invPerPage ?? 12, + mcount: payload?.mcount ?? 10, + lastGroupId: null, // @TODO: check type + avatar_size: 120, + member_avatar_size: 120, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupLinkDetail.ts b/_libs/zca-js/src/apis/getGroupLinkDetail.ts new file mode 100644 index 0000000..faccb8e --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupLinkDetail.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupLinkDetailResponse = { + link?: string; + expiration_date?: number; + /** + * 1: enabled, 0: disabled + */ + enabled: number; +}; + +export const getGroupLinkDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/detail`); + + /** + * Get group link detail + * + * @param groupId The group id + * + * @throws {ZaloApiError} + */ + return async function getGroupLinkDetail(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupLinkInfo.ts b/_libs/zca-js/src/apis/getGroupLinkInfo.ts new file mode 100644 index 0000000..367ecee --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupLinkInfo.ts @@ -0,0 +1,66 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { GroupSetting } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetGroupLinkInfoPayload = { + link: string; + /** + * Default: 1 + */ + memberPage?: number; +}; + +export type GetGroupLinkInfoResponse = { + groupId: string; + name: string; + desc: string; + type: number; + creatorId: string; + avt: string; + fullAvt: string; + adminIds: string[]; + currentMems: { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; + }[]; + admins: unknown[]; + hasMoreMember: number; + subType: number; + totalMember: number; + setting: GroupSetting; + globalId: string; +}; + +export const getGroupLinkInfoFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/ginfo`); + + /** + * Get group link info + * + * @param payload - The payload of the request + * + * @throws {ZaloApiError} + */ + return async function getGroupLinkInfo(payload: GetGroupLinkInfoPayload) { + const params = { + link: payload.link, + avatar_size: 120, + member_avatar_size: 120, + mpage: payload.memberPage ?? 1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getGroupMembersInfo.ts b/_libs/zca-js/src/apis/getGroupMembersInfo.ts new file mode 100644 index 0000000..107e132 --- /dev/null +++ b/_libs/zca-js/src/apis/getGroupMembersInfo.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GroupMemberProfile = { + displayName: string; + zaloName: string; + avatar: string; + accountStatus: number; + type: number; + lastUpdateTime: number; + globalId: string; + id: string; +}; + +export type GetGroupMembersInfoResponse = { + profiles: { + [memberId: string]: GroupMemberProfile; + }; + unchangeds_profile: unknown[]; +}; + +export const getGroupMembersInfoFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/group/members`); + + /** + * Get group information + * + * @param memberId member id or array of member ids + * + * @throws {ZaloApiError} + */ + return async function getGroupMembersInfo(memberId: string | string[]) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + friend_pversion_map: memberId.map((id) => (id.endsWith("_0") ? id : `${id}_0`)), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams })); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getHiddenConversations.ts b/_libs/zca-js/src/apis/getHiddenConversations.ts new file mode 100644 index 0000000..69e5ddd --- /dev/null +++ b/_libs/zca-js/src/apis/getHiddenConversations.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetHiddenConversationsResponse = { + pin: string; + threads: { + /** + * 1: true, 0: false + */ + is_group: number; + thread_id: string; + }[]; +}; + +export const getHiddenConversationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/get-all`); + + /** + * Get hidden conversations + * + * @throws {ZaloApiError} + * + */ + return async function getHiddenConversations() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getLabels.ts b/_libs/zca-js/src/apis/getLabels.ts new file mode 100644 index 0000000..16e1ce8 --- /dev/null +++ b/_libs/zca-js/src/apis/getLabels.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { LabelData } from "../models/index.js"; + +export type GetLabelsResponse = { + labelData: LabelData[]; + version: number; + lastUpdateTime: number; +}; + +type RawResponseType = Omit & { + labelData: string; +}; + +export const getLabelsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/convlabel/get`); + + /** + * Get all labels + * + * @throws {ZaloApiError} + */ + return async function getLabels() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams })); + + return utils.resolve(response, (result) => { + const data = result.data as RawResponseType; + const formattedData: GetLabelsResponse = { + labelData: JSON.parse(data.labelData), + version: data.version, + lastUpdateTime: data.lastUpdateTime, + }; + return formattedData; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getListBoard.ts b/_libs/zca-js/src/apis/getListBoard.ts new file mode 100644 index 0000000..39a8459 --- /dev/null +++ b/_libs/zca-js/src/apis/getListBoard.ts @@ -0,0 +1,79 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { NoteDetail, PinnedMessageDetail, PollDetail} from "../models/index.js"; +import { BoardType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type ListBoardOptions = { + /** + * Page number (default: 1) + */ + page?: number; + /** + * Number of items to retrieve (default: 20) + */ + count?: number; +}; + +export type BoardItem = { + boardType: BoardType; + data: PollDetail | NoteDetail | PinnedMessageDetail; +}; + +export type GetListBoardResponse = { + items: BoardItem[]; + count: number; +}; + +type RawListBoard = { + items: { + boardType: BoardType; + data: Record; + }[]; + count: number; +}; + +export const getListBoardFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/list`); + + /** + * Get list board items in a group + * + * @param options - The options for the request + * @param groupId - The ID of the group + * + * @throws {ZaloApiError} + * + */ + return async function getListBoard(options: ListBoardOptions, groupId: string) { + const requestParams = { + group_id: groupId, + board_type: 0, + page: options.page ?? 1, + count: options.count ?? 20, + last_id: 0, + last_type: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as RawListBoard; + data.items.forEach((item) => { + if (item.boardType != BoardType.Poll) { + const detailData = item.data; + if (typeof detailData.params === "string") { + detailData.params = JSON.parse(detailData.params); + } + } + }); + + return data as GetListBoardResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getListReminder.ts b/_libs/zca-js/src/apis/getListReminder.ts new file mode 100644 index 0000000..4b92d19 --- /dev/null +++ b/_libs/zca-js/src/apis/getListReminder.ts @@ -0,0 +1,88 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderGroup, ReminderUser} from "../models/index.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type ListReminderOptions = { + /** + * Page number (default: 1) + */ + page?: number; + /** + * Number of items to retrieve (default: 20) + */ + count?: number; +}; + +export type ReminderListUser = ReminderUser; +export type ReminderListGroup = ReminderGroup & { + groupId: string; + eventType: number; + responseMem: { + rejectMember: number; + myResp: number; + acceptMember: number; + }; + repeatInfo: { + list_ts: unknown[]; + }; + repeatData: unknown[]; +}; + +export type GetListReminderResponse = (ReminderListUser & ReminderListGroup)[]; + +export const getListReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/list`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/listReminder`), + }; + + /** + * Get list reminder + * + * @param options - The options for the request + * @param threadId - The ID of the thread + * @param type - The type of the thread (User or Group) + * + * @throws {ZaloApiError} + */ + return async function getListReminder( + options: ListReminderOptions, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + const requestParams = { + objectData: JSON.stringify( + type === ThreadType.User + ? { + uid: threadId, + board_type: 1, + page: options.page ?? 1, + count: options.count ?? 20, + last_id: 0, + last_type: 0, + } + : { + group_id: threadId, + board_type: 1, + page: options.page ?? 1, + count: options.count ?? 20, + last_id: 0, + last_type: 0, + }, + ), + ...(type === ThreadType.Group && { imei: ctx.imei }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL[type], { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + return JSON.parse(result.data as string) as GetListReminderResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getMultiUsersByPhones.ts b/_libs/zca-js/src/apis/getMultiUsersByPhones.ts new file mode 100644 index 0000000..345cf2e --- /dev/null +++ b/_libs/zca-js/src/apis/getMultiUsersByPhones.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type UserBasic } from "../models/index.js"; + +export type GetMultiUsersByPhonesResponse = { + [phoneNumber: string]: UserBasic; +}; + +export const getMultiUsersByPhonesFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/profile/multiget`); + + /** + * Get multiple user(s) by their phone numbers. + * + * @param phoneNumbers Phone number(s) + * @param avatarSize Avatar size (default: AvatarSize.Large) + * + * @throws {ZaloApiError} + */ + return async function getMultiUsersByPhones(phoneNumbers: string | string[], avatarSize: AvatarSize = AvatarSize.Large) { + if (!phoneNumbers) throw new ZaloApiError("Missing phoneNumbers"); + if (!Array.isArray(phoneNumbers)) phoneNumbers = [phoneNumbers]; + + phoneNumbers = phoneNumbers.map((phone) => { + if (phone.startsWith("0")) { + if (ctx.language == "vi") phone = "84" + phone.slice(1); + } + return phone; + }); + + const params = { + phones: phoneNumbers, + avatar_size: avatarSize, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getMute.ts b/_libs/zca-js/src/apis/getMute.ts new file mode 100644 index 0000000..d83311f --- /dev/null +++ b/_libs/zca-js/src/apis/getMute.ts @@ -0,0 +1,40 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type MuteEntriesInfo = { + id: string; + duration: number; + startTime: number; + systemTime: number; + currentTime: number; + muteMode: number; +}; + +export type GetMuteResponse = { + chatEntries: MuteEntriesInfo[]; + groupChatEntries: MuteEntriesInfo[]; +}; + +export const getMuteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/getmute`); + + /** + * Get mute + * + * @throws {ZaloApiError} + */ + return async function getMute() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getOwnId.ts b/_libs/zca-js/src/apis/getOwnId.ts new file mode 100644 index 0000000..e6ad339 --- /dev/null +++ b/_libs/zca-js/src/apis/getOwnId.ts @@ -0,0 +1,5 @@ +import { apiFactory } from "../utils.js"; + +export const getOwnIdFactory = apiFactory()((_, ctx) => { + return () => ctx.uid; +}); diff --git a/_libs/zca-js/src/apis/getPendingGroupMembers.ts b/_libs/zca-js/src/apis/getPendingGroupMembers.ts new file mode 100644 index 0000000..51c9b0a --- /dev/null +++ b/_libs/zca-js/src/apis/getPendingGroupMembers.ts @@ -0,0 +1,43 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetPendingGroupMembersUserInfo = { + uid: string; + dpn: string; + avatar: string; + user_submit: null; +}; + +export type GetPendingGroupMembersResponse = { + time: number; + users: GetPendingGroupMembersUserInfo[]; +}; + +export const getPendingGroupMembersFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/pending-mems/list`); + + /** + * Get pending group members + * + * @param groupId - The id of the group to get the pending members + * + * @note Only the group leader and deputy group leader can view + * + * @throws {ZaloApiError} + */ + return async function getPendingGroupMembers(groupId: string) { + const params = { + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getPinConversations.ts b/_libs/zca-js/src/apis/getPinConversations.ts new file mode 100644 index 0000000..2e013ed --- /dev/null +++ b/_libs/zca-js/src/apis/getPinConversations.ts @@ -0,0 +1,31 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetPinConversationsResponse = { + conversations: string[]; + version: number; +}; + +export const getPinConversationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/pinconvers/list`); + + /** + * Get pin conversations + * + * @throws {ZaloApiError} + */ + return async function getPinConversations() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getPollDetail.ts b/_libs/zca-js/src/apis/getPollDetail.ts new file mode 100644 index 0000000..62468d6 --- /dev/null +++ b/_libs/zca-js/src/apis/getPollDetail.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { PollDetail } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type PollDetailResponse = PollDetail; + +export const getPollDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/detail`); + + /** + * Get poll detail + * + * @param pollId Poll ID + * + * @throws {ZaloApiError} + */ + return async function getPollDetail(pollId: number) { + if (!pollId) throw new ZaloApiError("Missing poll id"); + + const params = { + poll_id: pollId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getProductCatalogList.ts b/_libs/zca-js/src/apis/getProductCatalogList.ts new file mode 100644 index 0000000..e7d3c55 --- /dev/null +++ b/_libs/zca-js/src/apis/getProductCatalogList.ts @@ -0,0 +1,58 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { ProductCatalogItem } from "../models/index.js"; + +export type GetProductCatalogListPayload = { + catalogId: string; + /** + * Number of items to retrieve (default: 100) + */ + limit?: number; + versionCatalog?: number; + lastProductId?: string; + /** + * Page number (default: 0) + */ + page?: number; +}; + +export type GetProductCatalogListResponse = { + items: ProductCatalogItem[]; + version: number; + has_more: number; +}; + +export const getProductCatalogListFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/list`); + + /** + * Get product catalog list? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function getProductCatalogList(payload: GetProductCatalogListPayload) { + const params = { + catalog_id: payload.catalogId, + limit: payload.limit ?? 100, + version_catalog: payload.versionCatalog ?? 0, + last_product_id: payload.lastProductId ?? -1, + page: payload.page ?? 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getQR.ts b/_libs/zca-js/src/apis/getQR.ts new file mode 100644 index 0000000..2302b9f --- /dev/null +++ b/_libs/zca-js/src/apis/getQR.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetQRResponse = { + [userId: string]: string; +}; + +export const getQRFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/mget-qr`); + + /** + * Get QR code for users + * + * @param userId User ID or list of user IDs + * + * @throws {ZaloApiError} + */ + return async function getQR(userId: string | string[]) { + if (typeof userId == "string") userId = [userId]; + + const params = { + fids: userId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getQuickMessageList.ts b/_libs/zca-js/src/apis/getQuickMessageList.ts new file mode 100644 index 0000000..9a168c2 --- /dev/null +++ b/_libs/zca-js/src/apis/getQuickMessageList.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { QuickMessage } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetQuickMessageListResponse = { + cursor: number; + version: number; + items: QuickMessage[]; +}; + +export const getQuickMessageListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/list`); + + /** + * Get quick message list + * + * @throws {ZaloApiError} + */ + return async function getQuickMessageList() { + const params = { + version: 0, + lang: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getRelatedFriendGroup.ts b/_libs/zca-js/src/apis/getRelatedFriendGroup.ts new file mode 100644 index 0000000..514d34f --- /dev/null +++ b/_libs/zca-js/src/apis/getRelatedFriendGroup.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetRelatedFriendGroupResponse = { + groupRelateds: { + [friendId: string]: string[]; + }; +}; + +export const getRelatedFriendGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/group/related`); + + /** + * Get related friend group + * + * @param friendId friend ids + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function getRelatedFriendGroup(friendId: string | string[]) { + const friendIds = Array.isArray(friendId) ? friendId : [friendId]; + + const params = { + friend_ids: JSON.stringify(friendIds), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getReminder.ts b/_libs/zca-js/src/apis/getReminder.ts new file mode 100644 index 0000000..a624b7a --- /dev/null +++ b/_libs/zca-js/src/apis/getReminder.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ReminderGroup } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetReminderResponse = ReminderGroup; + +export const getReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/getReminder`); + + /** + * Get reminder details from a group + * + * @param reminderId Reminder ID + * + * @throws {ZaloApiError} + */ + return async function getReminder(reminderId: string) { + const params = { + eventId: reminderId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getReminderResponses.ts b/_libs/zca-js/src/apis/getReminderResponses.ts new file mode 100644 index 0000000..54bb8c5 --- /dev/null +++ b/_libs/zca-js/src/apis/getReminderResponses.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type GetReminderResponsesResponse = { + rejectMember: string[]; + acceptMember: string[]; +}; + +export const getReminderResponsesFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/listResponseEvent`); + + /** + * Get reminder responses + * + * @param reminderId reminder id + * + * @throws {ZaloApiError} + */ + return async function getReminderResponses(reminderId: string) { + const params = { + eventId: reminderId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getSentFriendRequest.ts b/_libs/zca-js/src/apis/getSentFriendRequest.ts new file mode 100644 index 0000000..af0748a --- /dev/null +++ b/_libs/zca-js/src/apis/getSentFriendRequest.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ZBusinessPackage } from "../models/ZBusiness.js"; +import { apiFactory } from "../utils.js"; + +export type SentFriendRequestInfo = { + userId: string; + zaloName: string; + displayName: string; + avatar: string; + globalId: string; + bizPkg: ZBusinessPackage; + fReqInfo: { + message: string; + src: number; + time: number; + }; +}; + +export type GetSentFriendRequestResponse = { + [userId: string]: SentFriendRequestInfo; +}; + +export const getSentFriendRequestFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/requested/list`); + + /** + * Get friend requested + * + * @note Zalo might throw an error with code 112 if you have no friend requests. + * + * @throws {ZaloApiError} + */ + return async function getSentFriendRequest() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getSettings.ts b/_libs/zca-js/src/apis/getSettings.ts new file mode 100644 index 0000000..4cf61c8 --- /dev/null +++ b/_libs/zca-js/src/apis/getSettings.ts @@ -0,0 +1,27 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { UserSetting } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type GetSettingsResponse = UserSetting; + +export const getSettingsFactory = apiFactory()((_api, _ctx, utils) => { + const serviceURL = utils.makeURL(`https://wpa.chat.zalo.me/api/setting/me`); + + /** + * Get my account settings + * + * @throws {ZaloApiError} + */ + return async function getSettings() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getStickerCategoryDetail.ts b/_libs/zca-js/src/apis/getStickerCategoryDetail.ts new file mode 100644 index 0000000..395948d --- /dev/null +++ b/_libs/zca-js/src/apis/getStickerCategoryDetail.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { StickerDetail } from "../models/index.js"; + +export type GetStickerCategoryDetailResponse = StickerDetail[]; + +export const getStickerCategoryDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker[0]}/api/message/sticker/category/sticker_detail`); + + /** + * Get sticker category detail + * + * @param cateId Sticker category ID + * + * @throws {ZaloApiError} + */ + return async function getStickerCategoryDetail(cateId: number) { + const params = { + cid: cateId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/getStickers.ts b/_libs/zca-js/src/apis/getStickers.ts new file mode 100644 index 0000000..0fa97a6 --- /dev/null +++ b/_libs/zca-js/src/apis/getStickers.ts @@ -0,0 +1,57 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { StickerBasic } from "../models/index.js"; + +interface StickerSuggestions { + sugg_sticker: StickerBasic[] | null; + sugg_guggy: StickerBasic[] | null; + sugg_gif: StickerBasic[] | null; +} + +export const getStickersFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker}/api/message/sticker`); + + /** + * Get stickers by keyword + * + * @param keyword Keyword to search for + * @returns Sticker IDs + * + * @throws {ZaloApiError} + */ + return async function getStickers(keyword: string) { + if (!keyword) throw new ZaloApiError("Missing keyword"); + + const params = { + keyword: keyword, + gif: 1, + guggy: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const finalServiceUrl = new URL(serviceURL); + finalServiceUrl.pathname = finalServiceUrl.pathname + "/suggest/stickers"; + + const response = await utils.request( + utils.makeURL(finalServiceUrl.toString(), { + params: encryptedParams, + }), + ); + + return utils.resolve(response, (result) => { + const suggestions = result.data as StickerSuggestions; + const stickerIds: number[] = []; + + // @TODO: Implement these + // suggestions.sugg_guggy, suggestions.sugg_gif + if (suggestions.sugg_sticker) + suggestions.sugg_sticker.forEach((sticker) => stickerIds.push(sticker.sticker_id)); + + return stickerIds; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getStickersDetail.ts b/_libs/zca-js/src/apis/getStickersDetail.ts new file mode 100644 index 0000000..f41e956 --- /dev/null +++ b/_libs/zca-js/src/apis/getStickersDetail.ts @@ -0,0 +1,49 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory, resolveResponse } from "../utils.js"; + +import type { StickerDetail } from "../models/index.js"; + +export type StickerDetailResponse = StickerDetail[]; + +export const getStickersDetailFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker}/api/message/sticker/sticker_detail`); + + /** + * Get stickers detail by sticker IDs + * + * @param stickerIds Sticker IDs to search for + * + * @throws {ZaloApiError} + */ + return async function getStickersDetail(stickerIds: number | number[]) { + if (!stickerIds) throw new ZaloApiError("Missing sticker id"); + if (!Array.isArray(stickerIds)) stickerIds = [stickerIds]; + if (stickerIds.length == 0) throw new ZaloApiError("Missing sticker id"); + + const stickers: StickerDetailResponse = []; + const tasks = stickerIds.map((stickerId) => getStickerDetail(stickerId)); + const tasksResult = await Promise.allSettled(tasks); + tasksResult.forEach((result) => { + if (result.status === "fulfilled") stickers.push(result.value); + }); + + return stickers; + }; + + async function getStickerDetail(stickerId: number) { + const params = { + sid: stickerId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + ); + + return resolveResponse(ctx, response); + } +}); diff --git a/_libs/zca-js/src/apis/getUnreadMark.ts b/_libs/zca-js/src/apis/getUnreadMark.ts new file mode 100644 index 0000000..b9f6a5e --- /dev/null +++ b/_libs/zca-js/src/apis/getUnreadMark.ts @@ -0,0 +1,50 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UnreadMark = { + id: number; + cliMsgId: number; + fromUid: number; + ts: number; +}; + +export type GetUnreadMarkResponse = { + data: { + convsGroup: UnreadMark[]; + convsUser: UnreadMark[]; + }; + status: number; +}; + +export const getUnreadMarkFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/getUnreadMark`); + + /** + * Get unread mark + * + * @throws {ZaloApiError} + * + */ + return async function getUnreadMark() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, (result) => { + const data = result.data as { data: unknown; status: number }; + if (typeof data.data === "string") { + return { + data: JSON.parse(data.data) as GetUnreadMarkResponse["data"], + status: data.status, + }; + } + + return data as GetUnreadMarkResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/getUserInfo.ts b/_libs/zca-js/src/apis/getUserInfo.ts new file mode 100644 index 0000000..d160836 --- /dev/null +++ b/_libs/zca-js/src/apis/getUserInfo.ts @@ -0,0 +1,58 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import { AvatarSize, type User } from "../models/index.js"; + +export type ProfileInfo = User; + +export type UserInfoResponse = { + unchanged_profiles: Record; + phonebook_version: number; + changed_profiles: Record; +}; + +export const getUserInfoFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getprofiles/v2`); + + /** + * Get user info using user id + * + * @param userId User id(s) + * @param avatarSize Avatar size (default: AvatarSize.Small) + * + * @throws {ZaloApiError} + */ + return async function getUserInfo(userId: string | string[], avatarSize: AvatarSize = AvatarSize.Small) { + if (!userId) throw new ZaloApiError("Missing user id"); + + if (!Array.isArray(userId)) userId = [userId]; + + userId = userId.map((id) => { + if (id.split("_").length > 1) { + return id; + } + return `${id}_0`; + }); + + const params = { + phonebook_version: ctx.extraVer!.phonebook, + friend_pversion_map: userId, + avatar_size: avatarSize, + language: ctx.language, + show_online_status: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/inviteUserToGroups.ts b/_libs/zca-js/src/apis/inviteUserToGroups.ts new file mode 100644 index 0000000..68d3424 --- /dev/null +++ b/_libs/zca-js/src/apis/inviteUserToGroups.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type InviteUserToGroupsResponse = { + grid_message_map: { + [groupId: string]: { + error_code: number; + error_message: string; + data: string | null; + }; + }; +}; + +export const inviteUserToGroupsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/invite/multi`); + + /** + * Invite user to group + * + * @param groupId group ID(s) + * @param memberId member ID + * + * @throws {ZaloApiError} + * + */ + return async function inviteUserToGroups(userId: string, groupId: string | string[]) { + const params = { + grids: Array.isArray(groupId) ? groupId : [groupId], + member: userId, + memberType: -1, + srcInteraction: 2, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/joinGroupInviteBox.ts b/_libs/zca-js/src/apis/joinGroupInviteBox.ts new file mode 100644 index 0000000..1123e9c --- /dev/null +++ b/_libs/zca-js/src/apis/joinGroupInviteBox.ts @@ -0,0 +1,31 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type JoinGroupInviteBoxResponse = ""; + +export const joinGroupInviteBoxFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/inv-box/join`); + + /** + * Join group invite box + * + * @param groupId - The group id + * + * @throws {ZaloApiError} + */ + return async function joinGroupInviteBox(groupId: string) { + const params = { + grid: groupId, + lang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/joinGroupLink.ts b/_libs/zca-js/src/apis/joinGroupLink.ts new file mode 100644 index 0000000..f9b2bb4 --- /dev/null +++ b/_libs/zca-js/src/apis/joinGroupLink.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type JoinGroupLinkResponse = ""; + +export const joinGroupLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/link/join`); + + /** + * Join group via invite link + * + * @note Zalo might throw an error with code 240 if the group enabled membership approval, 178 if you are already a member. + * + * @param link - The link join group + * + * @throws {ZaloApiError} + */ + return async function joinGroupLink(link: string) { + const params = { + link: link, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/keepAlive.ts b/_libs/zca-js/src/apis/keepAlive.ts new file mode 100644 index 0000000..a6e9ed4 --- /dev/null +++ b/_libs/zca-js/src/apis/keepAlive.ts @@ -0,0 +1,28 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type KeepAliveResponse = { config_vesion: number }; + +export const keepAliveFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.chat[0]}/keepalive`); + + /** + * Keep Alive? + * + * @throws {ZaloApiError} + */ + return async function keepAlive() { + const params = { + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response, undefined, false); + }; +}); diff --git a/_libs/zca-js/src/apis/lastOnline.ts b/_libs/zca-js/src/apis/lastOnline.ts new file mode 100644 index 0000000..2d6fca8 --- /dev/null +++ b/_libs/zca-js/src/apis/lastOnline.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type LastOnlineResponse = { + settings: { + show_online_status: boolean; + }; + lastOnline: number; +}; + +export const lastOnlineFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/lastOnline`); + + /** + * Get last online + * + * @param uid User ID + * + * @throws {ZaloApiError} + */ + return async function lastOnline(uid: string) { + const params = { + uid: uid, + conv_type: 1, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/leaveGroup.ts b/_libs/zca-js/src/apis/leaveGroup.ts new file mode 100644 index 0000000..901c102 --- /dev/null +++ b/_libs/zca-js/src/apis/leaveGroup.ts @@ -0,0 +1,41 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type LeaveGroupResponse = { + memberError: unknown[]; +}; + +export const leaveGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/leave`); + + /** + * Leave group + * + * @param groupId - groupId to leave + * @param silent - Turn on/off silent leave group + * + * @note Zalo might throw an error with code 166 if you are not a member of the group + * + * @throws {ZaloApiError} + */ + return async function leaveGroup(groupId: string, silent: boolean = false) { + const requestParams = { + grids: [groupId], // API only supports leaving one group at a time + imei: ctx.imei, + silent: silent ? 1 : 0, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/listen.ts b/_libs/zca-js/src/apis/listen.ts new file mode 100644 index 0000000..becd9dd --- /dev/null +++ b/_libs/zca-js/src/apis/listen.ts @@ -0,0 +1,544 @@ +import EventEmitter from "events"; +import WebSocket from "ws"; +import { type FriendEvent, initializeFriendEvent, type TFriendEvent } from "../models/FriendEvent.js"; +import { type GroupEvent, initializeGroupEvent, type TGroupEvent } from "../models/GroupEvent.js"; +import type { Message, TGroupMessage, TMessage, TReaction, Typing } from "../models/index.js"; +import { GroupMessage, UserMessage, Reaction, Undo, ThreadType, GroupTyping, UserTyping } from "../models/index.js"; +import { decodeEventData, getFriendEventType, getGroupEventType, hasOwn, logger, makeURL } from "../utils.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { ContextSession } from "../context.js"; +import { type SeenMessage, GroupSeenMessage, UserSeenMessage } from "../models/SeenMessage.js"; +import { type DeliveredMessage, UserDeliveredMessage, GroupDeliveredMessage } from "../models/DeliveredMessage.js"; + +type UploadEventData = { + fileUrl: string; + fileId: string; +}; + +export type WsPayload> = { + version: number; + cmd: number; + subCmd: number; + data: T; +}; + +export type OnMessageCallback = (message: Message) => unknown; +export type OnClosedCallback = (code: CloseReason, reason: string) => unknown; +export type OnErrorCallback = (error: unknown) => unknown; + +export enum CloseReason { + ManualClosure = 1000, + AbnormalClosure = 1006, + DuplicateConnection = 3000, + KickConnection = 3003, +} + +interface ListenerEvents { + connected: []; + disconnected: [code: CloseReason, reason: string]; + closed: [code: CloseReason, reason: string]; + error: [error: unknown]; + typing: [typing: Typing]; + message: [message: Message]; + old_messages: [messages: Message[], type: ThreadType]; + seen_messages: [messages: SeenMessage[]]; + delivered_messages: [messages: DeliveredMessage[]]; + reaction: [reaction: Reaction]; + old_reactions: [reactions: Reaction[], isGroup: boolean]; + upload_attachment: [data: UploadEventData]; + undo: [data: Undo]; + friend_event: [data: FriendEvent]; + group_event: [data: GroupEvent]; + cipher_key: [key: string]; +} + +export class Listener extends EventEmitter { + private wsURL: string; + private cookie: string; + private userAgent: string; + + private ws: WebSocket | null; + private retryCount: Record< + string, + { + count: number; + max: number; + times: number[]; + } + >; + private rotateCount: number; + + private onConnectedCallback: () => unknown; + private onClosedCallback: OnClosedCallback; + private onErrorCallback: OnErrorCallback; + private onMessageCallback: OnMessageCallback; + private cipherKey?: string; + + private selfListen; + private pingInterval?: Timer; + + private id = 0; + + constructor( + private ctx: ContextSession, + private urls: string[], + ) { + super(); + if (!ctx.cookie) throw new ZaloApiError("Cookie is not available"); + if (!ctx.userAgent) throw new ZaloApiError("User agent is not available"); + + this.wsURL = makeURL(this.ctx, this.urls[0], { + t: Date.now(), + }); + this.retryCount = {}; + this.rotateCount = 0; + + for (const retry in ctx.settings.features.socket.retries) { + const { times, max } = ctx.settings.features.socket.retries[retry]; + this.retryCount[retry] = { + count: 0, + max: max || 0, + times: typeof times === "number" ? [times] : times, + }; + } + + this.cookie = ctx.cookie.getCookieStringSync("https://chat.zalo.me"); + this.userAgent = ctx.userAgent; + + this.selfListen = ctx.options.selfListen; + + this.ws = null; + + this.onConnectedCallback = () => {}; + this.onClosedCallback = () => {}; + this.onErrorCallback = () => {}; + this.onMessageCallback = () => {}; + } + + /** + * @deprecated Use `on` method instead + */ + public onConnected(cb: () => unknown) { + this.onConnectedCallback = cb; + } + + /** + * @deprecated Use `on` method instead + */ + public onClosed(cb: OnClosedCallback) { + this.onClosedCallback = cb; + } + + /** + * @deprecated Use `on` method instead + */ + public onError(cb: OnErrorCallback) { + this.onErrorCallback = cb; + } + + /** + * @deprecated Use `on` method instead + */ + public onMessage(cb: OnMessageCallback) { + this.onMessageCallback = cb; + } + + private canRetry(code: CloseReason) { + if (!this.ctx.settings.features.socket.close_and_retry_codes.includes(code)) return false; + if (this.retryCount[code.toString()].count >= this.retryCount[code.toString()].max) return false; + this.retryCount[code.toString()].count++; + + const { count, max, times } = this.retryCount[code.toString()]; + const retryTime = count - 1 < times.length ? times[count - 1] : times[times.length - 1]; + logger(this.ctx).verbose(`Retry for code ${code} in ${retryTime}ms (${count}/${max})`); + + return retryTime; + } + + private shouldRotate(code: CloseReason) { + if (!this.ctx.settings.features.socket.rotate_error_codes.includes(code)) return false; + if (this.rotateCount >= this.urls.length - 1) return false; + + return true; + } + + private rotateEndpoint() { + this.rotateCount++; + this.wsURL = makeURL(this.ctx, this.urls[this.rotateCount], { + t: Date.now(), + }); + logger(this.ctx).verbose(`Rotating endpoint to ${this.wsURL}`); + } + + public start({ retryOnClose = false }: { retryOnClose?: boolean } = {}) { + if (this.ws) throw new ZaloApiError("Already started"); + + const ws = new WebSocket(this.wsURL, { + headers: { + "accept-encoding": "gzip, deflate, br, zstd", + "accept-language": "en-US,en;q=0.9", + "cache-control": "no-cache", + connection: "Upgrade", + host: new URL(this.wsURL).host, + origin: "https://chat.zalo.me", + prgama: "no-cache", + "sec-websocket-extensions": "permessage-deflate; client_max_window_bits", + "sec-websocket-version": "13", + upgrade: "websocket", + "user-agent": this.userAgent, + cookie: this.cookie, + }, + agent: this.ctx.options.agent + }); + this.ws = ws; + + ws.onopen = () => { + this.onConnectedCallback(); + this.emit("connected"); + }; + + ws.onclose = (event) => { + this.reset(); + this.emit("disconnected", event.code as CloseReason, event.reason); + const retry = retryOnClose && this.canRetry(event.code as CloseReason); + if (retry && retryOnClose) { + const shouldRotate = this.shouldRotate(event.code as CloseReason); + if (shouldRotate) { + this.rotateEndpoint(); + } + setTimeout(() => { + this.start({ retryOnClose: true }); + }, retry); + } else { + this.onClosedCallback(event.code, event.reason); + this.emit("closed", event.code as CloseReason, event.reason); + } + }; + + ws.onerror = (event) => { + this.onErrorCallback(event); + this.emit("error", event); + }; + + ws.onmessage = async (event) => { + const { data } = event; + if (!(data instanceof Buffer)) return; + + const encodedHeader = data.subarray(0, 4); + const [version, cmd, subCmd] = getHeader(encodedHeader); + + try { + const dataToDecode = data.subarray(4); + const decodedData = new TextDecoder("utf-8").decode(dataToDecode); + if (decodedData.length == 0) return; + + const parsed = JSON.parse(decodedData); + + if (version == 1 && cmd == 1 && subCmd == 1 && hasOwn(parsed, "key")) { + this.cipherKey = parsed.key; + this.emit("cipher_key", parsed.key); + + if (this.pingInterval) clearInterval(this.pingInterval); + + const ping = () => { + const payload = { + version: 1, + cmd: 2, + subCmd: 1, + data: { eventId: Date.now() }, + }; + this.sendWs(payload, false); + }; + + this.pingInterval = setInterval(() => { + ping(); + }, this.ctx.settings.features.socket.ping_interval); + } + + if (version == 1 && cmd == 501 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { msgs } = parsedData; + for (const msg of msgs) { + if (typeof msg.content == "object" && hasOwn(msg.content, "deleteMsg")) { + const undoObject = new Undo(this.ctx.uid, msg, false); + if (undoObject.isSelf && !this.selfListen) continue; + this.emit("undo", undoObject); + } else { + const messageObject = new UserMessage(this.ctx.uid, msg); + if (messageObject.isSelf && !this.selfListen) continue; + this.onMessageCallback(messageObject); + this.emit("message", messageObject); + } + } + } + + if (version == 1 && cmd == 521 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { groupMsgs } = parsedData; + for (const msg of groupMsgs) { + if (typeof msg.content == "object" && hasOwn(msg.content, "deleteMsg")) { + const undoObject = new Undo(this.ctx.uid, msg, true); + if (undoObject.isSelf && !this.selfListen) continue; + this.emit("undo", undoObject); + } else { + const messageObject = new GroupMessage(this.ctx.uid, msg); + if (messageObject.isSelf && !this.selfListen) continue; + this.onMessageCallback(messageObject); + this.emit("message", messageObject); + } + } + } + + if (version == 1 && cmd == 601 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { controls } = parsedData; + for (const control of controls) { + if (control.content.act_type == "file_done") { + const data = { + fileUrl: control.content.data.url, + fileId: control.content.fileId, + }; + + const uploadCallback = this.ctx.uploadCallbacks.get(String(control.content.fileId)); + if (uploadCallback) uploadCallback(data); + this.ctx.uploadCallbacks.delete(String(control.content.fileId)); + + this.emit("upload_attachment", data); + } else if (control.content.act_type == "group") { + // 31/08/2024 + // for some reason, Zalo send both join and join_reject event when admin approve join requests + // Zalo itself doesn't seem to handle this properly either, so we gonna ignore the join_reject event + + if (control.content.act == "join_reject") continue; + + const groupEventData: TGroupEvent = + typeof control.content.data == "string" + ? JSON.parse(control.content.data) + : control.content.data; + + const groupEvent = initializeGroupEvent( + this.ctx.uid, + groupEventData, + getGroupEventType(control.content.act), + control.content.act + ); + if (groupEvent.isSelf && !this.selfListen) continue; + this.emit("group_event", groupEvent); + } else if (control.content.act_type == "fr") { + // 28/02/2025 + // Zalo send both req and req_v2 event when user send friend request + // Zalo itself doesn't seem to handle this properly either, so we gonna ignore the req event + + if (control.content.act == "req") continue; + + const friendEventData: TFriendEvent = + typeof control.content.data == "string" + ? JSON.parse(control.content.data) + : control.content.data; + + // Handles the case when act is "pin_create" and params is a string + if ( + typeof friendEventData == "object" && + "topic" in friendEventData && + typeof friendEventData.topic == "object" && + "params" in friendEventData.topic + ) { + friendEventData.topic.params = JSON.parse(`${friendEventData.topic.params}`); + } + + const friendEvent = initializeFriendEvent( + this.ctx.uid, + typeof friendEventData == "number" ? control.content.data : friendEventData, + getFriendEventType(control.content.act), + ); + if (friendEvent.isSelf && !this.selfListen) continue; + this.emit("friend_event", friendEvent); + } + } + } + + if (cmd == 612) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { reacts, reactGroups } = parsedData; + + for (const react of reacts) { + react.content = JSON.parse(react.content); + const reactionObject = new Reaction(this.ctx.uid, react, false); + if (reactionObject.isSelf && !this.selfListen) continue; + + this.emit("reaction", reactionObject); + } + + for (const reactGroup of reactGroups) { + reactGroup.content = JSON.parse(reactGroup.content); + const reactionObject = new Reaction(this.ctx.uid, reactGroup, true); + if (reactionObject.isSelf && !this.selfListen) continue; + + this.emit("reaction", reactionObject); + } + } + + if (cmd == 610 || cmd == 611) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const isGroup = cmd == 611; + + const reacts = parsedData[isGroup ? "reactGroups" : "reacts"] as TReaction[]; + const reactionObjects = reacts.map((react) => new Reaction(this.ctx.uid, react, isGroup)); + + this.emit("old_reactions", reactionObjects, isGroup); + } + + if (cmd == 510 && subCmd == 1) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const msgs = parsedData.msgs as TMessage[]; + const responseMsgs = msgs.map((msg) => new UserMessage(this.ctx.uid, msg)); + this.emit("old_messages", responseMsgs, ThreadType.User); + } + + if (cmd == 511 && subCmd == 1) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const groupMsgs = parsedData.groupMsgs as TGroupMessage[]; + const responseMsgs = groupMsgs.map((msg) => new GroupMessage(this.ctx.uid, msg)); + this.emit("old_messages", responseMsgs, ThreadType.Group); + } + + if (cmd == 602 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { actions } = parsedData; + + for (const action of actions) { + if (action.act_type == "typing") { + const data = JSON.parse(`{${action.data}}`); + if (action.act == "typing") { + const typingObject = new UserTyping(data); + this.emit("typing", typingObject); + } else if (action.act == "gtyping") { + // 26/02/2025 + // For a group with only two people, Zalo doesn't send a typing event. + + const typingObject = new GroupTyping(data); + this.emit("typing", typingObject); + } + } + } + } + + if (cmd == 502 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { delivereds: deliveredMsgs, seens: seenMsgs } = parsedData; + + if (Array.isArray(deliveredMsgs) && deliveredMsgs.length > 0) { + const deliveredObjects = deliveredMsgs.map((delivered) => new UserDeliveredMessage(delivered)); + this.emit("delivered_messages", deliveredObjects); + } + + if (Array.isArray(seenMsgs) && seenMsgs.length > 0) { + const seenObjects = seenMsgs.map((seen) => new UserSeenMessage(seen)); + this.emit("seen_messages", seenObjects); + } + } + + if (cmd == 522 && subCmd == 0) { + const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; + const { delivereds: deliveredMsgs, groupSeens: groupSeenMsgs } = parsedData; + + if (Array.isArray(deliveredMsgs) && deliveredMsgs.length > 0) { + let deliveredObjects = deliveredMsgs.map( + (delivered) => new GroupDeliveredMessage(this.ctx.uid, delivered), + ); + if (!this.selfListen) + deliveredObjects = deliveredObjects.filter((delivered) => !delivered.isSelf); + this.emit("delivered_messages", deliveredObjects); + } + + if (Array.isArray(groupSeenMsgs) && groupSeenMsgs.length > 0) { + let seenObjects = groupSeenMsgs.map((seen) => new GroupSeenMessage(this.ctx.uid, seen)); + if (!this.selfListen) seenObjects = seenObjects.filter((seen) => !seen.isSelf); + this.emit("seen_messages", seenObjects); + } + } + + if (version == 1 && cmd == 3000 && subCmd == 0) { + logger(this.ctx).error(); + logger(this.ctx).error("Another connection is opened, closing this one"); + logger(this.ctx).error(); + if (ws.readyState !== WebSocket.CLOSED) ws.close(CloseReason.DuplicateConnection); + } + } catch (error) { + this.onErrorCallback(error); + this.emit("error", error); + } + }; + } + + public stop() { + if (this.ws) { + this.ws.close(CloseReason.ManualClosure); + this.reset(); + } + } + + public sendWs(payload: WsPayload, requireId: boolean = true) { + if (this.ws) { + if (requireId) payload.data["req_id"] = `req_${this.id++}`; + + const encodedData = new TextEncoder().encode(JSON.stringify(payload.data)); + const dataLength = encodedData.length; + + const data = new DataView(Buffer.alloc(4 + dataLength).buffer); + data.setUint8(0, payload.version); + data.setInt32(1, payload.cmd, true); + data.setInt8(3, payload.subCmd); + + encodedData.forEach((e, i) => { + data.setUint8(4 + i, e); + }); + + this.ws.send(data); + } + } + + /** + * Request old messages + * + * @param lastMsgId + */ + public requestOldMessages(threadType: ThreadType, lastMsgId: string | null = null) { + const payload = { + version: 1, + cmd: threadType === ThreadType.User ? 510 : 511, + subCmd: 1, + data: { first: true, lastId: lastMsgId, preIds: [] }, + }; + this.sendWs(payload); + } + + /** + * Request old messages + * + * @param lastMsgId + */ + public requestOldReactions(threadType: ThreadType, lastMsgId: string | null = null) { + const payload = { + version: 1, + cmd: threadType === ThreadType.User ? 610 : 611, + subCmd: 1, + data: { first: true, lastId: lastMsgId, preIds: [] }, + }; + this.sendWs(payload); + } + + private reset() { + this.ws = null; + this.cipherKey = undefined; + if (this.pingInterval) clearInterval(this.pingInterval); + } +} + +function getHeader(buffer: Buffer) { + if (buffer.byteLength < 4) { + throw new ZaloApiError("Invalid header"); + } + + return [buffer[0], buffer.readUInt16LE(1), buffer[3]]; +} diff --git a/_libs/zca-js/src/apis/lockPoll.ts b/_libs/zca-js/src/apis/lockPoll.ts new file mode 100644 index 0000000..205ed90 --- /dev/null +++ b/_libs/zca-js/src/apis/lockPoll.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type LockPollResponse = ""; + +export const lockPollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/end`); + + /** + * Lock a poll, preventing further votes. + * + * @param pollId Poll id to lock + * + * @throws {ZaloApiError} + */ + return async function lockPoll(pollId: number) { + const params = { + poll_id: pollId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/login.ts b/_libs/zca-js/src/apis/login.ts new file mode 100644 index 0000000..78cb342 --- /dev/null +++ b/_libs/zca-js/src/apis/login.ts @@ -0,0 +1,118 @@ +import type { ContextBase } from "../context.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { decryptResp, getSignKey, logger, makeURL, ParamsEncryptor, request } from "../utils.js"; + +export async function login(ctx: ContextBase, encryptParams: boolean) { + const encryptedParams = await getEncryptParam(ctx, encryptParams, "getlogininfo"); + + try { + const response = await request( + ctx, + makeURL(ctx, "https://wpa.chat.zalo.me/api/login/getLoginInfo", { + ...encryptedParams.params, + nretry: 0, + }), + ); + if (!response.ok) throw new ZaloApiError("Failed to fetch login info: " + response.statusText); + const data = await response.json(); + + if (encryptedParams.enk) { + const decryptedData = decryptResp(encryptedParams.enk, data.data); + return decryptedData != null && typeof decryptedData != "string" ? decryptedData : null; + } + + return null; + } catch (error) { + logger(ctx).error("Login failed:", error); + throw error; + } +} + +export async function getServerInfo(ctx: ContextBase, encryptParams: boolean) { + const encryptedParams = await getEncryptParam(ctx, encryptParams, "getserverinfo"); + if (!encryptedParams.params.signkey || typeof encryptedParams.params.signkey !== "string") + throw new ZaloApiError("Missing signkey"); + + const response = await request( + ctx, + makeURL( + ctx, + "https://wpa.chat.zalo.me/api/login/getServerInfo", + { + imei: ctx.imei as string, + type: ctx.API_TYPE, + client_version: ctx.API_VERSION, + computer_name: "Web", + signkey: encryptedParams.params.signkey, + }, + false, + ), + ); + if (!response.ok) throw new ZaloApiError("Failed to fetch server info: " + response.statusText); + const data = await response.json(); + + if (data.data == null) throw new ZaloApiError("Failed to fetch server info: " + data.error_message); + return data.data; +} + +async function getEncryptParam(ctx: ContextBase, encryptParams: boolean, type: string) { + const params = {} as Record; + const data = { + computer_name: "Web", + imei: ctx.imei!, + language: ctx.language!, + ts: Date.now(), + }; + const encryptedData = await _encryptParam(ctx, data, encryptParams); + + if (encryptedData == null) Object.assign(params, data); + else { + const { encrypted_params, encrypted_data } = encryptedData; + Object.assign(params, encrypted_params); + params.params = encrypted_data; + } + + params.type = ctx.API_TYPE; + params.client_version = ctx.API_VERSION; + params.signkey = + type == "getserverinfo" + ? getSignKey(type, { + imei: ctx.imei, + type: ctx.API_TYPE, + client_version: ctx.API_VERSION, + computer_name: "Web", + }) + : getSignKey(type, params); + + return { + params, + enk: encryptedData ? encryptedData.enk : null, + }; +} + +async function _encryptParam(ctx: ContextBase, data: Record, encryptParams: boolean) { + if (encryptParams) { + const encryptor = new ParamsEncryptor({ + type: ctx.API_TYPE, + imei: data.imei as string, + firstLaunchTime: Date.now(), + }); + try { + const stringifiedData = JSON.stringify(data); + const encryptedKey = encryptor.getEncryptKey(); + const encodedData = ParamsEncryptor.encodeAES(encryptedKey, stringifiedData, "base64", false); + const params = encryptor.getParams(); + + return params + ? { + encrypted_data: encodedData, + encrypted_params: params, + enk: encryptedKey, + } + : null; + } catch (error) { + throw new ZaloApiError("Failed to encrypt params: " + error); + } + } + return null; +} diff --git a/_libs/zca-js/src/apis/loginQR.ts b/_libs/zca-js/src/apis/loginQR.ts new file mode 100644 index 0000000..ae90f04 --- /dev/null +++ b/_libs/zca-js/src/apis/loginQR.ts @@ -0,0 +1,529 @@ +import { CookieJar, type SerializedCookie, type SerializedCookieJar } from "tough-cookie"; + +import { writeFile } from "node:fs/promises"; +import type { ContextBase } from "../context.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { logger, request } from "../utils.js"; +import { ZaloApiLoginQRAborted } from "../Errors/ZaloApiLoginQRAborted.js"; +import { ZaloApiLoginQRDeclined } from "../Errors/ZaloApiLoginQRDeclined.js"; + +export enum LoginQRCallbackEventType { + QRCodeGenerated, + QRCodeExpired, + QRCodeScanned, + QRCodeDeclined, + GotLoginInfo, +} + +export type LoginQRCallbackEvent = + | { + type: LoginQRCallbackEventType.QRCodeGenerated; + data: { + code: string; + image: string; + options: { + enabledCheckOCR: boolean; + enabledMultiLayer: boolean; + }; + token: string; + }; + actions: { + saveToFile: (qrPath?: string) => Promise; + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.QRCodeExpired; + data: null; + actions: { + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.QRCodeScanned; + data: { + avatar: string; + display_name: string; + }; + actions: { + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.QRCodeDeclined; + data: { + code: string; + }; + actions: { + retry: () => unknown; + abort: () => unknown; + }; + } + | { + type: LoginQRCallbackEventType.GotLoginInfo; + data: { + cookie: SerializedCookie[]; + imei: string; + userAgent: string; + }; + actions: null; + }; + +export type LoginQRCallback = (event: LoginQRCallbackEvent) => unknown; + +async function loadLoginPage(ctx: ContextBase) { + const response = await request(ctx, "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", { + headers: { + accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "cache-control": "max-age=0", + priority: "u=0, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "document", + "sec-fetch-mode": "navigate", + "sec-fetch-site": "same-site", + "sec-fetch-user": "?1", + "upgrade-insecure-requests": "1", + Referer: "https://chat.zalo.me/", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + method: "GET", + }); + + const html = await response.text(); + + const regex = /https:\/\/stc-zlogin\.zdn\.vn\/main-([\d.]+)\.js/; + const match = html.match(regex); + + return match?.[1]; +} + +async function getLoginInfo(ctx: ContextBase, version: string) { + const form = new URLSearchParams(); + form.append("continue", "https://zalo.me/pc"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/logininfo", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fzalo.me%2Fpc", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +async function verifyClient(ctx: ContextBase, version: string) { + const form = new URLSearchParams(); + form.append("type", "device"); + form.append("continue", "https://zalo.me/pc"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/verify-client", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fzalo.me%2Fpc", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +async function generate( + ctx: ContextBase, + version: string, +): Promise<{ + data: { + code: string; + image: string; + options: { + enabledCheckOCR: boolean; + enabledMultiLayer: boolean; + }; + token: string; + } | null; + error_code: number; + error_message: string; +}> { + const form = new URLSearchParams(); + form.append("continue", "https://zalo.me/pc"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/authen/qr/generate", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fzalo.me%2Fpc", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +async function saveQRCodeToFile(filepath: string, imageData: string) { + await writeFile(filepath, imageData, "base64"); +} + +async function waitingScan( + ctx: ContextBase, + version: string, + code: string, + signal: AbortSignal, +): Promise< + | { + data: { + avatar: string; + display_name: string; + } | null; + error_code: number; + error_message: string; + } + | undefined +> { + const form = new URLSearchParams(); + form.append("code", code); + form.append("continue", "https://chat.zalo.me/"); + form.append("v", version); + + return await request(ctx, "https://id.zalo.me/account/authen/qr/waiting-scan", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + signal, + }) + .then((res) => res.json()) + .then((data) => { + if (data.error_code == 8) { + return waitingScan(ctx, version, code, signal); + } + + return data; + }) + .catch((e) => { + if (!signal.aborted) logger(ctx).error(e); + }); +} + +async function waitingConfirm( + ctx: ContextBase, + version: string, + code: string, + signal: AbortSignal, +): Promise< + | { + data: null; + error_code: number; + error_message: string; + } + | undefined +> { + const form = new URLSearchParams(); + form.append("code", code); + form.append("gToken", ""); + form.append("gAction", "CONFIRM_QR"); + form.append("continue", "https://chat.zalo.me/"); + form.append("v", version); + + logger(ctx).info("Please confirm on your phone"); + + return await request(ctx, "https://id.zalo.me/account/authen/qr/waiting-confirm", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + "content-type": "application/x-www-form-urlencoded", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + body: form, + method: "POST", + signal, + }) + .then((res) => res.json()) + .then((data) => { + if (data.error_code == 8) { + return waitingConfirm(ctx, version, code, signal); + } + + return data; + }) + .catch((e) => { + if (!signal.aborted) logger(ctx).error(e); + }); +} + +async function checkSession(ctx: ContextBase) { + return await request( + ctx, + "https://id.zalo.me/account/checksession?continue=https%3A%2F%2Fchat.zalo.me%2Findex.html", + { + headers: { + accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + priority: "u=0, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "document", + "sec-fetch-mode": "navigate", + "sec-fetch-site": "same-origin", + "upgrade-insecure-requests": "1", + Referer: "https://id.zalo.me/account?continue=https%3A%2F%2Fchat.zalo.me%2F", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + redirect: "manual", + method: "GET", + }, + ).catch(logger(ctx).error); +} + +async function getUserInfo(ctx: ContextBase): Promise< + | { + data: { + logged: boolean; + session_chat_valid: boolean; + info: { + name: string; + avatar: string; + }; + } | null; + error_code: number; + error_message: string; + } + | undefined +> { + return await request(ctx, "https://jr.chat.zalo.me/jr/userinfo", { + headers: { + accept: "*/*", + "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-site", + Referer: "https://chat.zalo.me/", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, + method: "GET", + }) + .then((res) => res.json()) + .catch(logger(ctx).error); +} + +export async function loginQR( + ctx: ContextBase, + options: { userAgent: string; qrPath?: string }, + callback?: LoginQRCallback, +) { + ctx.cookie = new CookieJar(); + ctx.userAgent = options.userAgent; + + return new Promise<{ + userInfo: { + name: string; + avatar: string; + }; + cookies: SerializedCookieJar["cookies"]; + // @TODO + // eslint-disable-next-line + } | null>(async (resolve, reject) => { + const controller = new AbortController(); + let qrTimeout: NodeJS.Timer | null = null; + + function cleanUp() { + controller.abort(); + if (qrTimeout) { + clearTimeout(qrTimeout); + qrTimeout = null; + } + } + + try { + function retry() { + cleanUp(); + return resolve(loginQR(ctx, options, callback)); + } + + function abort() { + cleanUp(); + return reject(new ZaloApiLoginQRAborted()); + } + + if (ctx.options.logging) console.log(); + + const loginVersion = await loadLoginPage(ctx); + if (!loginVersion) throw new ZaloApiError("Cannot get API login version"); + + logger(ctx).info("Got login version:", loginVersion); + + await getLoginInfo(ctx, loginVersion); + await verifyClient(ctx, loginVersion); + const qrGenResult = await generate(ctx, loginVersion); + if (!qrGenResult || !qrGenResult.data) + throw new ZaloApiError(`Unable to generate QRCode\nResponse: ${JSON.stringify(qrGenResult, null, 2)}`); + + const qrData = qrGenResult.data; + + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeGenerated, + data: { + ...qrGenResult.data, + image: qrGenResult.data.image.replace(/^data:image\/png;base64,/, ""), + }, + actions: { + async saveToFile(qrPath = options.qrPath ?? "qr.png") { + await saveQRCodeToFile(qrPath, qrData.image.replace(/^data:image\/png;base64,/, "")); + logger(ctx).info("Scan the QR code at", `'${qrPath}'`, "to proceed with login"); + }, + retry, + abort, + }, + }); + } else { + const qrPath = options.qrPath ?? "qr.png"; + + await saveQRCodeToFile(qrPath, qrData.image.replace(/^data:image\/png;base64,/, "")); + logger(ctx).info("Scan the QR code at", `'${qrPath}'`, "to proceed with login"); + } + + qrTimeout = setTimeout(() => { + cleanUp(); + + logger(ctx).info("QR expired!"); + + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeExpired, + data: null, + actions: { + retry, + abort, + }, + }); + } else { + retry(); + } + }, 100_000); + + const scanResult = await waitingScan(ctx, loginVersion, qrGenResult.data.code, controller.signal); + if (!scanResult || !scanResult.data) throw new ZaloApiError("Cannot get scan result"); + + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeScanned, + data: scanResult.data, + actions: { + retry, + abort, + }, + }); + } + + const confirmResult = await waitingConfirm(ctx, loginVersion, qrGenResult.data.code, controller.signal); + if (!confirmResult) throw new ZaloApiError("Cannot get confirm result"); + + clearTimeout(qrTimeout); + + if (confirmResult.error_code == -13) { + if (callback) { + callback({ + type: LoginQRCallbackEventType.QRCodeDeclined, + data: { + code: qrData.code, + }, + actions: { + retry, + abort, + }, + }); + } else { + logger(ctx).error("QRCode login declined"); + throw new ZaloApiLoginQRDeclined(); + } + + return; + } else if (confirmResult.error_code != 0) { + throw new ZaloApiError(`An error has occurred.\nResponse: ${JSON.stringify(confirmResult, null, 2)}`); + } + + const checkSessionResult = await checkSession(ctx); + if (!checkSessionResult) throw new ZaloApiError("Cannot get session, login failed"); + + logger(ctx).info("Successfully logged into the account", scanResult.data.display_name); + + const userInfo = await getUserInfo(ctx); + if (!userInfo || !userInfo.data) throw new ZaloApiError("Can't get account info"); + if (!userInfo.data.logged) throw new ZaloApiError("Can't login"); + + resolve({ + cookies: ctx.cookie!.toJSON()!.cookies, + userInfo: userInfo.data.info, + }); + } catch (error) { + cleanUp(); + reject(error); + } + }); +} diff --git a/_libs/zca-js/src/apis/parseLink.ts b/_libs/zca-js/src/apis/parseLink.ts new file mode 100644 index 0000000..808763e --- /dev/null +++ b/_libs/zca-js/src/apis/parseLink.ts @@ -0,0 +1,53 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ParseLinkErrorMaps = Record; + +export type ParseLinkResponse = { + data: { + thumb: string; + title: string; + desc: string; + src: string; + href: string; + media: { + type: number; + count: number; + mediaTitle: string; + artist: string; + streamUrl: string; + stream_icon: string; + }; + stream_icon: string; + }; + error_maps: ParseLinkErrorMaps; +}; + +export const parseLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/parselink`); + + /** + * Parse link + * + * @param link link to parse + * + * @throws {ZaloApiError} + * + */ + return async function parseLink(link: string) { + const params = { + link, + version: 1, // version 0 is not available errorMaps || version 1 is errorMaps (for response) + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/rejectFriendRequest.ts b/_libs/zca-js/src/apis/rejectFriendRequest.ts new file mode 100644 index 0000000..045c4e9 --- /dev/null +++ b/_libs/zca-js/src/apis/rejectFriendRequest.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RejectFriendRequestResponse = ""; + +export const rejectFriendRequestFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/reject`); + + /** + * Reject a friend request from a User + * + * @param friendId + * + * @throws {ZaloApiError} + */ + return async function rejectFriendRequest(friendId: string) { + const params = { + fid: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeFriend.ts b/_libs/zca-js/src/apis/removeFriend.ts new file mode 100644 index 0000000..046b026 --- /dev/null +++ b/_libs/zca-js/src/apis/removeFriend.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveFriendResponse = ""; + +export const removeFriendFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/remove`); + + /** + * Remove friend + * + * @param friendId - ID of the friend to remove + * + * @throws {ZaloApiError} + */ + return async function removeFriend(friendId: string) { + const params = { + fid: friendId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeFriendAlias.ts b/_libs/zca-js/src/apis/removeFriendAlias.ts new file mode 100644 index 0000000..f1059e3 --- /dev/null +++ b/_libs/zca-js/src/apis/removeFriendAlias.ts @@ -0,0 +1,30 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveFriendAliasResponse = ""; + +export const removeFriendAliasFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.alias[0]}/api/alias/remove`); + + /** + * Remove friend's alias + * + * @param friendId friend id + * + * @throws {ZaloApiError} + */ + return async function removeFriendAlias(friendId: string) { + const params = { + friendId: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeGroupBlockedMember.ts b/_libs/zca-js/src/apis/removeGroupBlockedMember.ts new file mode 100644 index 0000000..a02ed4d --- /dev/null +++ b/_libs/zca-js/src/apis/removeGroupBlockedMember.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveGroupBlockedMemberResponse = ""; + +export const removeGroupBlockedMemberFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/blockedmems/remove`); + + /** + * Remove group blocked member + * + * @param memberId member id(s) + * @param groupId group id + * + * @throws {ZaloApiError} + */ + return async function removeGroupBlockedMember(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeGroupDeputy.ts b/_libs/zca-js/src/apis/removeGroupDeputy.ts new file mode 100644 index 0000000..8f4658f --- /dev/null +++ b/_libs/zca-js/src/apis/removeGroupDeputy.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveGroupDeputyResponse = ""; + +export const removeGroupDeputyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/admins/remove`); + + /** + * Remove group deputy + * + * @param memberId user Id or list of user Ids + * @param groupId group Id + * + * @throws {ZaloApiError} + * + */ + return async function removeGroupDeputy(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeQuickMessage.ts b/_libs/zca-js/src/apis/removeQuickMessage.ts new file mode 100644 index 0000000..056e77c --- /dev/null +++ b/_libs/zca-js/src/apis/removeQuickMessage.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveQuickMessageResponse = { + itemIds: number[]; + version: number; +}; + +export const removeQuickMessageFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/delete`); + + /** + * Remove quick message + * + * @param itemIds - The id(s) of the quick message(s) to remove (number or number[]) + * + * @note Zalo might throw an error with code 212 if the item does not exist + * + * @throws {ZaloApiError} + */ + return async function removeQuickMessage(itemIds: number | number[]) { + const idsArray = Array.isArray(itemIds) ? itemIds : [itemIds]; + + const params = { + itemIds: idsArray, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeReminder.ts b/_libs/zca-js/src/apis/removeReminder.ts new file mode 100644 index 0000000..df822b2 --- /dev/null +++ b/_libs/zca-js/src/apis/removeReminder.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveReminderResponse = "" | number; + +export const removeReminderFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/oneone/remove`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group_board[0]}/api/board/topic/remove`), + }; + + /** + * Remove a reminder in a (user/group) + * + * @param reminderId Reminder ID to remove reminder from + * @param threadId (User/Group) ID to remove reminder from + * @param type Thread type (User or Group) + * + * @throws {ZaloApiError} + */ + return async function removeReminder(reminderId: string, threadId: string, type: ThreadType = ThreadType.User) { + const params = + type === ThreadType.User + ? { + uid: threadId, + reminderId: reminderId, + } + : { + grid: threadId, + topicId: reminderId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/removeUnreadMark.ts b/_libs/zca-js/src/apis/removeUnreadMark.ts new file mode 100644 index 0000000..c1a5729 --- /dev/null +++ b/_libs/zca-js/src/apis/removeUnreadMark.ts @@ -0,0 +1,63 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveUnreadMarkResponse = { + data: { + updateId: number; + }; + status: number; +}; + +export const removeUnreadMarkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/removeUnreadMark`); + + /** + * Remove unread mark from conversation + * + * @param threadId Thread ID + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function removeUnreadMark(threadId: string, type: ThreadType = ThreadType.User) { + const timestamp = Date.now(); + + const isGroup = type === ThreadType.Group; + const requestParams = { + param: JSON.stringify({ + [isGroup ? "convsGroup" : "convsUser"]: [threadId], + [isGroup ? "convsUser" : "convsGroup"]: [], + [isGroup ? "convsGroupData" : "convsUserData"]: [ + { + id: threadId, + ts: timestamp, + }, + ], + [isGroup ? "convsUserData" : "convsGroupData"]: [], + }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response, (result) => { + const data = result.data as { data: unknown; status: number }; + if (typeof data.data === "string") { + return { + data: JSON.parse(data.data), + status: data.status, + }; + } + + return result.data as RemoveUnreadMarkResponse; + }); + }; +}); diff --git a/_libs/zca-js/src/apis/removeUserFromGroup.ts b/_libs/zca-js/src/apis/removeUserFromGroup.ts new file mode 100644 index 0000000..d8f0129 --- /dev/null +++ b/_libs/zca-js/src/apis/removeUserFromGroup.ts @@ -0,0 +1,42 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type RemoveUserFromGroupResponse = { + errorMembers: string[]; +}; + +export const removeUserFromGroupFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/kickout`); + + /** + * Remove user from existing group + * + * @param memberId User ID or list of user IDs to remove + * @param groupId Group ID + * + * @note Zalo might throw an error with code 165 if the user is not in the group, 166 if you don't have enough permissions or is not in the group + * + * @throws {ZaloApiError} + */ + return async function removeUserFromGroup(memberId: string | string[], groupId: string) { + if (!Array.isArray(memberId)) memberId = [memberId]; + + const params = { + grid: groupId, + members: memberId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/resetHiddenConversPin.ts b/_libs/zca-js/src/apis/resetHiddenConversPin.ts new file mode 100644 index 0000000..31a1356 --- /dev/null +++ b/_libs/zca-js/src/apis/resetHiddenConversPin.ts @@ -0,0 +1,26 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ResetHiddenConversPinResponse = ""; + +export const resetHiddenConversPinFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/reset`); + + /** + * Reset hidden conversation pin + * + * @throws {ZaloApiError} + */ + return async function resetHiddenConversPin() { + const params = {}; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/reuseAvatar.ts b/_libs/zca-js/src/apis/reuseAvatar.ts new file mode 100644 index 0000000..a0ba850 --- /dev/null +++ b/_libs/zca-js/src/apis/reuseAvatar.ts @@ -0,0 +1,32 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ReuseAvatarResponse = null; + +export const reuseAvatarFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/reuse-avatar`); + + /** + * Reuse avatar + * + * @param photoId photo id from getAvatarList api + * + * @throws {ZaloApiError} + */ + return async function reuseAvatar(photoId: string) { + const params = { + photoId: photoId, + isPostSocial: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/reviewPendingMemberRequest.ts b/_libs/zca-js/src/apis/reviewPendingMemberRequest.ts new file mode 100644 index 0000000..01af499 --- /dev/null +++ b/_libs/zca-js/src/apis/reviewPendingMemberRequest.ts @@ -0,0 +1,51 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type ReviewPendingMemberRequestPayload = { + members: string | string[]; + isApprove: boolean; +}; + +export enum ReviewPendingMemberRequestStatus { + SUCCESS = 0, + NOT_IN_PENDING_LIST = 170, + ALREADY_IN_GROUP = 178, + INSUFFICIENT_PERMISSION = 166, +} + +export type ReviewPendingMemberRequestResponse = { + [memberId: string]: ReviewPendingMemberRequestStatus; +}; + +export const reviewPendingMemberRequestFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/pending-mems/review`); + + /** + * Review pending member(s) request + * + * @param payload - The payload containing data to review the pending member(s) request + * @param groupId - The id of the group to review the pending member(s) request + * + * @note Only the group leader and deputy group leader can review + * + * @throws {ZaloApiError} + */ + return async function reviewPendingMemberRequest(payload: ReviewPendingMemberRequestPayload, groupId: string) { + if (!Array.isArray(payload.members)) payload.members = [payload.members]; + + const params = { + grid: groupId, + members: payload.members, + isApprove: payload.isApprove ? 1 : 0, // 1: approve, 0: reject + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/searchSticker.ts b/_libs/zca-js/src/apis/searchSticker.ts new file mode 100644 index 0000000..5c7e6b9 --- /dev/null +++ b/_libs/zca-js/src/apis/searchSticker.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { StickerBasic } from "../models/index.js"; + +export type SearchStickerResponse = StickerBasic[]; + +export const searchStickerFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.sticker[0]}/api/message/sticker/search`); + + /** + * Search stickers + * + * @param keyword Keyword to search stickers + * @param limit Limit of stickers to return (default: 50) + * + * @throws {ZaloApiError} + */ + return async function searchSticker(keyword: string, limit: number = 50) { + const params = { + keyword: keyword, + limit: limit, + srcType: 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendBankCard.ts b/_libs/zca-js/src/apis/sendBankCard.ts new file mode 100644 index 0000000..810c422 --- /dev/null +++ b/_libs/zca-js/src/apis/sendBankCard.ts @@ -0,0 +1,49 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { BinBankCard } from "../models/index.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendBankCardPayload = { + binBank: BinBankCard; + numAccBank: string; + nameAccBank?: string; +}; + +export type SendBankCardResponse = ""; + +export const sendBankCardFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.zimsg[0]}/api/transfer/card`); + + /** + * Send bank card + * + * @param payload The payload containing the bank card information + * @param threadId The ID of the thread to send the bank card to + * @param type The type of thread to send the bank card to + * + * @throws {ZaloApiError} + */ + return async function sendBankCard(payload: SendBankCardPayload, threadId: string, type: ThreadType = ThreadType.User) { + const params = { + binBank: payload.binBank, + numAccBank: payload.numAccBank, + nameAccBank: payload.nameAccBank?.toUpperCase() || "---", + cliMsgId: Date.now().toString(), + tsMsg: Date.now(), + destUid: threadId, + destType: type === ThreadType.Group ? 1 : 0, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendCard.ts b/_libs/zca-js/src/apis/sendCard.ts new file mode 100644 index 0000000..dad3476 --- /dev/null +++ b/_libs/zca-js/src/apis/sendCard.ts @@ -0,0 +1,75 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendCardOptions = { + userId: string; + phoneNumber?: string; + ttl?: number; +}; + +export type SendCardResponse = { + msgId: number; +}; + +export const sendCardFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/forward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/forward`), + }; + + /** + * Send a card to a User - Group + * + * @param userId Unique ID for Card + * @param phoneNumber Optional phone number for sending card to a User + * @param ttl Time to live in milliseconds (default: 0) + * @param threadId ID of the conversation + * @param type Message type (User or GroupMessage) + * + * @throws {ZaloApiError} + * + */ + return async function sendCard(options: SendCardOptions, threadId: string, type: ThreadType = ThreadType.User) { + const data = await api.getQR(options.userId); + const QRCodeURL = data[options.userId]; + const clientId = Date.now().toString(); + + const params: Record = { + ttl: options.ttl ?? 0, + msgType: 6, + clientId: clientId, + msgInfo: { + contactUid: options.userId, + qrCodeUrl: QRCodeURL, + }, + }; + + if (options.phoneNumber) { + (params.msgInfo as Record).phone = options.phoneNumber; + } + + if (type == ThreadType.Group) { + params.visibility = 0; + params.grid = threadId; + } else { + params.toId = threadId; + params.imei = ctx.imei; + } + + const msgInfoStringified = JSON.stringify(params.msgInfo); + params.msgInfo = msgInfoStringified; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendDeliveredEvent.ts b/_libs/zca-js/src/apis/sendDeliveredEvent.ts new file mode 100644 index 0000000..fd808c5 --- /dev/null +++ b/_libs/zca-js/src/apis/sendDeliveredEvent.ts @@ -0,0 +1,103 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { MAX_MESSAGES_PER_SEND } from "../context.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendDeliveredEventResponse = "" | { status: number }; + +export type SendDeliveredEventMessageParams = { + msgId: string; + cliMsgId: string; + uidFrom: string; + idTo: string; + msgType: string; + st: number; + at: number; + cmd: number; + ts: string | number; +}; + +type RequestMessageParams = { + gmi: string; + cmi: string; + si: string; + di: string; + mt: string; + st: number; + at: number; + cmd: number; + ts: number; +}; + +type MsgInfos = { + seen: number; + data: RequestMessageParams[]; + grid?: string; // Optional, any random value works +}; + +export const sendDeliveredEventFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/deliveredv2`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/deliveredv2`), + }; + + /** + * Send message delivered event + * + * @param isSeen Whether the message is seen or not + * @param messages List of messages to send delivered event + * @param type Messages type (User or Group), defaults to User + * + * @throws {ZaloApiError} + */ + return async function sendDeliveredEvent( + isSeen: boolean, + messages: SendDeliveredEventMessageParams | SendDeliveredEventMessageParams[], + type: ThreadType = ThreadType.User, + ) { + if (!messages) throw new ZaloApiError("messages are missing or not in a valid array format."); + if (!Array.isArray(messages)) messages = [messages]; + if (messages.length === 0 || messages.length > MAX_MESSAGES_PER_SEND) + throw new ZaloApiError("messages must contain between 1 and 50 messages."); + + // 27/02/2025 + // This can send messages from multiple groups, but to prevent potential issues, + // we will restrict it to sending messages only within the same group. + const idTo = messages[0].idTo; + if (type === ThreadType.Group && !messages.every((msg) => msg.idTo === idTo)) + throw new ZaloApiError("All messages must have the same idTo for Group thread"); + + const msgInfos: MsgInfos = { + seen: isSeen ? 1 : 0, + data: messages.map((msg) => ({ + cmi: msg.cliMsgId, + gmi: msg.msgId, + si: msg.uidFrom, + di: msg.idTo === ctx.uid ? "0" : msg.idTo, + mt: msg.msgType, + st: msg.st || 0 === msg.st ? 0 : -1, + at: msg.at || 0 === msg.at ? 0 : -1, + cmd: msg.cmd || 0 === msg.cmd ? 0 : -1, + ts: parseInt(`${msg.ts}`) || 0 === parseInt(`${msg.ts}`) ? 0 : -1, + })), + ...(type === ThreadType.User ? {} : { grid: idTo }), + }; + + const params = { + msgInfos: JSON.stringify(msgInfos), + ...(type === ThreadType.User ? {} : { imei: ctx.imei }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendFriendRequest.ts b/_libs/zca-js/src/apis/sendFriendRequest.ts new file mode 100644 index 0000000..63cac0e --- /dev/null +++ b/_libs/zca-js/src/apis/sendFriendRequest.ts @@ -0,0 +1,45 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type SendFriendRequestResponse = ""; + +export const sendFriendRequestFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/sendreq`); + + /** + * Send a friend request to a user. + * + * @param msg message sent with friend request + * @param userId User ID to send friend request to + * + * @note Zalo might throw an error with code 225 if the user is already friends with you, + * 215 if the user might have blocked you, + * 222 if user has already sent you a friend request, your request will be treated as acceptance instead. + * + * @throws {ZaloApiError} + */ + return async function sendFriendRequest(msg: string, userId: string) { + const params = { + toid: userId, + msg: msg, + reqsrc: 30, + imei: ctx.imei, + language: ctx.language, + srcParams: JSON.stringify({ + uidTo: userId, + }), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendLink.ts b/_libs/zca-js/src/apis/sendLink.ts new file mode 100644 index 0000000..64895bb --- /dev/null +++ b/_libs/zca-js/src/apis/sendLink.ts @@ -0,0 +1,76 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendLinkOptions = { + msg?: string; + link: string; + ttl?: number; +}; + +export type SendLinkResponse = { + msgId: string; +}; + +export const sendLinkFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/link`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/sendlink`, { + nretry: 0, + }), + }; + + /** + * Send link + * + * @param options Link and ttl + * @param threadId Thread ID + * @param type Thread type + * + * @throws {ZaloApiError} + */ + return async function sendLink(options: SendLinkOptions, threadId: string, type: ThreadType = ThreadType.User) { + const res = await api.parseLink(options.link); + + const params: Record = { + msg: + options.msg && options.msg.trim() + ? options.msg.includes(options.link) + ? options.msg + : options.msg + " " + options.link + : options.link, + href: res.data.href, + src: res.data.src, + title: res.data.title, + desc: res.data.desc, + thumb: res.data.thumb, + type: 2, // 0 + media: JSON.stringify(res.data.media), + ttl: options.ttl ?? 0, + clientId: Date.now(), + }; + + if (type == ThreadType.Group) { + params.grid = threadId; + params.imei = ctx.imei; + // params.mentionInfo = "[]"; @TODO: implement this + } else { + params.toId = threadId; + params.mentionInfo = ""; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendMessage.ts b/_libs/zca-js/src/apis/sendMessage.ts new file mode 100644 index 0000000..5e16481 --- /dev/null +++ b/_libs/zca-js/src/apis/sendMessage.ts @@ -0,0 +1,674 @@ +import FormData from "form-data"; +import fs from "node:fs/promises"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType, type TMessage, type AttachmentSource } from "../models/index.js"; +import { + apiFactory, + getClientMessageType, + getFileExtension, + getFileName, + getGifMetaData, + getMd5LargeFileObject, + removeUndefinedKeys, + resolveResponse, +} from "../utils.js"; + +export type SendMessageResult = { + msgId: number; +}; + +export type SendMessageResponse = { + message: SendMessageResult | null; + attachment: SendMessageResult[]; +}; + +export type SendMessageQuote = { + content: TMessage["content"]; + msgType: TMessage["msgType"]; + propertyExt: TMessage["propertyExt"]; + + uidFrom: TMessage["uidFrom"]; + msgId: TMessage["msgId"]; + cliMsgId: TMessage["cliMsgId"]; + ts: TMessage["ts"]; + ttl: TMessage["ttl"]; +}; + +const attachmentUrlType = { + image: "photo_original/send?", + gif: "gif?", + video: "asyncfile/msg?", + others: "asyncfile/msg?", +}; + +function prepareQMSGAttach(quote: SendMessageQuote) { + const quoteData = quote; + if (typeof quoteData.content == "string") return quoteData.propertyExt; + if (quoteData.msgType == "chat.todo") + return { + properties: { + color: 0, + size: 0, + type: 0, + subType: 0, + ext: '{"shouldParseLinkOrContact":0}', + }, + }; + + return { + ...quoteData.content, + thumbUrl: quoteData.content.thumb, + oriUrl: quoteData.content.href, + normalUrl: quoteData.content.href, + }; +} + +function prepareQMSG(quote: SendMessageQuote) { + const quoteData = quote; + if (quoteData.msgType == "chat.todo" && typeof quoteData.content == "object" && typeof quoteData.content.params == "string") { + return JSON.parse(quoteData.content.params).item.content; + } + + return ""; +} + +type SendType = { + url: string; + body?: BodyInit | null; + headers?: Record; +}; + +type UpthumbType = { + hdUrl: string; + clientFileId: string; + url: string; + fileId: string; +}; + +type AttachmentData = + | { + query?: Record; + fileType: "image" | "video" | "others"; + body: URLSearchParams; + params: Record; + } + | { + query?: Record; + fileType: "gif"; + body: Buffer; + headers: Record; + }; + +export enum TextStyle { + Bold = "b", + Italic = "i", + Underline = "u", + StrikeThrough = "s", + Red = "c_db342e", + Orange = "c_f27806", + Yellow = "c_f7b503", + Green = "c_15a85f", + Small = "f_13", + Big = "f_18", + UnorderedList = "lst_1", + OrderedList = "lst_2", + Indent = "ind_$", +} + +export type Style = + | { + start: number; + len: number; + st: Exclude; + } + | { + start: number; + len: number; + st: TextStyle.Indent; + /** + * Number of spaces used for indentation. + */ + indentSize?: number; + }; + +export enum Urgency { + Default, + Important, + Urgent, +} + +export type Mention = { + /** + * mention position + */ + pos: number; + /** + * id of the mentioned user + */ + uid: string; + /** + * length of the mention + */ + len: number; +}; + +export type MessageContent = { + /** + * Text content of the message + */ + msg: string; + /** + * Text styles + */ + styles?: Style[]; + /** + * Urgency of the message + */ + urgency?: Urgency; + /** + * Quoted message (optional) + */ + quote?: SendMessageQuote; + /** + * Mentions in the message (optional) + */ + mentions?: Mention[]; + /** + * Attachments in the message (optional) + */ + attachments?: AttachmentSource | AttachmentSource[]; + /** + * Time to live in milliseconds + */ + ttl?: number; +}; + +export const sendMessageFactory = apiFactory()((api, ctx, utils) => { + const serviceURLs = { + message: { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group`, { + nretry: 0, + }), + }, + attachment: { + [ThreadType.User]: `${api.zpwServiceMap.file[0]}/api/message/`, + [ThreadType.Group]: `${api.zpwServiceMap.file[0]}/api/group/`, + }, + }; + + const { sharefile } = ctx.settings!.features; + + function isExceedMaxFile(totalFile: number) { + return totalFile > sharefile.max_file; + } + + function isExceedMaxFileSize(fileSize: number) { + return fileSize > sharefile.max_size_share_file_v3 * 1024 * 1024; + } + + function getGroupLayoutId() { + return Date.now(); + } + + async function send(data: SendType | SendType[]): Promise { + if (!Array.isArray(data)) data = [data]; + + const requests: Promise[] = []; + + for (const each of data) { + requests.push( + (async () => { + const response = await utils.request(each.url, { + method: "POST", + body: each.body, + headers: each.headers, + }); + + return await resolveResponse(ctx, response); + })(), + ); + } + + return await Promise.all(requests); + } + + async function upthumb(source: AttachmentSource, url: string): Promise { + const formData = new FormData(); + const buffer = typeof source == "string" ? await fs.readFile(source) : source.data; + formData.append("fileContent", buffer, { + filename: "blob", + contentType: "image/png", + }); + + const params = { + clientId: Date.now(), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request( + utils.makeURL(url + "upthumb?", { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return await resolveResponse(ctx, response); + } + + function handleMentions(type: ThreadType, msg: string, mentions?: Mention[]) { + let totalMentionLen = 0; + const mentionsFinal = + Array.isArray(mentions) && type == ThreadType.Group + ? mentions + .filter((m) => m.pos >= 0 && m.uid && m.len > 0) + .map((m) => { + totalMentionLen += m.len; + return { + pos: m.pos, + uid: m.uid, + len: m.len, + type: m.uid == "-1" ? 1 : 0, + }; + }) + : []; + + if (totalMentionLen > msg.length) { + throw new ZaloApiError("Invalid mentions: total mention characters exceed message length"); + } + + return { + mentionsFinal, + msgFinal: msg, + }; + } + + function handleStyles(params: Record, styles?: Style[]) { + if (styles) + Object.assign(params, { + textProperties: JSON.stringify({ + styles: styles.map((e) => { + const styleFinal = { + ...e, + indentSize: undefined, + st: + e.st == TextStyle.Indent + ? TextStyle.Indent.replace(/\$/g, `${e.indentSize ?? 1}0`) + : e.st, + }; + + removeUndefinedKeys(styleFinal); + return styleFinal; + }), + ver: 0, + }), + }); + } + + function handleUrgency(params: Record, urgency?: Urgency) { + if (urgency == Urgency.Important || urgency == Urgency.Urgent) { + Object.assign(params, { metaData: { urgency } }); + } + } + + async function handleMessage( + { msg, styles, urgency, mentions, quote, ttl }: MessageContent, + threadId: string, + type: ThreadType, + ) { + if (!msg || msg.length == 0) throw new ZaloApiError("Missing message content"); + const isGroupMessage = type == ThreadType.Group; + + const { mentionsFinal, msgFinal } = handleMentions(type, msg, mentions); + msg = msgFinal; + + if (quote) { + if (typeof quote.content != "string" && quote.msgType == "webchat") { + throw new ZaloApiError("This kind of `webchat` quote type is not available"); + } + + if (quote.msgType == "group.poll") { + throw new ZaloApiError("The `group.poll` quote type is not available"); + } + } + + const isMentionsValid = mentionsFinal.length > 0 && isGroupMessage; + const params = quote + ? { + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + message: msg, + clientId: Date.now(), + mentionInfo: isMentionsValid ? JSON.stringify(mentionsFinal) : undefined, + qmsgOwner: quote.uidFrom, + qmsgId: quote.msgId, + qmsgCliId: quote.cliMsgId, + qmsgType: getClientMessageType(quote.msgType), + qmsgTs: quote.ts, + qmsg: typeof quote.content == "string" ? quote.content : prepareQMSG(quote), + imei: isGroupMessage ? undefined : ctx.imei, + visibility: isGroupMessage ? 0 : undefined, + qmsgAttach: isGroupMessage ? JSON.stringify(prepareQMSGAttach(quote)) : undefined, + qmsgTTL: quote.ttl, + ttl: ttl ?? 0, + } + : { + message: msg, + clientId: Date.now(), + mentionInfo: isMentionsValid ? JSON.stringify(mentionsFinal) : undefined, + imei: isGroupMessage ? undefined : ctx.imei, + ttl: ttl ?? 0, + visibility: isGroupMessage ? 0 : undefined, + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + }; + + handleStyles(params, styles); + handleUrgency(params, urgency); + + removeUndefinedKeys(params); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + const finalServiceUrl = new URL(serviceURLs.message[type]); + if (quote) { + finalServiceUrl.pathname = finalServiceUrl.pathname + "/quote"; + } else { + finalServiceUrl.pathname = + finalServiceUrl.pathname + + "/" + + (isGroupMessage ? (params.mentionInfo ? "mention" : "sendmsg") : "sms"); + } + + return { + url: finalServiceUrl.toString(), + body: new URLSearchParams({ params: encryptedParams }), + }; + } + + async function handleAttachment( + { msg, attachments, mentions, quote, ttl, urgency }: MessageContent, + threadId: string, + type: ThreadType, + ) { + if (!attachments) throw new ZaloApiError("Missing attachments"); + if (!Array.isArray(attachments)) attachments = [attachments]; + if (attachments.length == 0) throw new ZaloApiError("Missing attachments"); + const firstSource = attachments[0]; + const isFilePath = typeof firstSource == "string"; + + const firstExtFile = getFileExtension(isFilePath ? firstSource : firstSource.filename); + const isSingleFile = attachments.length == 1; + const isGroupMessage = type == ThreadType.Group; + + const canBeDesc = isSingleFile && ["jpg", "jpeg", "png", "webp"].includes(firstExtFile); + + const gifFiles = attachments.filter((e) => getFileExtension(typeof e == "string" ? e : e.filename) == "gif"); + attachments = attachments.filter((e) => getFileExtension(typeof e == "string" ? e : e.filename) != "gif"); + + const uploadAttachment = attachments.length == 0 ? [] : await api.uploadAttachment(attachments, threadId, type); + + const attachmentsData: AttachmentData[] = []; + let indexInGroupLayout = 0; + + const groupLayoutId = getGroupLayoutId().toString(); + + const { mentionsFinal, msgFinal } = handleMentions(type, msg, mentions); + msg = msgFinal; + + const isMentionsValid = mentionsFinal.length > 0 && isGroupMessage && attachments.length == 1; + + const isMultiFile = attachments.length > 1; + let clientId = Date.now(); + for (const attachment of uploadAttachment) { + let data: AttachmentData; + switch (attachment.fileType) { + case "image": { + data = { + fileType: attachment.fileType, + params: { + photoId: attachment.photoId, + clientId: (clientId++).toString(), + desc: msg, + width: attachment.width, + height: attachment.height, + toid: isGroupMessage ? undefined : String(threadId), + grid: isGroupMessage ? String(threadId) : undefined, + rawUrl: attachment.normalUrl, + hdUrl: attachment.hdUrl, + thumbUrl: attachment.thumbUrl, + oriUrl: isGroupMessage ? attachment.normalUrl : undefined, + normalUrl: isGroupMessage ? undefined : attachment.normalUrl, + hdSize: String(attachment.totalSize), + zsource: -1, + ttl: ttl ?? 0, + jcp: '{"convertible":"jxl"}', + + groupLayoutId: isMultiFile ? groupLayoutId : undefined, + isGroupLayout: isMultiFile ? 1 : undefined, + idInGroup: isMultiFile ? indexInGroupLayout++ : undefined, + totalItemInGroup: isMultiFile ? uploadAttachment.length : undefined, + extMsgProp: isMultiFile ? `{"groupMediaMsg":{"groupLayoutId":"${groupLayoutId}"}}` : undefined, + + mentionInfo: + isMentionsValid && canBeDesc && !quote ? JSON.stringify(mentionsFinal) : undefined, + }, + body: new URLSearchParams(), + }; + break; + } + + case "video": { + data = { + fileType: attachment.fileType, + params: { + fileId: attachment.fileId, + checksum: attachment.checksum, + checksumSha: "", + extention: getFileExtension(attachment.fileName), + totalSize: attachment.totalSize, + fileName: attachment.fileName, + clientId: attachment.clientFileId, + fType: 1, + fileCount: 0, + fdata: "{}", + toid: isGroupMessage ? undefined : String(threadId), + grid: isGroupMessage ? String(threadId) : undefined, + fileUrl: attachment.fileUrl, + zsource: -1, + ttl: ttl ?? 0, + }, + body: new URLSearchParams(), + }; + break; + } + + case "others": { + data = { + fileType: attachment.fileType, + params: { + fileId: attachment.fileId, + checksum: attachment.checksum, + checksumSha: "", + extention: getFileExtension(attachment.fileName), + totalSize: attachment.totalSize, + fileName: attachment.fileName, + clientId: attachment.clientFileId, + fType: 1, + fileCount: 0, + fdata: "{}", + toid: isGroupMessage ? undefined : String(threadId), + grid: isGroupMessage ? String(threadId) : undefined, + fileUrl: attachment.fileUrl, + zsource: -1, + ttl: ttl ?? 0, + }, + body: new URLSearchParams(), + }; + break; + } + } + + handleUrgency(data.params, urgency); + + removeUndefinedKeys(data.params); + const encryptedParams = utils.encodeAES(JSON.stringify(data.params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + data.body.append("params", encryptedParams); + attachmentsData.push(data); + } + + for (const gif of gifFiles) { + const isFilePath = typeof gif == "string"; + const gifData = isFilePath ? await getGifMetaData(ctx, gif) : { ...gif.metadata, fileName: gif.filename }; + if (isExceedMaxFileSize(gifData.totalSize!)) + throw new ZaloApiError( + `File ${isFilePath ? getFileName(gif) : gif.filename} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + const _upthumb = await upthumb(gif, serviceURLs.attachment[ThreadType.User]); + + const formData = new FormData(); + formData.append("chunkContent", isFilePath ? await fs.readFile(gif) : gif.data, { + filename: isFilePath ? getFileName(gif) : gif.filename, + contentType: "application/octet-stream", + }); + + const params = { + clientId: Date.now().toString(), + fileName: gifData.fileName, + totalSize: gifData.totalSize, + width: gifData.width, + height: gifData.height, + msg: msg, + type: 1, + ttl: ttl ?? 0, + visibility: isGroupMessage ? 0 : undefined, + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + thumb: _upthumb.url, + checksum: (await getMd5LargeFileObject(gif, gifData.totalSize!)).data, + totalChunk: 1, + chunkId: 1, + }; + + handleUrgency(params, urgency); + + removeUndefinedKeys(params); + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + attachmentsData.push({ + query: { + params: encryptedParams, + type: "1", + }, + body: formData.getBuffer(), + headers: formData.getHeaders(), + fileType: "gif", + }); + } + + const responses = []; + + for (const data of attachmentsData) { + responses.push({ + url: utils.makeURL( + serviceURLs.attachment[type] + attachmentUrlType[data.fileType], + Object.assign( + { + nretry: "0", + }, + data.query || {}, + ), + ), + body: data.body, + headers: data.fileType == "gif" ? data.headers : {}, + }); + } + + return responses; + } + + /** + * Send a message to a thread + * + * @param message Message content + * @param threadId group or user id + * @param type Message type (User or Group) + * @param quote Message or GroupMessage instance (optional), used for quoting + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function sendMessage( + message: MessageContent | string, + threadId: string, + type: ThreadType = ThreadType.User, + ) { + if (!message) throw new ZaloApiError("Missing message content"); + if (!threadId) throw new ZaloApiError("Missing threadId"); + if (typeof message == "string") message = { msg: message }; + + let { msg, attachments, mentions } = message; + const { quote, ttl, styles, urgency } = message; + + if (attachments && !Array.isArray(attachments)) { + attachments = [attachments]; + } + + if (!msg && (!attachments || (attachments && attachments.length == 0))) + throw new ZaloApiError("Missing message content"); + + if (attachments && isExceedMaxFile(attachments.length)) + throw new ZaloApiError("Exceed maximum file of " + sharefile.max_file); + + const responses: { + message: SendMessageResult | null; + attachment: SendMessageResult[]; + } = { + message: null, + attachment: [], + }; + + if (attachments && attachments.length > 0) { + const firstExtFile = getFileExtension( + typeof attachments[0] == "string" ? attachments[0] : attachments[0].filename, + ); + const isSingleFile = attachments.length == 1; + + const canBeDesc = isSingleFile && ["jpg", "jpeg", "png", "webp"].includes(firstExtFile); + if ((!canBeDesc && msg.length > 0) || (msg.length > 0 && quote)) { + // send message and attachment separately + await handleMessage(message, threadId, type).then(async (data) => { + responses.message = (await send(data))[0]; + }); + msg = ""; + mentions = undefined; + } + const handledData = await handleAttachment( + { msg, mentions, attachments, quote, ttl, styles, urgency }, + threadId, + type, + ); + responses.attachment = await send(handledData); + msg = ""; + } + + if (msg.length > 0) { + const handledData = await handleMessage(message, threadId, type); + responses.message = (await send(handledData))[0]; + } + + return responses; + }; +}); diff --git a/_libs/zca-js/src/apis/sendReport.ts b/_libs/zca-js/src/apis/sendReport.ts new file mode 100644 index 0000000..9375669 --- /dev/null +++ b/_libs/zca-js/src/apis/sendReport.ts @@ -0,0 +1,72 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory, removeUndefinedKeys } from "../utils.js"; + +export enum ReportReason { + Sensitive = 1, + Annoy = 2, + Fraud = 3, + Other = 0, +} + +export type SendReportOptions = + | { + reason: ReportReason.Other; + content: string; + } + | { + reason: Exclude; + }; + +export type SendReportResponse = { + reportId: string; +}; + +export const sendReportFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/report/abuse-v2`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/reportabuse`), + }; + + /** + * Send report to Zalo + * + * @param options Report options + * @param threadId The threadID to report + * @param type Thread type, default direct + * + * @throws {ZaloApiError} + * + */ + return async function sendReport(options: SendReportOptions, threadId: string, type: ThreadType = ThreadType.User) { + const params = + type == ThreadType.User + ? { + idTo: threadId, + objId: "person.profile", + reason: options.reason.toString(), + content: options.reason == ReportReason.Other ? options.content : undefined, + } + : { + uidTo: threadId, + type: 14, + reason: options.reason, + content: options.reason == ReportReason.Other ? options.content : "", + imei: ctx.imei, + }; + + removeUndefinedKeys(params); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendSeenEvent.ts b/_libs/zca-js/src/apis/sendSeenEvent.ts new file mode 100644 index 0000000..b9724ca --- /dev/null +++ b/_libs/zca-js/src/apis/sendSeenEvent.ts @@ -0,0 +1,108 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { MAX_MESSAGES_PER_SEND } from "../context.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendSeenEventResponse = { + status: number; +}; + +export type SendSeenEventMessageParams = { + msgId: string; + cliMsgId: string; + uidFrom: string; + idTo: string; + msgType: string; + st: number; + at: number; + cmd: number; + ts: string | number; +}; + +type RequestMessageParams = { + gmi: string; + cmi: string; + si: string; + di: string; + mt: string; + st: number; + at: number; + cmd: number; + ts: number; +}; + +type MsgInfos = { + data: RequestMessageParams[]; + grid?: string; + senderId?: string; +}; + +export const sendSeenEventFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/seenv2`, { + nretry: 0, + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/seenv2`, { + nretry: 0, + }), + }; + + /** + * Send message seen event + * + * @param messages List of messages to send seen event + * @param type Messages type (User or Group), defaults to User + * + * @throws {ZaloApiError} + */ + return async function sendSeenEvent( + messages: SendSeenEventMessageParams | SendSeenEventMessageParams[], + type: ThreadType = ThreadType.User, + ) { + if (!messages) throw new ZaloApiError("messages are missing or not in a valid array format."); + if (!Array.isArray(messages)) messages = [messages]; + if (messages.length === 0 || messages.length > MAX_MESSAGES_PER_SEND) + throw new ZaloApiError("messages must contain between 1 and 50 messages."); + + const isGroup = type === ThreadType.Group; + const threadId = isGroup ? messages[0].idTo : messages[0].uidFrom; + + const msgInfos: MsgInfos = { + data: messages.map((msg) => { + const curThreadId = isGroup ? msg.idTo : msg.uidFrom; + if (curThreadId !== threadId) { + throw new ZaloApiError("All messages must belong to the same thread."); + } + return { + cmi: msg.cliMsgId, + gmi: msg.msgId, + si: msg.uidFrom, + di: msg.idTo === ctx.uid ? "0" : msg.idTo, + mt: msg.msgType, + st: msg.st || 0 === msg.st ? 0 : -1, + at: msg.at || 0 === msg.at ? 0 : -1, + cmd: msg.cmd || 0 === msg.cmd ? 0 : -1, + ts: parseInt(`${msg.ts}`) || 0 === parseInt(`${msg.ts}`) ? 0 : -1, + }; + }), + [isGroup ? "grid" : "senderId"]: threadId, + }; + + const params = { + msgInfos: JSON.stringify(msgInfos), + ...(isGroup ? { imei: ctx.imei } : {}), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendSticker.ts b/_libs/zca-js/src/apis/sendSticker.ts new file mode 100644 index 0000000..6b1a330 --- /dev/null +++ b/_libs/zca-js/src/apis/sendSticker.ts @@ -0,0 +1,71 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory, removeUndefinedKeys } from "../utils.js"; + +export type SendStickerPayload = { + id: number; + cateId: number; + type: number; +} + +export type SendStickerResponse = { + msgId: number; +}; + +export const sendStickerFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/sticker`, { + nretry: "0", + }), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/sticker`, { + nretry: "0", + }), + }; + + /** + * Send a sticker to a thread + * + * @param sticker Sticker object + * @param threadId group or user id + * @param type Message type (User or GroupMessage) + * + * @throws {ZaloApiError} + */ + return async function sendSticker(sticker: SendStickerPayload, threadId: string, type: ThreadType = ThreadType.User) { + if (!sticker) throw new ZaloApiError("Missing sticker"); + if (!threadId) throw new ZaloApiError("Missing threadId"); + + if (!sticker.id) throw new ZaloApiError("Missing sticker id"); + + // Sometime sticker.cateId = 0, which is invalid. + if (sticker.cateId === undefined || sticker.cateId === null) throw new ZaloApiError("Missing sticker cateId"); + if (!sticker.type) throw new ZaloApiError("Missing sticker type"); + + const isGroupMessage = type === ThreadType.Group; + + const params = { + stickerId: sticker.id, + cateId: sticker.cateId, + type: sticker.type, + clientId: Date.now(), + imei: ctx.imei, + zsource: 101, + toid: isGroupMessage ? undefined : threadId, + grid: isGroupMessage ? threadId : undefined, + }; + + removeUndefinedKeys(params); + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendTypingEvent.ts b/_libs/zca-js/src/apis/sendTypingEvent.ts new file mode 100644 index 0000000..21b06b8 --- /dev/null +++ b/_libs/zca-js/src/apis/sendTypingEvent.ts @@ -0,0 +1,49 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { DestType, ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendTypingEventResponse = { + status: number; +}; + +export const sendTypingEventFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/typing`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/typing`), + }; + + /** + * Send typing event + * + * @param threadId The ID of the User or Group to send the typing event to + * @param type The type of thread (User or Group) + * @param destType The destination type (User or Page), for User threads only, defaults to User + * + * @throws {ZaloApiError} + */ + return async function sendTypingEvent( + threadId: string, + type: ThreadType = ThreadType.User, + destType: DestType = DestType.User, + ) { + if (!threadId) throw new ZaloApiError("Missing threadId"); + + const params = { + [type === ThreadType.User ? "toid" : "grid"]: threadId, + ...(type === ThreadType.User ? { destType } : {}), + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendVideo.ts b/_libs/zca-js/src/apis/sendVideo.ts new file mode 100644 index 0000000..3dd335f --- /dev/null +++ b/_libs/zca-js/src/apis/sendVideo.ts @@ -0,0 +1,161 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendVideoOptions = { + /** + * Optional message to send along with the video + */ + msg?: string; + /** + * URL of the video + */ + videoUrl: string; + /** + * URL of the thumbnail + */ + thumbnailUrl: string; + /** + * Video duration in milliseconds || Eg: video duration: 5.5s => 5.5 * 1000 = 5500 + */ + duration?: number; + /** + * Width of the video + */ + width?: number; + /** + * Height of the video + */ + height?: number; + /** + * Time to live in milliseconds (default: 0) + */ + ttl?: number; +}; + +export type SendVideoResponse = { + msgId: number; +}; + +export const sendVideoFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/forward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/forward`), + }; + + /** + * Send a video to a User - Group + * + * @param options send video options + * @param threadId ID of the user or group to send the video to + * @param type Type of thread (USER or GROUP) + * + * @throws {ZaloApiError} + * + * @examples Example Video Resolutions: + * - **Standard Videos**: + * - 3840x2160 (4K UHD): Width 3840px, Height 2160px. + * - 1920x1080 (Full HD): Width 1920px, Height 1080px. + * - 1280x720 (HD): Width 1280px, Height 720px. + * - **Document-Oriented Videos** (Portrait): + * - 3840x2160 (4K UHD): Width 3840px, Height 2160px. + * - 720x1280 (HD): Width 720px, Height 1280px. + * - 1440x2560 (2K): Width 1440px, Height 2560px. + * + */ + return async function sendVideo(options: SendVideoOptions, threadId: string, type: ThreadType = ThreadType.User) { + let fileSize: number = 0; + const clientId = Date.now(); + + try { + const headResponse = await utils.request(options.videoUrl, { method: "HEAD" }, true); + if (headResponse.ok) { + fileSize = parseInt(headResponse.headers.get("content-length") || "0"); + } + } catch (error: unknown) { + throw new ZaloApiError( + `Unable to get video content: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + const params = + type === ThreadType.User + ? { + toId: threadId, + clientId: String(clientId), + ttl: options.ttl ?? 0, + zsource: 704, + msgType: 5, + msgInfo: JSON.stringify({ + videoUrl: options.videoUrl, + thumbUrl: options.thumbnailUrl, + duration: options.duration ?? 0, + width: options.width ?? 1280, + height: options.height ?? 720, + fileSize: fileSize, + properties: { + color: -1, + size: -1, + type: 1003, + subType: 0, + ext: { + sSrcType: -1, + sSrcStr: "", + msg_warning_type: 0, + }, + }, + title: options.msg ?? "", + }), + imei: ctx.imei, + } + : { + grid: threadId, + visibility: 0, + clientId: String(clientId), + ttl: options.ttl ?? 0, + zsource: 704, + msgType: 5, + msgInfo: JSON.stringify({ + videoUrl: options.videoUrl, + thumbUrl: options.thumbnailUrl, + duration: options.duration ?? 0, + width: options.width ?? 1280, + height: options.height ?? 720, + fileSize: fileSize, + properties: { + color: -1, + size: -1, + type: 1003, + subType: 0, + ext: { + sSrcType: -1, + sSrcStr: "", + msg_warning_type: 0, + }, + }, + title: options.msg ?? "", + }), + imei: ctx.imei, + }; + + // @TODO: later + // if (typeof message !== "string" && message.mention) { + // params.mentionInfo = message.mention; + // } + if (type !== ThreadType.User && type !== ThreadType.Group) { + throw new ZaloApiError("Thread type is invalid"); + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sendVoice.ts b/_libs/zca-js/src/apis/sendVoice.ts new file mode 100644 index 0000000..823338b --- /dev/null +++ b/_libs/zca-js/src/apis/sendVoice.ts @@ -0,0 +1,93 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SendVoiceOptions = { + voiceUrl: string; + /** + * Time to live in milliseconds (default: 0) + */ + ttl?: number; +}; + +export type SendVoiceResponse = { + msgId: string; +}; + +export const sendVoiceFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/forward`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.file[0]}/api/group/forward`), + }; + + /** + * Send a voice to a User - Group + * + * @param options voice options + * @param threadId ID of the user or group to send the voice to + * @param type Type of thread, default user + * + * @throws {ZaloApiError} + */ + return async function sendVoice(options: SendVoiceOptions, threadId: string, type: ThreadType = ThreadType.User) { + let fileSize = null; + const clientId = Date.now().toString(); + + try { + const headResponse = await utils.request(options.voiceUrl, { method: "HEAD" }, true); + if (headResponse.ok) { + fileSize = parseInt(headResponse.headers.get("content-length") || "0"); + } + } catch (error: unknown) { + throw new ZaloApiError( + `Unable to get voice content: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + const params = + type === ThreadType.User + ? { + toId: threadId, + ttl: options.ttl ?? 0, + zsource: -1, + msgType: 3, + clientId: clientId, + msgInfo: JSON.stringify({ + voiceUrl: options.voiceUrl, + m4aUrl: options.voiceUrl, + fileSize: fileSize ?? 0, + }), + imei: ctx.imei, + } + : { + grid: threadId, + visibility: 0, + ttl: options.ttl ?? 0, + zsource: -1, + msgType: 3, + clientId: clientId, + msgInfo: JSON.stringify({ + voiceUrl: options.voiceUrl, + m4aUrl: options.voiceUrl, + fileSize: fileSize ?? 0, + }), + imei: ctx.imei, + }; + + if (type !== ThreadType.User && type !== ThreadType.Group) { + throw new ZaloApiError("Thread type is invalid"); + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/setHiddenConversations.ts b/_libs/zca-js/src/apis/setHiddenConversations.ts new file mode 100644 index 0000000..b6ec740 --- /dev/null +++ b/_libs/zca-js/src/apis/setHiddenConversations.ts @@ -0,0 +1,51 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SetHiddenConversationsResponse = ""; + +export const setHiddenConversationsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/add-remove`); + + /** + * Set hidden conversations + * + * @param hidden - Hide or unhide conversations + * @param threadId Thread ID(s) + * @param type Thread type (User/Group) + * + * @throws {ZaloApiError} + */ + return async function setHiddenConversations( + hidden: boolean, + threadId: string | string[], + type: ThreadType = ThreadType.User, + ) { + threadId = Array.isArray(threadId) ? threadId : [threadId]; + if (threadId.length === 0) throw new ZaloApiError("threadId is required"); + + const is_group = type === ThreadType.Group ? 1 : 0; + const params = { + [hidden ? "add_threads" : "del_threads"]: JSON.stringify( + threadId.map((id) => ({ + thread_id: id, + is_group: is_group, + })), + ), + [hidden ? "del_threads" : "add_threads"]: "[]", + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/setMute.ts b/_libs/zca-js/src/apis/setMute.ts new file mode 100644 index 0000000..e1ae073 --- /dev/null +++ b/_libs/zca-js/src/apis/setMute.ts @@ -0,0 +1,82 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SetMuteParams = { + /** + * Mute duration in seconds or predefined durations + */ + duration?: MuteDuration | number; + action?: MuteAction; +}; + +export type SetMuteResponse = ""; + +export enum MuteDuration { + ONE_HOUR = 3600, + FOUR_HOURS = 14400, + FOREVER = -1, + UNTIL_8AM = "until8AM", +} + +export enum MuteAction { + MUTE = 1, + UNMUTE = 3, // khi tat thi duration = -1 +} + +export const setMuteFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/setmute`); + + /** + * Set mute + * + * @param params - Mute parameters + * @param threadID - ID of the thread to mute + * @param type - Type of thread (User or Group) + * + * @throws {ZaloApiError} + */ + return async function setMute(params: SetMuteParams = {}, threadID: string, type: ThreadType = ThreadType.User) { + const { duration = MuteDuration.FOREVER, action = MuteAction.MUTE } = params; + let muteDuration: number; + + if (action === MuteAction.UNMUTE) { + muteDuration = -1; + } else if (duration === MuteDuration.FOREVER) { + muteDuration = -1; + } else if (duration === MuteDuration.UNTIL_8AM) { + const now = new Date(); + const next8AM = new Date(now); + next8AM.setHours(8, 0, 0, 0); + + if (now.getHours() >= 8) { + next8AM.setDate(next8AM.getDate() + 1); + } + + muteDuration = Math.floor((next8AM.getTime() - now.getTime()) / 1000); + } else { + muteDuration = duration; + } + + const requestParams = { + toid: threadID, + duration: muteDuration, + action: action, + startTime: Math.floor(Date.now() / 1000), + muteType: type === ThreadType.User ? 1 : 2, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(requestParams)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/setPinnedConversations.ts b/_libs/zca-js/src/apis/setPinnedConversations.ts new file mode 100644 index 0000000..587e359 --- /dev/null +++ b/_libs/zca-js/src/apis/setPinnedConversations.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type SetPinnedConversationsResponse = ""; + +export const setPinnedConversationsFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/pinconvers/updatev2`); + + /** + * Pin and unpin conversations of threads) + * + * @param pinned Should pin conversations + * @param threadId The ID(s) of the thread + * @param type Type of thread, default User + * + * @throws {ZaloApiError} + * + */ + return async function setPinnedConversations( + pinned: boolean, + threadId: string | string[], + type: ThreadType = ThreadType.User, + ) { + if (typeof threadId == "string") threadId = [threadId]; + + const params = { + actionType: pinned ? 1 : 2, + conversations: type == ThreadType.Group ? threadId.map((id) => `g${id}`) : threadId.map((id) => `u${id}`), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/sharePoll.ts b/_libs/zca-js/src/apis/sharePoll.ts new file mode 100644 index 0000000..a689c96 --- /dev/null +++ b/_libs/zca-js/src/apis/sharePoll.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type SharePollResponse = ""; + +export const sharePollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/share`); + + /** + * Share a poll + * + * @param pollId poll id to share + * + * @throws {ZaloApiError} + */ + return async function sharePoll(pollId: number) { + const params = { + poll_id: pollId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/unblockUser.ts b/_libs/zca-js/src/apis/unblockUser.ts new file mode 100644 index 0000000..002f6a8 --- /dev/null +++ b/_libs/zca-js/src/apis/unblockUser.ts @@ -0,0 +1,34 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UnBlockUserResponse = ""; + +export const unblockUserFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/unblock`); + + /** + * Unblock a User + * + * @param userId The ID of the User to unblock + * + * @throws {ZaloApiError} + */ + return async function unblockUser(userId: string) { + const params = { + fid: userId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/undo.ts b/_libs/zca-js/src/apis/undo.ts new file mode 100644 index 0000000..7873e0d --- /dev/null +++ b/_libs/zca-js/src/apis/undo.ts @@ -0,0 +1,53 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type UndoPayload = { + msgId: string | number; + cliMsgId: string | number; +}; + +export type UndoResponse = { + status: number; +}; + +export const undoFactory = apiFactory()((api, ctx, utils) => { + const URLType = { + [ThreadType.User]: utils.makeURL(`${api.zpwServiceMap.chat[0]}/api/message/undo`), + [ThreadType.Group]: utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/undomsg`), + }; + /** + * Undo a message + * + * @param payload Undo payload containing message ID and client message ID + * @param threadId group or user id + * @param type Message type (User or Group), default is User + * + * @throws {ZaloApiError} + */ + return async function undo(payload: UndoPayload, threadId: string, type: ThreadType = ThreadType.User) { + const params: Record = { + msgId: payload.msgId, + clientId: Date.now(), + cliMsgIdUndo: payload.cliMsgId, + }; + + if (type == ThreadType.Group) { + params["grid"] = threadId; + params["visibility"] = 0; + params["imei"] = ctx.imei; + } else params["toid"] = threadId; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(URLType[type], { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/undoFriendRequest.ts b/_libs/zca-js/src/apis/undoFriendRequest.ts new file mode 100644 index 0000000..6fec1db --- /dev/null +++ b/_libs/zca-js/src/apis/undoFriendRequest.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UndoFriendRequestResponse = ""; + +export const undoFriendRequestFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/undo`); + + /** + * Undo send a friend request to a user. + * + * @param options + * + * @throws {ZaloApiError} + */ + return async function undoFriendRequest(friendId: string) { + const params = { + fid: friendId, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateActiveStatus.ts b/_libs/zca-js/src/apis/updateActiveStatus.ts new file mode 100644 index 0000000..3e4020d --- /dev/null +++ b/_libs/zca-js/src/apis/updateActiveStatus.ts @@ -0,0 +1,35 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateActiveStatusResponse = { + status: boolean; +}; + +export const updateActiveStatusFactory = apiFactory()((api, ctx, utils) => { + const pingURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/ping`); + const deactiveURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/deactive`); + + /** + * Update active status? + * + * @param active + * + * @throws {ZaloApiError} + */ + return async function updateActiveStatus(active: boolean) { + const params = { + status: active ? 1 : 0, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const targetURL = active ? pingURL : deactiveURL; + const response = await utils.request(utils.makeURL(targetURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateArchivedChatList.ts b/_libs/zca-js/src/apis/updateArchivedChatList.ts new file mode 100644 index 0000000..7748248 --- /dev/null +++ b/_libs/zca-js/src/apis/updateArchivedChatList.ts @@ -0,0 +1,44 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { ThreadType } from "../models/Enum.js"; + +export type UpdateArchivedChatListTarget = { id: string; type: ThreadType }; + +export type UpdateArchivedChatListResponse = { needResync: boolean; version: number }; + +export const updateArchivedChatListFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/archivedchat/update`); + + /** + * Archive conversations + * + * @param isArchived - true to archive, false to unarchive + * @param conversations - List of conversations to archive/unarchive + * + * @throws {ZaloApiError} + */ + return async function updateArchivedChatList(isArchived: boolean, conversations: UpdateArchivedChatListTarget | UpdateArchivedChatListTarget[]) { + if (!Array.isArray(conversations)) { + conversations = [conversations]; + } + const params = { + actionType: isArchived ? 0 : 1, + ids: conversations, + imei: ctx.imei, + version: Date.now(), + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateAutoDeleteChat.ts b/_libs/zca-js/src/apis/updateAutoDeleteChat.ts new file mode 100644 index 0000000..d4cd536 --- /dev/null +++ b/_libs/zca-js/src/apis/updateAutoDeleteChat.ts @@ -0,0 +1,46 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export enum ChatTTL { + NO_DELETE = 0, + ONE_DAY = 86400000, + SEVEN_DAYS = 604800000, + FOURTEEN_DAYS = 1209600000, +} + +export type UpdateAutoDeleteChatResponse = ""; + +export const updateAutoDeleteChatFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/conv/autodelete/updateConvers`); + + /** + * Auto delete chat + * + * @param ttl The time to live of the chat + * @param threadId The thread ID to auto delete chat + * @param type Type of thread (User or Group) + * + * @throws {ZaloApiError} + */ + return async function updateAutoDeleteChat(ttl: ChatTTL, threadId: string, type: ThreadType = ThreadType.User) { + const params = { + threadId: threadId, + isGroup: type === ThreadType.Group ? 1 : 0, + ttl: ttl, + clientLang: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateAutoReply.ts b/_libs/zca-js/src/apis/updateAutoReply.ts new file mode 100644 index 0000000..a64ada1 --- /dev/null +++ b/_libs/zca-js/src/apis/updateAutoReply.ts @@ -0,0 +1,60 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AutoReplyItem, AutoReplyScope } from "../models/index.js"; + +export type UpdateAutoReplyPayload = { + id: number; + content: string; + isEnable: boolean; + startTime: number; + endTime: number; + scope: AutoReplyScope; + uids?: string | string[]; +}; + +export type UpdateAutoReplyResponse = { + item: AutoReplyItem; + version: number; +}; + +export const updateAutoReplyFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.auto_reply[0]}/api/autoreply/update`); + + /** + * Update auto reply + * + * @param payload payload + * + * @note this API used for zBusiness + * @throws {ZaloApiError} + */ + return async function updateAutoReply(payload: UpdateAutoReplyPayload) { + const uids = Array.isArray(payload.uids) ? payload.uids : [payload.uids]; + const resultUids = payload.scope === 2 || payload.scope === 3 ? uids : []; + + const params = { + cliLang: ctx.language, + id: payload.id, + enable: payload.isEnable, + content: payload.content, + startTime: payload.startTime, + endTime: payload.endTime, + recurrence: ["RRULE:FREQ=DAILY;"], + scope: payload.scope, + uids: resultUids, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateCatalog.ts b/_libs/zca-js/src/apis/updateCatalog.ts new file mode 100644 index 0000000..45b4c2a --- /dev/null +++ b/_libs/zca-js/src/apis/updateCatalog.ts @@ -0,0 +1,47 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { CatalogItem } from "../models/index.js"; + +export type UpdateCatalogPayload = { + catalogId: string; + catalogName: string; +}; + +export type UpdateCatalogResponse = { + item: CatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const updateCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/catalog/update`); + + /** + * Update catalog? + * + * @param payload payload + * + * @note this API is used for zBusiness + * @throws {ZaloApiError} + */ + return async function updateCatalog(payload: UpdateCatalogPayload) { + const params = { + catalog_id: payload.catalogId, + catalog_name: payload.catalogName, + catalog_photo: "", // "" is defaut don't upload photo + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateGroupSettings.ts b/_libs/zca-js/src/apis/updateGroupSettings.ts new file mode 100644 index 0000000..f3c94d2 --- /dev/null +++ b/_libs/zca-js/src/apis/updateGroupSettings.ts @@ -0,0 +1,97 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateGroupSettingsOptions = { + /** + * Disallow group members to change the group name and avatar + */ + blockName?: boolean; + /** + * Highlight messages from owner/admins + */ + signAdminMsg?: boolean; + /** + * Don't pin messages, notes, and polls to the top of a conversation + */ + setTopicOnly?: boolean; + /** + * Allow new members to read most recent messages + */ + enableMsgHistory?: boolean; + /** + * Membership approval + */ + joinAppr?: boolean; + /** + * Disallow group members to create notes & reminders + */ + lockCreatePost?: boolean; + /** + * Disallow group members to create polls + */ + lockCreatePoll?: boolean; + /** + * Disallow group members to send messages + */ + lockSendMsg?: boolean; + /** + * Disallow group members to view full member list (community only) + */ + lockViewMember?: boolean; + + // bannFeature?: boolean; // not see in UI, not implemented + // addMemberOnly?: boolean; // not see in UI, not implemented + // dirtyMedia?: boolean; // not see in UI, not implemented + // banDuration?: boolean | number; // not see in UI, not implemented +}; + +export type UpdateGroupSettingsResponse = ""; + +export const updateGroupSettingsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/setting/update`); + + /** + * Update group settings + * + * @param options Options + * @param groupId Group ID + * + * @note Zalo might throw an error with code 166 if you don't have enough permissions to change the settings. + * + * @throws {ZaloApiError} + */ + return async function updateGroupSettings(options: UpdateGroupSettingsOptions, groupId: string) { + const params = { + blockName: options.blockName ? 1 : 0, + signAdminMsg: options.signAdminMsg ? 1 : 0, + + // addMemberOnly: 0, // very tricky, any idea? + + setTopicOnly: options.setTopicOnly ? 1 : 0, + enableMsgHistory: options.enableMsgHistory ? 1 : 0, + joinAppr: options.joinAppr ? 1 : 0, + lockCreatePost: options.lockCreatePost ? 1 : 0, + lockCreatePoll: options.lockCreatePoll ? 1 : 0, + lockSendMsg: options.lockSendMsg ? 1 : 0, + lockViewMember: options.lockViewMember ? 1 : 0, + + // default values for not implemented options + bannFeature: 0, + dirtyMedia: 0, + banDuration: 0, + blocked_members: [], + + grid: groupId, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateHiddenConversPin.ts b/_libs/zca-js/src/apis/updateHiddenConversPin.ts new file mode 100644 index 0000000..e5de815 --- /dev/null +++ b/_libs/zca-js/src/apis/updateHiddenConversPin.ts @@ -0,0 +1,37 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory, encryptPin } from "../utils.js"; + +export type UpdateHiddenConversPinResponse = ""; + +export const updateHiddenConversPinFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/hiddenconvers/update-pin`); + const pinRegex = /^\d{4}$/; + + /** + * Update hidden conversation pin + * + * @param pin The pin to update (must be a 4-digit number between 0000-9999) + * + * @throws {ZaloApiError} + */ + return async function updateHiddenConversPin(pin: string) { + if (!pinRegex.test(pin)) { + throw new ZaloApiError("Pin must be a 4-digit number between 0000-9999"); + } + + const encryptedPin = encryptPin(pin); + const params = { + new_pin: encryptedPin, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateLabels.ts b/_libs/zca-js/src/apis/updateLabels.ts new file mode 100644 index 0000000..cdb8c1a --- /dev/null +++ b/_libs/zca-js/src/apis/updateLabels.ts @@ -0,0 +1,54 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { LabelData } from "../models/index.js"; + +export type UpdateLabelsPayload = { + labelData: LabelData[]; + version: number; +}; + +export type UpdateLabelsResponse = { + labelData: LabelData[]; + version: number; + lastUpdateTime: number; +}; + +export const updateLabelsFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.label[0]}/api/convlabel/update`); + + /** + * Update labels + * + * @param label label data + * + * @throws {ZaloApiError} + */ + return async function updateLabels(payload: UpdateLabelsPayload) { + const params = { + labelData: JSON.stringify(payload.labelData), + version: payload.version, + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + const unFormatted = (await utils.resolve(response)) as unknown as Omit & { + labelData: string; + }; + + return { + labelData: JSON.parse(unFormatted.labelData), + version: unFormatted.version, + lastUpdateTime: unFormatted.lastUpdateTime, + }; + }; +}); diff --git a/_libs/zca-js/src/apis/updateLang.ts b/_libs/zca-js/src/apis/updateLang.ts new file mode 100644 index 0000000..4d9f246 --- /dev/null +++ b/_libs/zca-js/src/apis/updateLang.ts @@ -0,0 +1,36 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export enum UpdateLangAvailableLanguages { + VI = "VI", + EN = "EN", +} + +export type UpdateLangResponse = ""; + +export const updateLangFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/updatelang`); + + /** + * Update language? + * + * @param language language to update (VI, EN) + * + * @note Calling this API alone will not update the user's language. + * @throws {ZaloApiError} + */ + return async function updateLang(language: UpdateLangAvailableLanguages = UpdateLangAvailableLanguages.VI) { + const params = { + language: language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateProductCatalog.ts b/_libs/zca-js/src/apis/updateProductCatalog.ts new file mode 100644 index 0000000..40b6f07 --- /dev/null +++ b/_libs/zca-js/src/apis/updateProductCatalog.ts @@ -0,0 +1,89 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +import type { AttachmentSource, ProductCatalogItem } from "../models/index.js"; + +export type UpdateProductCatalogPayload = { + catalogId: string; + + productId: string; + productName: string; + price: string; + description: string; + createTime: number; + + /** + * Upto 5 media files are allowed, will be ignored if product_photos is provided + */ + files?: AttachmentSource[]; + /** + * List of product photo URLs, upto 5 + * + * You can manually get the URL using `uploadProductPhoto` api + */ + product_photos?: string[]; +}; + +export type UpdateProductCatalogResponse = { + item: ProductCatalogItem; + version_ls_catalog: number; + version_catalog: number; +}; + +export const updateProductCatalogFactory = apiFactory()((api, _, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.catalog[0]}/api/prodcatalog/product/update`); + + /** + * Update product catalog? + * + * @param payload payload + * + * @note this API is used for zBusiness - Maximum 5 media files are supported + * @throws {ZaloApiError} + */ + return async function updateProductCatalog(payload: UpdateProductCatalogPayload) { + const productPhoto = payload.product_photos || []; + + if (payload.files && payload.files.length == 0) { + if (payload.files.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + for (const mediaFile of payload.files) { + const uploadMedia = await api.uploadProductPhoto({ + file: mediaFile, + }); + + const url = uploadMedia.normalUrl || uploadMedia.hdUrl; + productPhoto.push(url); + } + } + + if (productPhoto.length > 5) { + throw new ZaloApiError("Maximum 5 media files are allowed"); + } + + const params = { + product_id: payload.productId, + product_name: payload.productName, + price: payload.price, + description: payload.description, + product_photos: productPhoto, + catalog_id: payload.catalogId, + currency_unit: "โ‚ซ", + create_time: payload.createTime, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateProfile.ts b/_libs/zca-js/src/apis/updateProfile.ts new file mode 100644 index 0000000..05720f9 --- /dev/null +++ b/_libs/zca-js/src/apis/updateProfile.ts @@ -0,0 +1,67 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { BusinessCategory, Gender } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateProfilePayload = { + profile: { + name: string; + /** + * Date of birth in the format YYYY-MM-DD + */ + dob: `${string}-${string}-${string}`; + gender: Gender; + }; + biz?: Partial<{ + cate: BusinessCategory; + description: string; + address: string; + website: string; + email: string; + }>; +}; + +export type UpdateProfileResponse = ""; + +export const updateProfileFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/update`); + + /** + * Change account setting information + * + * @param payload payload + * + * @note If your account is a Business Account, include the biz.cate field; otherwise the category will be removed. + * You may leave the other biz fields empty if you donโ€™t want to change them. + * + * @throws {ZaloApiError} + */ + return async function updateProfile(payload: UpdateProfilePayload) { + const params = { + profile: JSON.stringify({ + name: payload.profile.name, + dob: payload.profile.dob, + gender: payload.profile.gender, + }), + biz: JSON.stringify({ + desc: payload.biz?.description, + cate: payload.biz?.cate, + addr: payload.biz?.address, + website: payload.biz?.website, + email: payload.biz?.email, + }), + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(serviceURL, { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateProfileBio.ts b/_libs/zca-js/src/apis/updateProfileBio.ts new file mode 100644 index 0000000..22d9533 --- /dev/null +++ b/_libs/zca-js/src/apis/updateProfileBio.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateProfileBioResponse = ""; + +export const updateProfileBioFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/profile/status`); + + /** + * Update profile bio + * + * @param status status for update bio + * + * @throws {ZaloApiError} + */ + return async function updateProfileBio(status: string) { + const params = { + status: status, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "POST", + body: new URLSearchParams({ + params: encryptedParams, + }), + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateQuickMessage.ts b/_libs/zca-js/src/apis/updateQuickMessage.ts new file mode 100644 index 0000000..98ffedc --- /dev/null +++ b/_libs/zca-js/src/apis/updateQuickMessage.ts @@ -0,0 +1,80 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { QuickMessage, AttachmentSource } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateQuickMessagePayload = { + keyword: string; + title: string; + media?: AttachmentSource; +}; + +export type UpdateQuickMessageResponse = { + item: QuickMessage; + version: number; +}; + +export const updateQuickMessageFactory = apiFactory()((api, _ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.quick_message[0]}/api/quickmessage/update`); + + /** + * Update quick message + * + * @param updatePayload - The payload containing data to update the quick message + * @param itemId - The id of the quick message to update + * + * @note Zalo might throw an error with code 212 if the itemId does not exist. + * + * @throws {ZaloApiError} + */ + return async function updateQuickMessage(updatePayload: UpdateQuickMessagePayload, itemId: number) { + const isType = !updatePayload.media ? 0 : 1; + + const params: Record = { + itemId: itemId, + keyword: updatePayload.keyword, + message: { + title: updatePayload.title, + params: "", + }, + type: isType, + }; + + if (isType === 1) { + if (!updatePayload.media) throw new ZaloApiError("Media is required"); + const uploadMedia = await api.uploadProductPhoto({ + file: updatePayload.media, + }); + + const photoId = uploadMedia.photoId; + const thumbUrl = uploadMedia.thumbUrl; + const normalUrl = uploadMedia.normalUrl; + const hdUrl = uploadMedia.hdUrl; + + params.media = { + items: [ + { + type: 0, + photoId: photoId, + title: "", + width: "", + height: "", + previewThumb: thumbUrl, + rawUrl: normalUrl || hdUrl, + thumbUrl: thumbUrl, + normalUrl: normalUrl || hdUrl, + hdUrl: hdUrl || normalUrl, + }, + ], + }; + } + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/updateSettings.ts b/_libs/zca-js/src/apis/updateSettings.ts new file mode 100644 index 0000000..06c263a --- /dev/null +++ b/_libs/zca-js/src/apis/updateSettings.ts @@ -0,0 +1,96 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpdateSettingsResponse = ""; + +export enum UpdateSettingsType { + ViewBirthday = "view_birthday", + ShowOnlineStatus = "show_online_status", + DisplaySeenStatus = "display_seen_status", + ReceiveMessage = "receive_message", + AcceptCall = "accept_stranger_call", + AddFriendViaPhone = "add_friend_via_phone", + AddFriendViaQR = "add_friend_via_qr", + AddFriendViaGroup = "add_friend_via_group", + AddFriendViaContact = "add_friend_via_contact", + DisplayOnRecommendFriend = "display_on_recommend_friend", + ArchivedChat = "archivedChatStatus", + QuickMessage = "quickMessageStatus", +} + +export const updateSettingsFactory = apiFactory()((_api, _ctx, utils) => { + const serviceURL = utils.makeURL(`https://wpa.chat.zalo.me/api/setting/update`); + + /** + * Set account settings + * + * @param type The type of setting to update + * @param value + * + * ViewBirthday + * * 0: hide + * * 1: show full day/month/year + * * 2: show day/month + * + * ShowOnlineStatus + * * 0: hide + * * 1: show + * + * DisplaySeenStatus + * * 0: hide + * * 1: show + * + * ReceiveMessage + * * 1: everyone + * * 2: only friends + * + * AcceptCall + * * 2: only friends + * * 3: everyone + * * 4: friends and person who contacted + * + * AddFriendViaPhone + * * 0: disable + * * 1: enable + * + * AddFriendViaQR + * * 0: disable + * * 1: enable + * + * AddFriendViaGroup + * * 0: disable + * * 1: enable + * + * AddFriendViaContact + * * 0: disable + * * 1: enable + * + * DisplayOnRecommendFriend + * * 0: disable + * * 1: enable + * + * ArchivedChat + * * 0: disable + * * 1: enable + * + * QuickMessage + * * 0: disable + * * 1: enable + * + * @throws {ZaloApiError} + */ + return async function updateSettings(type: UpdateSettingsType, value: number) { + const params = { + [type]: value, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/upgradeGroupToCommunity.ts b/_libs/zca-js/src/apis/upgradeGroupToCommunity.ts new file mode 100644 index 0000000..9ab57d1 --- /dev/null +++ b/_libs/zca-js/src/apis/upgradeGroupToCommunity.ts @@ -0,0 +1,33 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { apiFactory } from "../utils.js"; + +export type UpgradeGroupToCommunityResponse = ""; + +export const upgradeGroupToCommunityFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/upgrade/community`); + + /** + * Upgrade group to community + * + * @param groupId Group ID upgrade + * + * @note Zalo account identity must be verified and the account must be over 18 to use + * @code 185 The limit on the number of communities that can own has been reached + * @throws {ZaloApiError} + */ + return async function upgradeGroupToCommunity(groupId: string) { + const params = { + grId: groupId, + language: ctx.language, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/uploadAttachment.ts b/_libs/zca-js/src/apis/uploadAttachment.ts new file mode 100644 index 0000000..9cffb9a --- /dev/null +++ b/_libs/zca-js/src/apis/uploadAttachment.ts @@ -0,0 +1,366 @@ +import FormData from "form-data"; +import fs from "node:fs"; +import type { UploadCallback } from "../context.js"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import { ThreadType, type AttachmentSource } from "../models/index.js"; +import { + apiFactory, + getFileExtension, + getFileName, + getFileSize, + getImageMetaData, + getMd5LargeFileObject, + resolveResponse, +} from "../utils.js"; + +export type UploadAttachmentImageResponse = { + normalUrl: string; + photoId: string; + finished: number | boolean; + hdUrl: string; + thumbUrl: string; + clientFileId: number; + chunkId: number; + + fileType: "image"; + width: number; + height: number; + totalSize: number; + hdSize: number; +}; + +export type UploadAttachmentVideoResponse = { + finished: number | boolean; + clientFileId: number; + chunkId: number; + + fileType: "video"; + fileUrl: string; + fileId: string; + checksum: string; + totalSize: number; + fileName: string; +}; + +export type UploadAttachmentFileResponse = { + finished: number | boolean; + clientFileId: number; + chunkId: number; + + fileType: "others"; + fileUrl: string; + fileId: string; + checksum: string; + totalSize: number; + fileName: string; +}; + +export type ImageData = { + fileName: string; + totalSize: number | undefined; + width: number | undefined; + height: number | undefined; +}; + +export type FileData = { + fileName: string; + totalSize: number; +}; + +export type UploadAttachmentType = UploadAttachmentImageResponse | UploadAttachmentVideoResponse | UploadAttachmentFileResponse; +export type UploadAttachmentResponse = UploadAttachmentType[]; + +type RawResponse = { + finished: number | boolean; + clientFileId: number; + chunkId: number; + + fileId?: string; + photoId?: string; + + normalUrl?: string; + hdUrl?: string; + thumbUrl?: string; +}; + +type AttachmentData = + | { + filePath: string; + fileType: "image"; + chunkContent: FormData[]; + fileData: ImageData; + params: { + toid?: string; + grid?: string; + totalChunk: number; + fileName: string; + clientId: number; + totalSize: number; + imei: string; + isE2EE: number; + jxl: number; + chunkId: number; + }; + source: AttachmentSource; + } + | { + filePath: string; + fileType: "video" | "others"; + chunkContent: FormData[]; + fileData: FileData; + params: { + toid?: string; + grid?: string; + totalChunk: number; + fileName: string; + clientId: number; + totalSize: number; + imei: string; + isE2EE: number; + jxl: number; + chunkId: number; + }; + source: AttachmentSource; + }; + +const urlType = { + image: "photo_original/upload", + video: "asyncfile/upload", + others: "asyncfile/upload", +}; + +export const uploadAttachmentFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = `${api.zpwServiceMap.file[0]}/api`; + const { sharefile } = ctx.settings!.features; + + function isExceedMaxFile(totalFile: number) { + return totalFile > sharefile.max_file; + } + + function isExceedMaxFileSize(fileSize: number) { + return fileSize > sharefile.max_size_share_file_v3 * 1024 * 1024; + } + + function isExtensionValid(ext: string) { + return sharefile.restricted_ext_file.indexOf(ext) == -1; + } + + /** + * Upload an attachment to a thread + * + * @param sources path to files or attachment sources + * @param threadId Group or User ID + * @param type Message type (User or Group) + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function uploadAttachment( + sources: AttachmentSource | AttachmentSource[], + threadId: string, + type: ThreadType = ThreadType.User, + ): Promise { + if (!sources) throw new ZaloApiError("Missing sources"); + if (!Array.isArray(sources)) sources = [sources]; + if (sources.length == 0) throw new ZaloApiError("Missing sources"); + if (isExceedMaxFile(sources.length)) throw new ZaloApiError("Exceed maximum file of " + sharefile.max_file); + if (!threadId) throw new ZaloApiError("Missing threadId"); + + const chunkSize = ctx.settings!.features.sharefile.chunk_size_file; + const isGroupMessage = type == ThreadType.Group; + const attachmentsData: AttachmentData[] = []; + const url = `${serviceURL}/${isGroupMessage ? "group" : "message"}/`; + const typeParam = isGroupMessage ? "11" : "2"; + + let clientId = Date.now(); + for (const source of sources) { + const isFilePath = typeof source == "string"; + const isBuffer = typeof source == "object" && source.data instanceof Buffer; + + if (!isFilePath && !isBuffer) throw new ZaloApiError("Invalid source type"); + if (!isFilePath && !source.filename) throw new ZaloApiError("Missing filename"); + + if (isFilePath && !fs.existsSync(source)) throw new ZaloApiError("File not found"); + + const extFile = getFileExtension(isFilePath ? source : source.filename).toLowerCase(); + const fileName = isFilePath ? getFileName(source) : source.filename; + + if (isExtensionValid(extFile) == false) + throw new ZaloApiError(`File extension "${extFile}" is not allowed`); + + const data: AttachmentData = { + filePath: isFilePath ? source : source.filename, + chunkContent: [] as FormData[], + params: {}, + source, + } as AttachmentData; + + if (isGroupMessage) data.params.grid = threadId; + else data.params.toid = threadId; + + switch (extFile) { + case "jpg": + case "jpeg": + case "png": + case "webp": { + const imageData = isFilePath ? await getImageMetaData(ctx, source) : { ...source.metadata, fileName }; + if (isExceedMaxFileSize(imageData.totalSize!)) + throw new ZaloApiError( + `File ${fileName} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + data.fileData = imageData; + data.fileType = "image"; + + data.params.totalChunk = Math.ceil(data.fileData.totalSize! / chunkSize); + data.params.fileName = fileName; + data.params.clientId = clientId++; + data.params.totalSize = imageData.totalSize!; + data.params.imei = ctx.imei; + data.params.isE2EE = 0; + data.params.jxl = 0; + data.params.chunkId = 1; + + break; + } + case "mp4": { + const videoSize = isFilePath ? await getFileSize(source) : source.metadata.totalSize; + if (isExceedMaxFileSize(videoSize)) + throw new ZaloApiError( + `File ${fileName} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + data.fileType = "video"; + data.fileData = { + fileName, + totalSize: videoSize, + }; + + data.params.totalChunk = Math.ceil(data.fileData.totalSize! / chunkSize); + data.params.fileName = fileName; + data.params.clientId = clientId++; + data.params.totalSize = videoSize; + data.params.imei = ctx.imei; + data.params.isE2EE = 0; + data.params.jxl = 0; + data.params.chunkId = 1; + + break; + } + default: { + const fileSize = isFilePath ? await getFileSize(source) : source.metadata.totalSize; + if (isExceedMaxFileSize(fileSize)) + throw new ZaloApiError( + `File ${fileName} size exceed maximum size of ${sharefile.max_size_share_file_v3}MB`, + ); + + data.fileType = "others"; + data.fileData = { + fileName, + totalSize: fileSize, + }; + + data.params.totalChunk = Math.ceil(data.fileData.totalSize! / chunkSize); + data.params.fileName = fileName; + data.params.clientId = clientId++; + data.params.totalSize = fileSize; + data.params.imei = ctx.imei; + data.params.isE2EE = 0; + data.params.jxl = 0; + data.params.chunkId = 1; + + break; + } + } + + const fileBuffer = isFilePath ? await fs.promises.readFile(source) : source.data; + for (let i = 0; i < data.params.totalChunk; i++) { + const formData = new FormData(); + const slicedBuffer = fileBuffer.subarray(i * chunkSize, (i + 1) * chunkSize); + formData.append("chunkContent", slicedBuffer, { + filename: fileName, + contentType: "application/octet-stream", + }); + + data.chunkContent[i] = formData; + } + attachmentsData.push(data); + } + + const requests = [], + results: UploadAttachmentType[] = []; + + for (let atmIndex = 0; atmIndex < attachmentsData.length; atmIndex++) { + const data = attachmentsData[atmIndex]; + for (let i = 0; i < data.params.totalChunk; i++) { + const encryptedParams = utils.encodeAES(JSON.stringify(data.params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message"); + + requests.push( + utils + .request( + utils.makeURL(url + urlType[data.fileType], { type: typeParam, params: encryptedParams }), + { + method: "POST", + headers: data.chunkContent[i].getHeaders(), + body: data.chunkContent[i].getBuffer(), + }, + ) + .then(async (response) => { + /** + * @TODO: better type rather than any + */ + const resData = await resolveResponse(ctx, response); + + if (resData && resData.fileId != "-1" && resData.photoId != "-1") + await new Promise((resolve) => { + if (data.fileType == "video" || data.fileType == "others") { + const uploadCallback: UploadCallback = async (wsData) => { + const result = { + fileType: data.fileType, + ...resData, + ...wsData, + totalSize: data.fileData.totalSize, + fileName: data.fileData.fileName, + checksum: ( + await getMd5LargeFileObject(data.source, data.fileData.totalSize) + ).data, + }; + results[atmIndex] = result; + resolve(); + }; + + ctx.uploadCallbacks.set(resData.fileId!.toString(), uploadCallback); + } + + if (data.fileType == "image") { + const result = { + fileType: "image" as const, + width: data.fileData.width!, + height: data.fileData.height!, + totalSize: data.fileData.totalSize!, + hdSize: data.fileData.totalSize!, + + finished: resData.finished, + normalUrl: resData.normalUrl!, + hdUrl: resData.hdUrl!, + thumbUrl: resData.thumbUrl!, + chunkId: resData.chunkId, + + photoId: resData.photoId!, + clientFileId: resData.clientFileId, + }; + results[atmIndex] = result; + resolve(); + } + }); + }), + ); + data.params.chunkId++; + } + } + + await Promise.all(requests); + + return results; + }; +}); diff --git a/_libs/zca-js/src/apis/uploadProductPhoto.ts b/_libs/zca-js/src/apis/uploadProductPhoto.ts new file mode 100644 index 0000000..3cdbb8d --- /dev/null +++ b/_libs/zca-js/src/apis/uploadProductPhoto.ts @@ -0,0 +1,76 @@ +import fs from "node:fs"; +import FormData from "form-data"; +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { AttachmentSource } from "../models/index.js"; +import { apiFactory, getImageMetaData } from "../utils.js"; + +export type UploadProductPhotoPayload = { + file: AttachmentSource; +}; + +export type UploadProductPhotoResponse = { + normalUrl: string; + photoId: string; + finished: number; + hdUrl: string; + thumbUrl: string; + clientFileId: number; + chunkId: number; +}; + +export const uploadProductPhotoFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/product/upload/photo`); + + /** + * Upload product photo for api quick message, product catalog or custom local storage + * + * @param payload file path or attachment source + * + * @throws {ZaloApiError | ZaloApiMissingImageMetadataGetter} + */ + return async function uploadProductPhoto(payload: UploadProductPhotoPayload) { + const isSourceFilePath = typeof payload.file == "string"; + const fileMetaData = isSourceFilePath + ? await getImageMetaData(ctx, payload.file as string) + : (payload.file as { metadata: { totalSize: number; width?: number; height?: number } }).metadata; + + const fileSize = fileMetaData.totalSize || 0; + + const fileBuffer = isSourceFilePath + ? await fs.promises.readFile(payload.file as string) + : (payload.file as { data: Buffer }).data; + + const formData = new FormData(); + formData.append("chunkContent", fileBuffer, { + filename: "undefined", + contentType: "application/octet-stream", + }); + + const params = { + totalChunk: 1, + fileName: `Base64_Img_Picker_${Date.now()}.jpg`, + clientId: Date.now(), + totalSize: fileSize, + imei: ctx.imei, + chunkId: 1, + toid: ctx.loginInfo.send2me_id, + featureId: 1, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request( + utils.makeURL(serviceURL, { + params: encryptedParams, + }), + { + method: "POST", + headers: formData.getHeaders(), + body: formData.getBuffer(), + }, + ); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/apis/votePoll.ts b/_libs/zca-js/src/apis/votePoll.ts new file mode 100644 index 0000000..56208c1 --- /dev/null +++ b/_libs/zca-js/src/apis/votePoll.ts @@ -0,0 +1,38 @@ +import { ZaloApiError } from "../Errors/ZaloApiError.js"; +import type { PollOptions } from "../models/index.js"; +import { apiFactory } from "../utils.js"; + +export type VotePollResponse = { + options: PollOptions[]; +}; + +export const votePollFactory = apiFactory()((api, ctx, utils) => { + const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/poll/vote`); + + /** + * Vote on a poll + * + * @param pollId The ID of the poll to vote on + * @param optionId The ID(s) of the option to vote on + * + * @throws ZaloApiError + */ + return async function votePoll(pollId: number, optionId: number | number[]) { + if (!Array.isArray(optionId)) optionId = [optionId]; + + const params = { + poll_id: pollId, + option_ids: optionId, // unvote = empty array + imei: ctx.imei, + }; + + const encryptedParams = utils.encodeAES(JSON.stringify(params)); + if (!encryptedParams) throw new ZaloApiError("Failed to encrypt params"); + + const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), { + method: "GET", + }); + + return utils.resolve(response); + }; +}); diff --git a/_libs/zca-js/src/context.ts b/_libs/zca-js/src/context.ts new file mode 100644 index 0000000..21f2b20 --- /dev/null +++ b/_libs/zca-js/src/context.ts @@ -0,0 +1,216 @@ +import type { Agent } from "http"; +import type { CookieJar } from "tough-cookie"; + +type UploadEventData = { + fileUrl: string; + fileId: string; +}; + +export type UploadCallback = (data: UploadEventData) => unknown; + +type ShareFileSettings = { + big_file_domain_list: string[]; + max_size_share_file_v2: number; + max_size_share_file_v3: number; + file_upload_show_icon_1GB: boolean; + restricted_ext: string; + next_file_time: number; + max_file: number; + max_size_photo: number; + max_size_share_file: number; + max_size_resize_photo: number; + max_size_gif: number; + max_size_original_photo: number; + chunk_size_file: number; + restricted_ext_file: string[]; +}; + +type SocketSettings = { + rotate_error_codes: number[]; + retries: { + [key: string]: { + max?: number; + times: number[] | number; + }; + }; + debug: { + enable: boolean; + }; + ping_interval: number; + reset_endpoint: number; + queue_ctrl_actionid_map: { + "611_0": string; + "610_1": string; + "610_0": string; + "603_0": string; + "611_1": string; + }; + close_and_retry_codes: number[]; + max_msg_size: number; + enable_ctrl_socket: boolean; + reconnect_after_fallback: boolean; + enable_chat_socket: boolean; + submit_wss_log: boolean; + disable_lp: boolean; + offline_monitor: { + enable: boolean; + }; +}; + +type LoginInfo = { + // eslint-disable-next-line + [key: string]: any; + haspcclient: number; + public_ip: string; + language: string; + send2me_id: string; + zpw_service_map_v3: { + other_contact: string[]; + chat_e2e: string[]; + workspace: string[]; + catalog: string[]; + boards: string[]; + downloadStickerUrl: string[]; + sp_contact: string[]; + zcloud_up_file: string[]; + media_store_send2me: string[]; + push_act: string[]; + aext: string[]; + zfamily: string[]; + group_poll: string[]; + group_cloud_message: string[]; + media_store: string[]; + file: string[]; + auto_reply: string[]; + sync_action: string[]; + friendLan: string[]; + friend: string[]; + alias: string[]; + zimsg: string[]; + group_board: string[]; + conversation: string[]; + group: string[]; + fallback_LP: string[]; + friend_board: string[]; + up_file: string[]; + zavi: string[]; + reaction: string[]; + voice_call: string[]; + profile: string[]; + sticker: string[]; + label: string[]; + consent: string[]; + zcloud: string[]; + chat: string[]; + todoUrl: string[]; + recent_search: string[]; + group_e2e: string[]; + quick_message: string[]; + }; +}; + +type ExtraVer = { + phonebook: number; + conv_label: string; + friend: string; + ver_sticker_giphy_suggest: number; + ver_giphy_cate: number; + alias: string; + ver_sticker_cate_list: number; + block_friend: string; +}; + +export type ZPWServiceMap = LoginInfo["zpw_service_map_v3"]; + +export type AppContextBase = { + uid: string; + imei: string; + cookie: CookieJar; + userAgent: string; + language: string; + secretKey: string | null; + zpwServiceMap: ZPWServiceMap; + settings: { + // eslint-disable-next-line + [key: string]: any; + features: { + // eslint-disable-next-line + [key: string]: any; + sharefile: ShareFileSettings; + socket: SocketSettings; + }; + keepalive: { + alway_keepalive: number; + keepalive_duration: number; + time_deactive: number; + }; + }; + loginInfo: LoginInfo; + extraVer: ExtraVer; +}; + +export type ImageMetadataGetterResponse = { width: number; height: number; size: number } | null; +export type ImageMetadataGetter = (filePath: string) => Promise; + +export type Options = { + selfListen: boolean; + checkUpdate: boolean; + logging: boolean; + + apiType: number; + apiVersion: number; + + agent?: Agent; + + /** + * Optional fetch implementation for polyfills in non-standard environments. + * If using proxy, `node-fetch` is highly recommended. + */ + polyfill: typeof fetch; + + imageMetadataGetter?: ImageMetadataGetter; +}; + +const _5_MINUTES = 5 * 60 * 1000; +class CallbacksMap extends Map { + /** + * @param ttl Time to live in milliseconds. Default is 5 minutes. + */ + set(key: string, value: UploadCallback, ttl: number = _5_MINUTES): this { + setTimeout(() => { + this.delete(key); + }, ttl); + return super.set(key, value); + } +} + +export type AppContextExtended = { + uploadCallbacks: CallbacksMap; + options: Options; + readonly API_TYPE: number; + readonly API_VERSION: number; +}; + +export type ContextBase = Partial & AppContextExtended; + +export const createContext = (apiType = 30, apiVersion = 671) => + ({ + API_TYPE: apiType, + API_VERSION: apiVersion, + uploadCallbacks: new CallbacksMap(), + options: { + selfListen: false, + checkUpdate: true, + logging: true, + polyfill: global.fetch, + }, + secretKey: null, + }) as ContextBase; + +export type ContextSession = AppContextBase & AppContextExtended & { secretKey: string }; + +export function isContextSession(ctx: ContextBase): ctx is ContextSession { + return !!ctx.secretKey; +} + +export const MAX_MESSAGES_PER_SEND = 50; diff --git a/_libs/zca-js/src/index.ts b/_libs/zca-js/src/index.ts new file mode 100644 index 0000000..ce84709 --- /dev/null +++ b/_libs/zca-js/src/index.ts @@ -0,0 +1,163 @@ +export * from "./Errors/index.js"; +export * from "./models/index.js"; +export * from "./zalo.js"; + +export type { ContextSession, ContextBase, AppContextExtended, AppContextBase, Options, ZPWServiceMap, ImageMetadataGetter,ImageMetadataGetterResponse } from "./context.js"; + +// API TYPES +export type { AcceptFriendRequestResponse } from "./apis/acceptFriendRequest.js"; +export type { AddGroupBlockedMemberResponse } from "./apis/addGroupBlockedMember.js"; +export type { AddGroupDeputyResponse } from "./apis/addGroupDeputy.js"; +export type { AddPollOptionsPayload, AddPollOptionsResponse, AddPollOptionsOption } from "./apis/addPollOptions.js"; +export type { AddQuickMessagePayload, AddQuickMessageResponse } from "./apis/addQuickMessage.js"; +export type { AddReactionDestination, AddReactionResponse, CustomReaction } from "./apis/addReaction.js"; +export type { AddUnreadMarkResponse } from "./apis/addUnreadMark.js"; +export type { AddUserToGroupResponse } from "./apis/addUserToGroup.js"; +export type { BlockUserResponse } from "./apis/blockUser.js"; +export type { BlockViewFeedResponse } from "./apis/blockViewFeed.js"; +export type { ChangeAccountAvatarResponse } from "./apis/changeAccountAvatar.js"; +export type { ChangeFriendAliasResponse } from "./apis/changeFriendAlias.js"; +export type { ChangeGroupAvatarResponse } from "./apis/changeGroupAvatar.js"; +export type { ChangeGroupNameResponse } from "./apis/changeGroupName.js"; +export type { ChangeGroupOwnerResponse } from "./apis/changeGroupOwner.js"; +export type { CreateAutoReplyPayload, CreateAutoReplyResponse } from "./apis/createAutoReply.js"; +export type { CreateCatalogResponse } from "./apis/createCatalog.js"; +export type { CreateGroupOptions, CreateGroupResponse } from "./apis/createGroup.js"; +export type { CreateNoteOptions, CreateNoteResponse } from "./apis/createNote.js"; +export type { CreatePollOptions, CreatePollResponse } from "./apis/createPoll.js"; +export type { CreateProductCatalogPayload, CreateProductCatalogResponse } from "./apis/createProductCatalog.js"; +export type { CreateReminderOptions, CreateReminderResponse, CreateReminderUser, CreateReminderGroup } from "./apis/createReminder.js"; +export type { DeleteAutoReplyResponse } from "./apis/deleteAutoReply.js"; +export type { DeleteAvatarResponse } from "./apis/deleteAvatar.js"; +export type { DeleteCatalogResponse } from "./apis/deleteCatalog.js"; +export type { DeleteChatLastMessage, DeleteChatResponse } from "./apis/deleteChat.js"; +export type { DeleteGroupInviteBoxResponse } from "./apis/deleteGroupInviteBox.js"; +export type { DeleteMessageDestination, DeleteMessageResponse } from "./apis/deleteMessage.js"; +export type { DeleteProductCatalogPayload, DeleteProductCatalogResponse } from "./apis/deleteProductCatalog.js"; +export type { DisableGroupLinkResponse } from "./apis/disableGroupLink.js"; +export type { DisperseGroupResponse } from "./apis/disperseGroup.js"; +export type { EditNoteOptions, EditNoteResponse } from "./apis/editNote.js"; +export type { EditReminderGroup, EditReminderUser, EditReminderOptions, EditReminderResponse } from "./apis/editReminder.js"; +export type { EnableGroupLinkResponse } from "./apis/enableGroupLink.js"; +export type { FetchAccountInfoResponse } from "./apis/fetchAccountInfo.js"; +export type { FindUserResponse } from "./apis/findUser.js"; +export type { FindUserByUsernameResponse } from "./apis/findUserByUsername.js"; +export type { ForwardMessageSuccess, ForwardMessageFail, ForwardMessagePayload, ForwardMessageResponse } from "./apis/forwardMessage.js"; +export type { GetAliasListResponse } from "./apis/getAliasList.js"; +export type { GetAllFriendsResponse } from "./apis/getAllFriends.js"; +export type { GetAllGroupsResponse } from "./apis/getAllGroups.js"; +export type { GetArchivedChatListResponse } from "./apis/getArchivedChatList.js"; +export type { GetAutoDeleteChatResponse } from "./apis/getAutoDeleteChat.js"; +export type { GetAutoReplyListResponse } from "./apis/getAutoReplyList.js"; +export type { GetAvatarListResponse } from "./apis/getAvatarList.js"; +export type { GetAvatarUrlProfileResponse } from "./apis/getAvatarUrlProfile.js"; +export type { GetBizAccountResponse } from "./apis/getBizAccount.js"; +export type { GetCatalogListPayload, GetCatalogListResponse } from "./apis/getCatalogList.js"; +export type { GetCloseFriendsResponse } from "./apis/getCloseFriends.js"; +export type { GetFriendBoardListResponse } from "./apis/getFriendBoardList.js"; +export type { GetFriendOnlinesResponse, GetFriendOnlinesStatus } from "./apis/getFriendOnlines.js"; +export type { GetFriendRecommendationsResponse, FriendRecommendationsCollapseMsgListConfig, FriendRecommendationsDataInfo, FriendRecommendationsRecommItem } from "./apis/getFriendRecommendations.js"; +export type { GetFriendRequestStatusResponse } from "./apis/getFriendRequestStatus.js"; +export type { GetFullAvatarResponse } from "./apis/getFullAvatar.js"; +export type { GetGroupBlockedMemberPayload, GetGroupBlockedMemberResponse } from "./apis/getGroupBlockedMember.js"; +export type { GetGroupChatHistoryResponse } from "./apis/getGroupChatHistory.js"; +export type { GroupInfoPendingApprove, GroupInfoResponse } from "./apis/getGroupInfo.js"; +export type { GetGroupInviteBoxInfoPayload, GetGroupInviteBoxInfoResponse } from "./apis/getGroupInviteBoxInfo.js"; +export type { GetGroupInviteBoxListPayload, GetGroupInviteBoxListResponse } from "./apis/getGroupInviteBoxList.js"; +export type { GetGroupLinkDetailResponse } from "./apis/getGroupLinkDetail.js"; +export type { GetGroupLinkInfoPayload, GetGroupLinkInfoResponse } from "./apis/getGroupLinkInfo.js"; +export type { GetGroupMembersInfoResponse, GroupMemberProfile } from "./apis/getGroupMembersInfo.js"; +export type { GetHiddenConversationsResponse } from "./apis/getHiddenConversations.js"; +export type { GetLabelsResponse } from "./apis/getLabels.js"; +export type { BoardItem, GetListBoardResponse, ListBoardOptions } from "./apis/getListBoard.js"; +export type { GetListReminderResponse, ListReminderOptions, ReminderListGroup, ReminderListUser } from "./apis/getListReminder.js"; +export type { GetMultiUsersByPhonesResponse } from "./apis/getMultiUsersByPhones.js"; +export type { GetMuteResponse, MuteEntriesInfo } from "./apis/getMute.js"; +export type { GetPendingGroupMembersResponse, GetPendingGroupMembersUserInfo } from "./apis/getPendingGroupMembers.js"; +export type { GetPinConversationsResponse } from "./apis/getPinConversations.js"; +export type { PollDetailResponse } from "./apis/getPollDetail.js"; +export type { GetProductCatalogListPayload, GetProductCatalogListResponse } from "./apis/getProductCatalogList.js"; +export type { GetQRResponse } from "./apis/getQR.js"; +export type { GetQuickMessageListResponse } from "./apis/getQuickMessageList.js"; +export type { GetRelatedFriendGroupResponse } from "./apis/getRelatedFriendGroup.js"; +export type { GetReminderResponse } from "./apis/getReminder.js"; +export type { GetReminderResponsesResponse } from "./apis/getReminderResponses.js"; +export type { GetSentFriendRequestResponse, SentFriendRequestInfo } from "./apis/getSentFriendRequest.js"; +export type { GetSettingsResponse } from "./apis/getSettings.js"; +export type { GetStickerCategoryDetailResponse } from "./apis/getStickerCategoryDetail.js"; +export type { StickerDetailResponse } from "./apis/getStickersDetail.js"; +export type { GetUnreadMarkResponse, UnreadMark } from "./apis/getUnreadMark.js"; +export type { ProfileInfo, UserInfoResponse } from "./apis/getUserInfo.js"; +export type { InviteUserToGroupsResponse } from "./apis/inviteUserToGroups.js"; +export type { JoinGroupInviteBoxResponse } from "./apis/joinGroupInviteBox.js"; +export type { JoinGroupLinkResponse } from "./apis/joinGroupLink.js"; +export type { KeepAliveResponse } from "./apis/keepAlive.js"; +export type { LastOnlineResponse } from "./apis/lastOnline.js"; +export type { LeaveGroupResponse } from "./apis/leaveGroup.js"; +export type { LockPollResponse } from "./apis/lockPoll.js"; +export type { LoginQRCallback, LoginQRCallbackEvent } from "./apis/loginQR.js"; +export type { ParseLinkErrorMaps, ParseLinkResponse } from "./apis/parseLink.js"; +export type { RejectFriendRequestResponse } from "./apis/rejectFriendRequest.js"; +export type { RemoveFriendResponse } from "./apis/removeFriend.js"; +export type { RemoveFriendAliasResponse } from "./apis/removeFriendAlias.js"; +export type { RemoveGroupBlockedMemberResponse } from "./apis/removeGroupBlockedMember.js"; +export type { RemoveGroupDeputyResponse } from "./apis/removeGroupDeputy.js"; +export type { RemoveQuickMessageResponse } from "./apis/removeQuickMessage.js"; +export type { RemoveReminderResponse } from "./apis/removeReminder.js"; +export type { RemoveUnreadMarkResponse } from "./apis/removeUnreadMark.js"; +export type { RemoveUserFromGroupResponse } from "./apis/removeUserFromGroup.js"; +export type { ResetHiddenConversPinResponse } from "./apis/resetHiddenConversPin.js"; +export type { ReuseAvatarResponse } from "./apis/reuseAvatar.js"; +export type { ReviewPendingMemberRequestPayload, ReviewPendingMemberRequestResponse } from "./apis/reviewPendingMemberRequest.js"; +export type { SearchStickerResponse } from "./apis/searchSticker.js"; +export type { SendBankCardPayload, SendBankCardResponse } from "./apis/sendBankCard.js"; +export type { SendCardOptions, SendCardResponse } from "./apis/sendCard.js"; +export type { SendDeliveredEventMessageParams, SendDeliveredEventResponse } from "./apis/sendDeliveredEvent.js"; +export type { SendFriendRequestResponse } from "./apis/sendFriendRequest.js"; +export type { SendLinkOptions, SendLinkResponse } from "./apis/sendLink.js"; +export type { Mention, MessageContent, SendMessageQuote, SendMessageResponse, SendMessageResult, Style } from "./apis/sendMessage.js"; +export type { SendReportOptions, SendReportResponse } from "./apis/sendReport.js"; +export type { SendSeenEventMessageParams, SendSeenEventResponse } from "./apis/sendSeenEvent.js"; +export type { SendStickerResponse, SendStickerPayload } from "./apis/sendSticker.js"; +export type { SendTypingEventResponse } from "./apis/sendTypingEvent.js"; +export type { SendVideoOptions, SendVideoResponse } from "./apis/sendVideo.js"; +export type { SendVoiceOptions, SendVoiceResponse } from "./apis/sendVoice.js"; +export type { SetHiddenConversationsResponse } from "./apis/setHiddenConversations.js"; +export type { SetMuteParams, SetMuteResponse } from "./apis/setMute.js"; +export type { SetPinnedConversationsResponse } from "./apis/setPinnedConversations.js"; +export type { SharePollResponse } from "./apis/sharePoll.js"; +export type { UnBlockUserResponse } from "./apis/unblockUser.js"; +export type { UndoPayload, UndoResponse } from "./apis/undo.js"; +export type { UndoFriendRequestResponse } from "./apis/undoFriendRequest.js"; +export type { UpdateActiveStatusResponse } from "./apis/updateActiveStatus.js"; +export type { UpdateArchivedChatListTarget, UpdateArchivedChatListResponse } from "./apis/updateArchivedChatList.js"; +export type { UpdateAutoDeleteChatResponse } from "./apis/updateAutoDeleteChat.js"; +export type { UpdateAutoReplyPayload, UpdateAutoReplyResponse } from "./apis/updateAutoReply.js"; +export type { UpdateCatalogPayload, UpdateCatalogResponse } from "./apis/updateCatalog.js"; +export type { UpdateGroupSettingsOptions, UpdateGroupSettingsResponse } from "./apis/updateGroupSettings.js"; +export type { UpdateHiddenConversPinResponse } from "./apis/updateHiddenConversPin.js"; +export type { UpdateLabelsPayload, UpdateLabelsResponse } from "./apis/updateLabels.js"; +export type { UpdateLangResponse } from "./apis/updateLang.js"; +export type { UpdateProductCatalogPayload, UpdateProductCatalogResponse } from "./apis/updateProductCatalog.js"; +export type { UpdateProfilePayload, UpdateProfileResponse } from "./apis/updateProfile.js"; +export type { UpdateProfileBioResponse } from "./apis/updateProfileBio.js"; +export type { UpdateQuickMessagePayload, UpdateQuickMessageResponse } from "./apis/updateQuickMessage.js"; +export type { UpdateSettingsResponse } from "./apis/updateSettings.js"; +export type { UpgradeGroupToCommunityResponse } from "./apis/upgradeGroupToCommunity.js"; +export type { FileData, ImageData, UploadAttachmentResponse, UploadAttachmentType, UploadAttachmentImageResponse, UploadAttachmentVideoResponse, UploadAttachmentFileResponse } from "./apis/uploadAttachment.js"; +export type { UploadProductPhotoPayload, UploadProductPhotoResponse } from "./apis/uploadProductPhoto.js"; +export type { VotePollResponse } from "./apis/votePoll.js"; + +export type { CustomAPICallback, CustomAPIProps } from "./apis/custom.js"; + +// Others (Enum) +export { CloseReason } from "./apis/listen.js"; +export { LoginQRCallbackEventType } from "./apis/loginQR.js"; +export { FriendRecommendationsType } from "./apis/getFriendRecommendations.js"; +export { ReviewPendingMemberRequestStatus } from "./apis/reviewPendingMemberRequest.js"; +export { TextStyle, Urgency } from "./apis/sendMessage.js"; +export { ReportReason } from "./apis/sendReport.js"; +export { MuteAction, MuteDuration } from "./apis/setMute.js"; +export { ChatTTL } from "./apis/updateAutoDeleteChat.js"; +export { UpdateLangAvailableLanguages } from "./apis/updateLang.js"; +export { UpdateSettingsType } from "./apis/updateSettings.js"; diff --git a/_libs/zca-js/src/models/Attachment.ts b/_libs/zca-js/src/models/Attachment.ts new file mode 100644 index 0000000..e65b27b --- /dev/null +++ b/_libs/zca-js/src/models/Attachment.ts @@ -0,0 +1,11 @@ +export type AttachmentSource = + | string + | { + data: Buffer; + filename: `${string}.${string}`; + metadata: { + totalSize: number; + width?: number; + height?: number; + }; + }; diff --git a/_libs/zca-js/src/models/AutoReply.ts b/_libs/zca-js/src/models/AutoReply.ts new file mode 100644 index 0000000..a59372d --- /dev/null +++ b/_libs/zca-js/src/models/AutoReply.ts @@ -0,0 +1,21 @@ +export type AutoReplyItem = { + id: number; + weight: number; + enable: boolean; + modifiedTime: number; + startTime: number; + endTime: number; + content: string; + scope: AutoReplyScope; + uids: string[] | null; + ownerId: number; + recurrence: string[]; + createdTime: number; +}; + +export enum AutoReplyScope { + Everyone = 0, + Stranger = 1, + SpecificFriends = 2, + FriendsExcept = 3 +} diff --git a/_libs/zca-js/src/models/Board.ts b/_libs/zca-js/src/models/Board.ts new file mode 100644 index 0000000..7f6a78f --- /dev/null +++ b/_libs/zca-js/src/models/Board.ts @@ -0,0 +1,64 @@ +export enum BoardType { + Note = 1, + PinnedMessage = 2, + Poll = 3, +} + +export type PollDetail = { + creator: string; + question: string; + options: PollOptions[]; + joined: boolean; + closed: boolean; + poll_id: number; + allow_multi_choices: boolean; + allow_add_new_option: boolean; + is_anonymous: boolean; + poll_type: number; + created_time: number; + updated_time: number; + expired_time: number; + is_hide_vote_preview: boolean; + num_vote: number; +}; + +export type PollOptions = { + content: string; + votes: number; + voted: boolean; + voters: string[]; + option_id: number; +}; + +export type NoteDetail = { + id: string; + type: number; + color: number; + emoji: string; + startTime: number; + duration: number; + params: { + title: string; + extra?: string; + }; + creatorId: string; + editorId: string; + createTime: number; + editTime: number; + repeat: number; +}; + +export type PinnedMessageDetail = { + id: string; + type: number; + color: number; + emoji: string; + startTime: number; + duration: number; + params: Record; // @TODO: Define a more specific type + creatorId: string; + editorId: string; + createTime: number; + editTime: number; + repeat: number; +}; diff --git a/_libs/zca-js/src/models/Catalog.ts b/_libs/zca-js/src/models/Catalog.ts new file mode 100644 index 0000000..8f98b22 --- /dev/null +++ b/_libs/zca-js/src/models/Catalog.ts @@ -0,0 +1,16 @@ +export type CatalogItem = { + id: string; + name: string; + version: number; + ownerId: string; + isDefault: boolean; + /** + * Relative path used to build the catalog URL. + * + * Example: `https://catalog.zalo.me/${path}` + */ + path: string; + catalogPhoto: string | null; + totalProduct: number; + created_time: number; +}; diff --git a/_libs/zca-js/src/models/DeliveredMessage.ts b/_libs/zca-js/src/models/DeliveredMessage.ts new file mode 100644 index 0000000..5ed3b29 --- /dev/null +++ b/_libs/zca-js/src/models/DeliveredMessage.ts @@ -0,0 +1,44 @@ +import { ThreadType } from "./Enum.js"; + +export type TDeliveredMessage = { + msgId: string; + seen: number; + deliveredUids: string[]; + seenUids: string[]; + realMsgId: string; + mSTs: number; +}; + +export type TGroupDeliveredMessage = TDeliveredMessage & { + groupId: string; +}; + +export class UserDeliveredMessage { + type: ThreadType.User = ThreadType.User; + + data: TDeliveredMessage; + threadId: string; + isSelf: false; + + constructor(data: TDeliveredMessage) { + this.data = data; + this.threadId = data.deliveredUids[0]; + this.isSelf = false; + } +} + +export class GroupDeliveredMessage { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupDeliveredMessage; + threadId: string; + isSelf: boolean; + + constructor(uid: string, data: TGroupDeliveredMessage) { + this.data = data; + this.threadId = data.groupId; + this.isSelf = data.deliveredUids.includes(uid); + } +} + +export type DeliveredMessage = UserDeliveredMessage | GroupDeliveredMessage; diff --git a/_libs/zca-js/src/models/Enum.ts b/_libs/zca-js/src/models/Enum.ts new file mode 100644 index 0000000..2f6cdbf --- /dev/null +++ b/_libs/zca-js/src/models/Enum.ts @@ -0,0 +1,255 @@ +export enum ThreadType { + User, + Group, +} + +export enum DestType { + Group = 1, + User = 3, + Page = 5, +} + +export enum Gender { + Male = 0, + Female = 1, +} + +export enum AvatarSize { + Small = 120, + Large = 240, +} + +/** + * @note Bank codes list after Mitm on Mobile and Bank's supported by Zalo + * @documents https://developers.zalo.me/docs/zalo-notification-service/phu-luc/danh-sach-bin-code - docs missing bin code and short_name bank + */ +export enum BinBankCard { + /** + * NH TMCP An Bรฌnh + */ + ABBank = 970425, + /** + * NH TMCP ร Chรขu + */ + ACB = 970416, + /** + * NH Nรดng nghiแป‡p vร  Phรกt triแปƒn Nรดng thรดn Viแป‡t Nam + */ + Agribank = 970405, + /** + * NH TMCP ฤแบงu tฦฐ vร  Phรกt triแปƒn Viแป‡t Nam + */ + BIDV = 970418, + /** + * NH TMCP Bแบฃn Viแป‡t + */ + BVBank = 970454, + /** + * NH TMCP Bแบฏc ร + */ + BacA_Bank = 970409, + /** + * NH TMCP Bแบฃo Viแป‡t + */ + BaoViet_Bank = 970438, + /** + * NH sแป‘ CAKE by VPBank - TMCP Viแป‡t Nam Thแป‹nh Vฦฐแปฃng + */ + CAKE = 546034, + /** + * NH Thฦฐฦกng mแบกi TNHH MTV Xรขy dแปฑng Viแป‡t Nam + */ + CB_Bank = 970444, + /** + * NH TNHH MTV CIMB Viแป‡t Nam + */ + CIMB_Bank = 422589, + /** + * NH Hแปฃp tรกc xรฃ Viแป‡t Nam + */ + Coop_Bank = 970446, + /** + * NH TNHH MTV Phรกt triแปƒn Singapore - CN TP. Hแป“ Chรญ Minh + */ + DBS_Bank = 796500, + /** + * NH TMCP ฤรดng ร + */ + DongA_Bank = 970406, + /** + * NH TMCP Xuแบฅt Nhแบญp khแบฉu Viแป‡t Nam + */ + Eximbank = 970431, + /** + * NH TMCP Dแบงu khรญ Toร n cแบงu + */ + GPBank = 970408, + /** + * NH TMCP Phรกt triแปƒn TP. Hแป“ Chรญ Minh + */ + HDBank = 970437, + /** + * NH TNHH MTV HSBC (Viแป‡t Nam) + */ + HSBC = 458761, + /** + * NH TNHH MTV Hong Leong Viแป‡t Nam + */ + HongLeong_Bank = 970442, + /** + * NH Cรดng nghiแป‡p Hร n Quแป‘c - CN TP. Hแป“ Chรญ Minh + */ + IBK_HCM = 970456, + /** + * NH Cรดng nghiแป‡p Hร n Quแป‘c - CN Hร  Nแป™i + */ + IBK_HN = 970455, + /** + * NH TNHH Indovina + */ + Indovina_Bank = 970434, + /** + * NH ฤแบกi chรบng TNHH Kasikornbank - CN TP. Hแป“ Chรญ Minh + */ + KBank = 668888, + /** + * NH TMCP Kiรชn Long + */ + KienlongBank = 970452, + /** + * NH Kookmin - CN TP. Hแป“ Chรญ Minh + */ + Kookmin_Bank_HCM = 970463, + /** + * NH Kookmin - CN Hร  Nแป™i + */ + Kookmin_Bank_HN = 970462, + /** + * NH TMCP Lแป™c Phรกt Viแป‡t Nam + */ + LPBank = 970449, + /** + * NH TMCP Quรขn ฤ‘แป™i + */ + MB_Bank = 970422, + /** + * NH TMCP Hร ng Hแบฃi + */ + MSB = 970426, + /** + * NH TMCP Quแป‘c Dรขn + */ + NCB = 970419, + /** + * NH TMCP Nam ร + */ + Nam_A_Bank = 970428, + /** + * NH Nonghyup - CN Hร  Nแป™i + */ + NongHyup_Bank = 801011, + /** + * NH TMCP Phฦฐฦกng ฤรดng + */ + OCB = 970448, + /** + * NH Thฦฐฦกng mแบกi TNHH MTV ฤแบกi Dฦฐฦกng + */ + Ocean_Bank = 970414, + /** + * NH TMCP Thแป‹nh vฦฐแปฃng vร  Phรกt triแปƒn + */ + PGBank = 970430, + /** + * NH TMCP ฤแบกi Chรบng Viแป‡t Nam + */ + PVcomBank = 970412, + /** + * NH TNHH MTV Public Viแป‡t Nam + */ + Public_Bank_Vietnam = 970439, + /** + * NH TMCP Sร i Gรฒn + */ + SCB = 970429, + /** + * NH TMCP Sร i Gรฒn - Hร  Nแป™i + */ + SHB = 970443, + /** + * NH TMCP Sร i Gรฒn Thฦฐฦกng Tรญn + */ + Sacombank = 970403, + /** + * NH TMCP Sร i Gรฒn Cรดng Thฦฐฦกng + */ + Saigon_Bank = 970400, + /** + * NH TMCP ฤรดng Nam ร + */ + SeABank = 970440, + /** + * NH TNHH MTV Shinhan Viแป‡t Nam + */ + Shinhan_Bank = 970424, + /** + * NH TNHH MTV Standard Chartered Bank Viแป‡t Nam + */ + Standard_Chartered_Vietnam = 970410, + /** + * NH sแป‘ TNEX + */ + TNEX = 9704261, + /** + * NH TMCP Tiรชn Phong + */ + TPBank = 970423, + /** + * NH TMCP Kแปน thฦฐฦกng Viแป‡t Nam + */ + Techcombank = 970407, + /** + * NH sแป‘ Timo by Bแบฃn Viแป‡t Bank + */ + Timo = 963388, + /** + * NH sแป‘ UBank by VPBank + */ + UBank = 546035, + /** + * NH United Overseas Bank Viแป‡t Nam + */ + United_Overseas_Bank_Vietnam = 970458, + /** + * NH TMCP Quแป‘c tแบฟ Viแป‡t Nam + */ + VIB = 970441, + /** + * NH TMCP Viแป‡t Nam Thแป‹nh Vฦฐแปฃng + */ + VPBank = 970432, + /** + * NH Liรชn doanh Viแป‡t - Nga + */ + VRB = 970421, + /** + * NH TMCP Viแป‡t ร + */ + VietABank = 970427, + /** + * NH TMCP Viแป‡t Nam Thฦฐฦกng Tรญn + */ + VietBank = 970433, + /** + * NH TMCP Ngoแบกi Thฦฐฦกng Viแป‡t Nam + */ + Vietcombank = 970436, + /** + * NH TMCP Cรดng thฦฐฦกng Viแป‡t Nam + */ + VietinBank = 970415, + /** + * NH TNHH MTV Woori Viแป‡t Nam + */ + Woori_Bank = 970457, +} diff --git a/_libs/zca-js/src/models/FriendEvent.ts b/_libs/zca-js/src/models/FriendEvent.ts new file mode 100644 index 0000000..c87cfa7 --- /dev/null +++ b/_libs/zca-js/src/models/FriendEvent.ts @@ -0,0 +1,205 @@ +export enum FriendEventType { + ADD, + REMOVE, + + REQUEST, + UNDO_REQUEST, + REJECT_REQUEST, + + SEEN_FRIEND_REQUEST, + + BLOCK, + UNBLOCK, + BLOCK_CALL, + UNBLOCK_CALL, + + PIN_UNPIN, + PIN_CREATE, + + UNKNOWN, +} + +export type TFriendEventBase = string; + +export type TFriendEventRejectUndo = { + toUid: string; + fromUid: string; +}; + +export type TFriendEventRequest = { + toUid: string; + fromUid: string; + src: number; + message: string; +}; + +export type TFriendEventSeenRequest = string[]; + +export type TFriendEventPinCreateTopicParams = { + senderUid: string; + senderName: string; + client_msg_id: string; + global_msg_id: string; + msg_type: number; + title: string; +}; + +export type TFriendEventPinTopic = { + topicId: string; + topicType: number; +}; + +export type TFriendEventPinCreateTopic = { + type: number; + color: number; + emoji: string; + startTime: number; + duration: number; + params: TFriendEventPinCreateTopicParams; + id: string; + creatorId: string; + createTime: number; + editorId: string; + editTime: number; + repeat: number; + action: number; +}; + +export type TFriendEventPinCreate = { + oldTopic?: TFriendEventPinTopic; + topic: TFriendEventPinCreateTopic; + actorId: string; + oldVersion: number; + version: number; + conversationId: string; +}; + +export type TFriendEventPinUnpin = { + topic: TFriendEventPinTopic; + actorId: string; + oldVersion: number; + version: number; + conversationId: string; +}; + +export type TFriendEvent = + | TFriendEventBase + | TFriendEventRequest + | TFriendEventRejectUndo + | TFriendEventSeenRequest + | TFriendEventPinUnpin + | TFriendEventPinCreate; + +export type FriendEvent = + | { + type: + | FriendEventType.ADD + | FriendEventType.REMOVE + | FriendEventType.BLOCK + | FriendEventType.UNBLOCK + | FriendEventType.BLOCK_CALL + | FriendEventType.UNBLOCK_CALL; + data: TFriendEventBase; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.REJECT_REQUEST | FriendEventType.UNDO_REQUEST; + data: TFriendEventRejectUndo; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.REQUEST; + data: TFriendEventRequest; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.SEEN_FRIEND_REQUEST; + data: TFriendEventSeenRequest; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.PIN_CREATE; + data: TFriendEventPinCreate; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.PIN_UNPIN; + data: TFriendEventPinUnpin; + threadId: string; + isSelf: boolean; + } + | { + type: FriendEventType.UNKNOWN; + data: string; + threadId: string; + isSelf: boolean; + }; + +export function initializeFriendEvent(uid: string, data: TFriendEvent, type: FriendEventType): FriendEvent { + if ( + type == FriendEventType.ADD || + type == FriendEventType.REMOVE || + type == FriendEventType.BLOCK || + type == FriendEventType.UNBLOCK || + type == FriendEventType.BLOCK_CALL || + type == FriendEventType.UNBLOCK_CALL + ) { + return { + type, + data: data as TFriendEventBase, + threadId: data as string, + isSelf: ![FriendEventType.ADD, FriendEventType.REMOVE].includes(type), + }; + } else if (type == FriendEventType.REJECT_REQUEST || type == FriendEventType.UNDO_REQUEST) { + const threadId = (data as TFriendEventRejectUndo).toUid; + return { + type, + data: data as TFriendEventRejectUndo, + threadId, + isSelf: (data as TFriendEventRequest).fromUid == uid, + }; + } else if (type == FriendEventType.REQUEST) { + const threadId = (data as TFriendEventRequest).toUid; + return { + type, + data: data as TFriendEventRequest, + threadId, + isSelf: (data as TFriendEventRequest).fromUid == uid, + }; + } else if (type == FriendEventType.SEEN_FRIEND_REQUEST) { + return { + type, + data: data as TFriendEventSeenRequest, + threadId: uid, + isSelf: true, + }; + } else if (type == FriendEventType.PIN_CREATE) { + const threadId = (data as TFriendEventPinCreate).conversationId; + return { + type, + data: data as TFriendEventPinCreate, + threadId, + isSelf: (data as TFriendEventPinCreate).actorId == uid, + }; + } else if (type == FriendEventType.PIN_UNPIN) { + const threadId = (data as TFriendEventPinUnpin).conversationId; + return { + type, + data: data as TFriendEventPinUnpin, + threadId, + isSelf: (data as TFriendEventPinUnpin).actorId == uid, + }; + } else { + return { + type: FriendEventType.UNKNOWN, + data: JSON.stringify(data), + threadId: "", + isSelf: false, + }; + } +} diff --git a/_libs/zca-js/src/models/Group.ts b/_libs/zca-js/src/models/Group.ts new file mode 100644 index 0000000..6eb5f04 --- /dev/null +++ b/_libs/zca-js/src/models/Group.ts @@ -0,0 +1,146 @@ +export type GroupSetting = { + blockName: number; + signAdminMsg: number; + addMemberOnly: number; + setTopicOnly: number; + enableMsgHistory: number; + joinAppr: number; + lockCreatePost: number; + lockCreatePoll: number; + lockSendMsg: number; + lockViewMember: number; + bannFeature: number; + dirtyMedia: number; + banDuration: number; +}; + +export enum GroupTopicType { + Note = 0, + Message = 2, + Poll = 3, +} + +export type GroupTopicNoteParams = { + client_msg_id: string; + global_msg_id: string; + title: string; +}; + +export type GroupTopicTextMessageParams = { + senderUid: string; + senderName: string; + client_msg_id: string; + global_msg_id: string; + msg_type: 1; + title: string; +}; + +export type GroupTopicVoiceMessageParams = GroupTopicImageMessageParams & { + msg_type: 31; +}; + +export type GroupTopicImageMessageParams = GroupTopicTextMessageParams & { + msg_type: 32; + thumb: string; +}; + +export type GroupTopicVideoMessageParams = GroupTopicTextMessageParams & { + msg_type: 44; + thumb: string; +}; + +export type GroupTopicFileMessageParams = GroupTopicTextMessageParams & { + msg_type: 46; + extra: { + fileSize: string; + checksum: string; + checksumSha: unknown; + fileExt: string; + fdata: string; + fType: number; + }; +}; + +export type GroupTopicGifMessageParams = GroupTopicTextMessageParams & { + msg_type: 49; + thumb: string; +}; + +export type GroupTopicMessageParams = + | GroupTopicTextMessageParams + | GroupTopicVoiceMessageParams + | GroupTopicImageMessageParams + | GroupTopicVideoMessageParams + | GroupTopicFileMessageParams + | GroupTopicGifMessageParams; + +export type GroupTopicPollParams = { + pollId: number; + title: string; +}; + +export type GroupTopicOtherParams = { + [key: string]: unknown; +}; + +export type GroupTopic = { + type: GroupTopicType; + color: number; + emoji: string; + startTime: number; + duration: number; + params: GroupTopicNoteParams | GroupTopicMessageParams | GroupTopicPollParams | GroupTopicOtherParams; + id: string; + creatorId: string; + createTime: number; + editorId: string; + editTime: number; + repeat: number; + action: number; +}; + +export enum GroupType { + Group = 1, + Community = 2, +} + +export type GroupCurrentMem = { + id: string; + dName: string; + zaloName: string; + avatar: string; + avatar_25: string; + accountStatus: number; + type: number; +}; + +export type GroupInfo = { + groupId: string; + name: string; + desc: string; + type: GroupType; + creatorId: string; + version: string; + avt: string; + fullAvt: string; + memberIds: string[]; + adminIds: string[]; + currentMems: GroupCurrentMem[]; + updateMems: unknown[]; + admins: unknown[]; + hasMoreMember: number; + subType: number; + totalMember: number; + maxMember: number; + setting: GroupSetting; + createdTime: number; + visibility: number; + globalId: string; + /** + * 1: True, 0: False + */ + e2ee: number; + extraInfo: { + enable_media_store: number; + }; +}; diff --git a/_libs/zca-js/src/models/GroupEvent.ts b/_libs/zca-js/src/models/GroupEvent.ts new file mode 100644 index 0000000..80270dd --- /dev/null +++ b/_libs/zca-js/src/models/GroupEvent.ts @@ -0,0 +1,285 @@ +import type { GroupSetting, GroupTopic } from "./Group.js"; +import type { ReminderGroup } from "./Reminder.js"; + +export enum GroupEventType { + JOIN_REQUEST = "join_request", + JOIN = "join", + LEAVE = "leave", + REMOVE_MEMBER = "remove_member", + BLOCK_MEMBER = "block_member", + + UPDATE_SETTING = "update_setting", + UPDATE = "update", + NEW_LINK = "new_link", + + ADD_ADMIN = "add_admin", + REMOVE_ADMIN = "remove_admin", + + NEW_PIN_TOPIC = "new_pin_topic", + UPDATE_PIN_TOPIC = "update_pin_topic", + REORDER_PIN_TOPIC = "reorder_pin_topic", + + UPDATE_BOARD = "update_board", + REMOVE_BOARD = "remove_board", + + UPDATE_TOPIC = "update_topic", + UNPIN_TOPIC = "unpin_topic", + REMOVE_TOPIC = "remove_topic", + + ACCEPT_REMIND = "accept_remind", + REJECT_REMIND = "reject_remind", + REMIND_TOPIC = "remind_topic", + + UPDATE_AVATAR = "update_avatar", + + UNKNOWN = "unknown", +} + +export type GroupEventUpdateMember = { + id: string; + dName: string; + avatar: string; + type: number; + avatar_25: string; +}; + +export type GroupEventGroupInfo = { + group_link?: string; + link_expired_time?: number; + [key: string]: unknown; +}; + +export type GroupEventExtraData = { + featureId?: number; + field?: string; + [key: string]: unknown; +}; + +export type TGroupEventBase = { + subType: number; + groupId: string; + creatorId: string; + groupName: string; + sourceId: string; + updateMembers: GroupEventUpdateMember[]; + groupSetting: GroupSetting | null; + groupTopic: GroupTopic | null; + info: GroupEventGroupInfo | null; + extraData: GroupEventExtraData | null; + time: string; + avt: string | null; + fullAvt: string | null; + isAdd: number; + hideGroupInfo: number; + version: string; + groupType: number; + clientId?: number; + errorMap?: Record; + e2ee?: number; +}; + +export type TGroupEventJoinRequest = { + uids: string[]; + totalPending: number; + groupId: string; + time: string; +}; + +export type TGroupEventPinTopic = { + oldBoardVersion: number; + boardVersion: number; + topic: GroupTopic; + actorId: string; + groupId: string; +}; + +export type TGroupEventReorderPinTopic = { + oldBoardVersion: number; + actorId: string; + topics: { + topicId: string; + topicType: number; + }[]; + groupId: string; + boardVersion: number; + topic: null; +}; + +export type TGroupEventBoard = { + sourceId: string; + groupName: string; + groupTopic: (GroupTopic | ReminderGroup) & { params: string }; + groupId: string; + creatorId: string; + + subType?: number; + updateMembers?: GroupEventUpdateMember[]; + groupSetting?: GroupSetting; + info?: GroupEventGroupInfo; + extraData?: GroupEventExtraData; + time?: string; + avt?: null; + fullAvt?: null; + isAdd?: number; + hideGroupInfo?: number; + version?: string; + groupType?: number; +}; + +export type TGroupEventRemindRespond = { + topicId: string; + updateMembers: string[]; + groupId: string; + time: string; +}; + +export type TGroupEventRemindTopic = { + msg: string; + editorId: string; + color: string; + emoji: string; + creatorId: string; + editTime: number; + type: number; + duration: number; + group_id: string; + createTime: number; + repeat: number; + startTime: number; + time: number; + remindType: number; +}; + +export type TGroupEvent = + | TGroupEventBase + | TGroupEventJoinRequest + | TGroupEventPinTopic + | TGroupEventReorderPinTopic + | TGroupEventBoard + | TGroupEventRemindRespond + | TGroupEventRemindTopic; + +export type GroupEvent = + | { + type: GroupEventType.JOIN_REQUEST; + data: TGroupEventJoinRequest; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.NEW_PIN_TOPIC | GroupEventType.UNPIN_TOPIC | GroupEventType.UPDATE_PIN_TOPIC; + data: TGroupEventPinTopic; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.REORDER_PIN_TOPIC; + data: TGroupEventReorderPinTopic; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.UPDATE_BOARD | GroupEventType.REMOVE_BOARD; + data: TGroupEventBoard; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.ACCEPT_REMIND | GroupEventType.REJECT_REMIND; + data: TGroupEventRemindRespond; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: GroupEventType.REMIND_TOPIC; + data: TGroupEventRemindTopic; + act: string; + threadId: string; + isSelf: boolean; + } + | { + type: Exclude< + GroupEventType, + | GroupEventType.JOIN_REQUEST + | GroupEventType.NEW_PIN_TOPIC + | GroupEventType.UNPIN_TOPIC + | GroupEventType.UPDATE_PIN_TOPIC + | GroupEventType.REORDER_PIN_TOPIC + | GroupEventType.UPDATE_BOARD + | GroupEventType.REMOVE_BOARD + | GroupEventType.ACCEPT_REMIND + | GroupEventType.REJECT_REMIND + | GroupEventType.REMIND_TOPIC + >; + data: TGroupEventBase; + act: string; + threadId: string; + isSelf: boolean; + }; + +export function initializeGroupEvent(uid: string, data: TGroupEvent, type: GroupEventType, act: string): GroupEvent { + const threadId = "group_id" in data ? data.group_id : data.groupId; + + if (type == GroupEventType.JOIN_REQUEST) { + return { type, act, data: data as TGroupEventJoinRequest, threadId, isSelf: false }; + } else if ( + type == GroupEventType.NEW_PIN_TOPIC || + type == GroupEventType.UNPIN_TOPIC || + type == GroupEventType.UPDATE_PIN_TOPIC + ) { + return { + type, + act, + data: data as TGroupEventPinTopic, + threadId, + isSelf: (data as TGroupEventPinTopic).actorId == uid, + }; + } else if (type == GroupEventType.REORDER_PIN_TOPIC) { + return { + type, + act, + data: data as TGroupEventReorderPinTopic, + threadId, + isSelf: (data as TGroupEventPinTopic).actorId == uid, + }; + } else if (type == GroupEventType.UPDATE_BOARD || type == GroupEventType.REMOVE_BOARD) { + return { + type, + act, + data: data as TGroupEventBoard, + threadId, + isSelf: (data as TGroupEventBoard).sourceId == uid, + }; + } else if (type == GroupEventType.ACCEPT_REMIND || type == GroupEventType.REJECT_REMIND) { + return { + type, + act, + data: data as TGroupEventRemindRespond, + threadId, + isSelf: (data as TGroupEventRemindRespond).updateMembers.some((memberId) => memberId == uid), + }; + } else if (type == GroupEventType.REMIND_TOPIC) { + return { + type, + act, + data: data as TGroupEventRemindTopic, + threadId, + isSelf: (data as TGroupEventRemindTopic).creatorId == uid, + }; + } else { + const baseData = data as TGroupEventBase; + + return { + type, + act, + data: baseData, + threadId, + isSelf: baseData.updateMembers?.some((member) => member.id == uid) || baseData.sourceId == uid, + }; + } +} diff --git a/_libs/zca-js/src/models/Label.ts b/_libs/zca-js/src/models/Label.ts new file mode 100644 index 0000000..605a5dc --- /dev/null +++ b/_libs/zca-js/src/models/Label.ts @@ -0,0 +1,10 @@ +export type LabelData = { + id: number; + text: string; + textKey: string; + conversations: string[]; + color: string; + offset: number; + emoji: string; + createTime: number; +}; diff --git a/_libs/zca-js/src/models/Message.ts b/_libs/zca-js/src/models/Message.ts new file mode 100644 index 0000000..7d996ad --- /dev/null +++ b/_libs/zca-js/src/models/Message.ts @@ -0,0 +1,125 @@ +import { ThreadType } from "./Enum.js"; + +export type TAttachmentContent = { + title: string; + description: string; + href: string; + thumb: string; + childnumber: number; + action: string; + params: string; + type: string; +}; + +export type TOtherContent = { + [key: string]: unknown; +}; + +export type TMessage = { + actionId: string; + msgId: string; + cliMsgId: string; + msgType: string; + uidFrom: string; + idTo: string; + dName: string; + ts: string; + status: number; + content: string | TAttachmentContent | TOtherContent; + notify: string; + ttl: number; + userId: string; + uin: string; + topOut: string; + topOutTimeOut: string; + topOutImprTimeOut: string; + propertyExt: + | { + color: number; + size: number; + type: number; + subType: number; + ext: string; + } + | undefined; + paramsExt: { + countUnread: number; + containType: number; + platformType: number; + }; + cmd: number; + st: number; + at: number; + realMsgId: string; + quote: TQuote | undefined; +}; + +export type TGroupMessage = TMessage & { + mentions: TMention[] | undefined; +}; + +export type TQuote = { + ownerId: string; + cliMsgId: number; + globalMsgId: number; + cliMsgType: number; + ts: number; + msg: string; + attach: string; + fromD: string; + ttl: number; +}; + +export type TMention = { + uid: string; + pos: number; + len: number; + type: 0 | 1; +}; + +export class UserMessage { + type: ThreadType.User = ThreadType.User; + + data: TMessage; + threadId: string; + /** + * true if the message is sent by the logged in account + */ + isSelf: boolean; + + constructor(uid: string, data: TMessage) { + this.data = data; + this.threadId = data.uidFrom == "0" ? data.idTo : data.uidFrom; + this.isSelf = data.uidFrom == "0"; + + if (data.idTo == "0") data.idTo = uid; + if (data.uidFrom == "0") data.uidFrom = uid; + if (data.quote) { + data.quote.ownerId = String(data.quote.ownerId); + } + } +} + +export class GroupMessage { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupMessage; + threadId: string; + /** + * true if the message is sent by the logged in account + */ + isSelf: boolean; + + constructor(uid: string, data: TGroupMessage) { + this.data = data; + this.threadId = data.idTo; + this.isSelf = data.uidFrom == "0"; + + if (data.uidFrom == "0") data.uidFrom = uid; + if (data.quote) { + data.quote.ownerId = String(data.quote.ownerId); + } + } +} + +export type Message = UserMessage | GroupMessage; diff --git a/_libs/zca-js/src/models/ProductCatalog.ts b/_libs/zca-js/src/models/ProductCatalog.ts new file mode 100644 index 0000000..8dd01a4 --- /dev/null +++ b/_libs/zca-js/src/models/ProductCatalog.ts @@ -0,0 +1,17 @@ +export type ProductCatalogItem = { + price: string; + description: string; + /** + * Relative path used to build the product URL. + * + * Example: https://catalog.zalo.me/${path} + */ + path: string; + product_id: string; + product_name: string; + currency_unit: string; + product_photos: string[]; + create_time: number; + catalog_id: string; + owner_id: string; +}; diff --git a/_libs/zca-js/src/models/QuickMessage.ts b/_libs/zca-js/src/models/QuickMessage.ts new file mode 100644 index 0000000..e79d4c3 --- /dev/null +++ b/_libs/zca-js/src/models/QuickMessage.ts @@ -0,0 +1,25 @@ +export type QuickMessage = { + id: number; + keyword: string; + type: number; + createdTime: number; + lastModified: number; + message: { + title: string; + params: string | null; + }; + media: { + items: { + type: number; + photoId: number; + title: string; + width: number; + height: number; + previewThumb: string; + rawUrl: string; + thumbUrl: string; + normalUrl: string; + hdUrl: string; + }[]; + } | null; +}; diff --git a/_libs/zca-js/src/models/Reaction.ts b/_libs/zca-js/src/models/Reaction.ts new file mode 100644 index 0000000..619f584 --- /dev/null +++ b/_libs/zca-js/src/models/Reaction.ts @@ -0,0 +1,92 @@ +export enum Reactions { + HEART = "/-heart", + LIKE = "/-strong", + HAHA = ":>", + WOW = ":o", + CRY = ":-((", + ANGRY = ":-h", + KISS = ":-*", + TEARS_OF_JOY = ":')", + SHIT = "/-shit", + ROSE = "/-rose", + BROKEN_HEART = "/-break", + DISLIKE = "/-weak", + LOVE = ";xx", + CONFUSED = ";-/", + WINK = ";-)", + FADE = "/-fade", + SUN = "/-li", + BIRTHDAY = "/-bd", + BOMB = "/-bome", + OK = "/-ok", + PEACE = "/-v", + THANKS = "/-thanks", + PUNCH = "/-punch", + SHARE = "/-share", + PRAY = "_()_", + NO = "/-no", + BAD = "/-bad", + LOVE_YOU = "/-loveu", + SAD = "--b", + VERY_SAD = ":((", + COOL = "x-)", + NERD = "8-)", + BIG_SMILE = ";-d", + SUNGLASSES = "b-)", + NEUTRAL = ":--|", + SAD_FACE = "p-(", + BYE = ":-bye", + SLEEPY = "|-)", + WIPE = ":wipe", + DIG = ":-dig", + ANGUISH = "&-(", + HANDCLAP = ":handclap", + ANGRY_FACE = ">-|", + F_CHAIR = ":-f", + L_CHAIR = ":-l", + R_CHAIR = ":-r", + SILENT = ";-x", + SURPRISE = ":-o", + EMBARRASSED = ";-s", + AFRAID = ";-a", + SAD2 = ":-<", + BIG_LAUGH = ":))", + RICH = "$-)", + BEER = "/-beer", + NONE = "", // more... +} + +export type TReaction = { + actionId: string; + msgId: string; + cliMsgId: string; + msgType: string; + uidFrom: string; + idTo: string; + dName?: string; + content: { + rMsg: { gMsgID: string; cMsgID: string; msgType: number }[]; + rIcon: Reactions; + rType: number; + source: number; + }; + ts: string; + ttl: number; +}; + +export class Reaction { + data: TReaction; + threadId: string; + isSelf: boolean; + isGroup: boolean; + + constructor(uid: string, data: TReaction, isGroup: boolean) { + this.data = data; + this.threadId = isGroup || data.uidFrom == "0" ? data.idTo : data.uidFrom; + this.isSelf = data.uidFrom == "0"; + this.isGroup = isGroup; + + if (data.idTo == "0") data.idTo = uid; + if (data.uidFrom == "0") data.uidFrom = uid; + } +} diff --git a/_libs/zca-js/src/models/Reminder.ts b/_libs/zca-js/src/models/Reminder.ts new file mode 100644 index 0000000..3e1ea33 --- /dev/null +++ b/_libs/zca-js/src/models/Reminder.ts @@ -0,0 +1,53 @@ +export enum ReminderRepeatMode { + None = 0, + Daily = 1, + Weekly = 2, + Monthly = 3, +} + +export type ReminderUser = { + creatorUid: string; + toUid: string; + emoji: string; + color: number; + reminderId: string; + createTime: number; + repeat: ReminderRepeatMode; + startTime: number; + editTime: number; + endTime: number; + params: { + title: string; + setTitle: boolean; + }; + type: number; +}; + +export type ReminderGroup = { + editorId: string; + emoji: string; + color: number; + groupId: string; + creatorId: string; + editTime: number; + eventType: number; + responseMem: { + rejectMember: number; + myResp: number; + acceptMember: number; + }; + params: { + title: string; + setTitle?: boolean; + }; + type: number; + duration: number; + repeatInfo: { + list_ts: unknown[]; + } | null; + repeatData: unknown[]; + createTime: number; + repeat: ReminderRepeatMode; + startTime: number; + id: string; +}; diff --git a/_libs/zca-js/src/models/SeenMessage.ts b/_libs/zca-js/src/models/SeenMessage.ts new file mode 100644 index 0000000..c337d7a --- /dev/null +++ b/_libs/zca-js/src/models/SeenMessage.ts @@ -0,0 +1,43 @@ +import { ThreadType } from "./Enum.js"; + +export type TUserSeenMessage = { + idTo: string; + msgId: string; + realMsgId: string; +}; + +export type TGroupSeenMessage = { + msgId: string; + groupId: string; + seenUids: string[]; +}; + +export class UserSeenMessage { + type: ThreadType.User = ThreadType.User; + + data: TUserSeenMessage; + threadId: string; + isSelf: false; + + constructor(data: TUserSeenMessage) { + this.data = data; + this.threadId = data.idTo; + this.isSelf = false; + } +} + +export class GroupSeenMessage { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupSeenMessage; + threadId: string; + isSelf: boolean; + + constructor(uid: string, data: TGroupSeenMessage) { + this.data = data; + this.threadId = data.groupId; + this.isSelf = data.seenUids.includes(uid); + } +} + +export type SeenMessage = UserSeenMessage | GroupSeenMessage; diff --git a/_libs/zca-js/src/models/Sticker.ts b/_libs/zca-js/src/models/Sticker.ts new file mode 100644 index 0000000..2f0c5b4 --- /dev/null +++ b/_libs/zca-js/src/models/Sticker.ts @@ -0,0 +1,28 @@ +export type StickerDetail = { + id: number; + cateId: number; + type: number; + text: string; + uri: string; + fkey: number; + status: number; + stickerUrl: string; + stickerSpriteUrl: string; + stickerWebpUrl: string | null; + totalFrames: number; + duration: number; + effectId: number; + checksum: string; + ext: number; + source: number; + fss: unknown; + fssInfo: unknown; + version: number; + extInfo: unknown; +}; + +export type StickerBasic = { + type: number; + cate_id: number; + sticker_id: number; +}; diff --git a/_libs/zca-js/src/models/Typing.ts b/_libs/zca-js/src/models/Typing.ts new file mode 100644 index 0000000..275f896 --- /dev/null +++ b/_libs/zca-js/src/models/Typing.ts @@ -0,0 +1,41 @@ +import { ThreadType } from "./Enum.js"; + +export type TTyping = { + uid: string; + ts: string; + isPC: 0 | 1; +}; + +export type TGroupTyping = TTyping & { + gid: string; +}; + +export class UserTyping { + type: ThreadType.User = ThreadType.User; + + data: TTyping; + threadId: string; + isSelf: false; + + constructor(data: TTyping) { + this.data = data; + this.threadId = data.uid; + this.isSelf = false; + } +} + +export class GroupTyping { + type: ThreadType.Group = ThreadType.Group; + + data: TGroupTyping; + threadId: string; + isSelf: false; + + constructor(data: TGroupTyping) { + this.data = data; + this.threadId = data.gid; + this.isSelf = false; + } +} + +export type Typing = UserTyping | GroupTyping; diff --git a/_libs/zca-js/src/models/Undo.ts b/_libs/zca-js/src/models/Undo.ts new file mode 100644 index 0000000..cadd3fc --- /dev/null +++ b/_libs/zca-js/src/models/Undo.ts @@ -0,0 +1,45 @@ +export type TUndoContent = { + globalMsgId: number; + cliMsgId: number; + deleteMsg: number; + srcId: number; + destId: number; +}; + +export type TUndo = { + actionId: string; + msgId: string; + cliMsgId: string; + msgType: string; + uidFrom: string; + idTo: string; + dName: string; + ts: string; + status: number; + content: TUndoContent; + notify: string; + ttl: number; + userId: string; + uin: string; + cmd: number; + st: number; + at: number; + realMsgId: string; +}; + +export class Undo { + data: TUndo; + threadId: string; + isSelf: boolean; + isGroup: boolean; + + constructor(uid: string, data: TUndo, isGroup: boolean) { + this.data = data; + this.threadId = isGroup || data.uidFrom == "0" ? data.idTo : data.uidFrom; + this.isSelf = data.uidFrom == "0"; + this.isGroup = isGroup; + + if (data.idTo == "0") data.idTo = uid; + if (data.uidFrom == "0") data.uidFrom = uid; + } +} diff --git a/_libs/zca-js/src/models/User.ts b/_libs/zca-js/src/models/User.ts new file mode 100644 index 0000000..03f52f1 --- /dev/null +++ b/_libs/zca-js/src/models/User.ts @@ -0,0 +1,65 @@ +import type { Gender } from "./Enum.js"; +import type { ZBusinessPackage } from "./ZBusiness.js"; + +export type User = { + userId: string; + username: string; + displayName: string; + zaloName: string; + avatar: string; + bgavatar: string; + cover: string; + gender: Gender; + dob: number; + sdob: string; + status: string; + phoneNumber: string; + isFr: number; + isBlocked: number; + lastActionTime: number; + lastUpdateTime: number; + isActive: number; + key: number; + type: number; + isActivePC: number; + isActiveWeb: number; + isValid: number; + userKey: string; + accountStatus: number; + oaInfo: unknown; + user_mode: number; + globalId: string; + bizPkg: ZBusinessPackage; + createdTs: number; + oa_status: unknown; +}; + +export type UserBasic = { + avatar: string; + cover: string; + status: string; + gender: Gender; + dob: number; + sdob: string; + globalId: string; + bizPkg: ZBusinessPackage; + uid: string; + zalo_name: string; + display_name: string; +}; + +export type UserSetting = { + add_friend_via_contact: number; + display_on_recommend_friend: number; + add_friend_via_group: number; + add_friend_via_qr: number; + quick_message_status: number; + show_online_status: boolean; + accept_stranger_call: number; + archived_chat_status: number; + receive_message: number; + add_friend_via_phone: number; + display_seen_status: number; + view_birthday: number; + setting_2FA_status: number; +}; diff --git a/_libs/zca-js/src/models/ZBusiness.ts b/_libs/zca-js/src/models/ZBusiness.ts new file mode 100644 index 0000000..9c1ea24 --- /dev/null +++ b/_libs/zca-js/src/models/ZBusiness.ts @@ -0,0 +1,40 @@ +export type ZBusinessPackage = { + label?: Record | null; + pkgId: number; // @TODO: what is this? +}; + +export enum BusinessCategory { + Other = 0, + RealEstate = 1, + TechnologyAndDevices = 2, + TravelAndHospitality = 3, + EducationAndTraining = 4, + ShoppingAndRetail = 5, + CosmeticsAndBeauty = 6, + RestaurantAndCafe = 7, + AutoAndMotorbike = 8, + FashionAndApparel = 9, + FoodAndBeverage = 10, + MediaAndEntertainment = 11, + InternalCommunications = 12, + Transportation = 13, + Telecommunications = 14, +} + +export const BusinessCategoryName: Record = { + [BusinessCategory.Other]: "Dแป‹ch vแปฅ khรกc (Khรดng hiแปƒn thแป‹)", + [BusinessCategory.RealEstate]: "Bแบฅt ฤ‘แป™ng sแบฃn", + [BusinessCategory.TechnologyAndDevices]: "Cรดng nghแป‡ & Thiแบฟt bแป‹", + [BusinessCategory.TravelAndHospitality]: "Du lแป‹ch & Lฦฐu trรบ", + [BusinessCategory.EducationAndTraining]: "Giรกo dแปฅc & ฤร o tแบกo", + [BusinessCategory.ShoppingAndRetail]: "Mua sแบฏm & Bรกn lแบป", + [BusinessCategory.CosmeticsAndBeauty]: "Mแปน phแบฉm & Lร m ฤ‘แบนp", + [BusinessCategory.RestaurantAndCafe]: "Nhร  hร ng & Quรกn", + [BusinessCategory.AutoAndMotorbike]: "ร” tรด & Xe mรกy", + [BusinessCategory.FashionAndApparel]: "Thแปi trang & May mแบทc", + [BusinessCategory.FoodAndBeverage]: "Thแปฑc phแบฉm & ฤแป“ uแป‘ng", + [BusinessCategory.MediaAndEntertainment]: "Truyแปn thรดng & Giแบฃi trรญ", + [BusinessCategory.InternalCommunications]: "Truyแปn thรดng nแป™i bแป™", + [BusinessCategory.Transportation]: "Vแบญn tแบฃi", + [BusinessCategory.Telecommunications]: "Viแป…n thรดng", +}; diff --git a/_libs/zca-js/src/models/index.ts b/_libs/zca-js/src/models/index.ts new file mode 100644 index 0000000..46f087d --- /dev/null +++ b/_libs/zca-js/src/models/index.ts @@ -0,0 +1,21 @@ +export * from "./Attachment.js"; +export * from "./AutoReply.js"; +export * from "./Board.js"; +export * from "./Catalog.js"; +export * from "./DeliveredMessage.js"; +export * from "./Enum.js"; +export * from "./FriendEvent.js"; +export * from "./Group.js"; +export * from "./GroupEvent.js"; +export * from "./Message.js"; +export * from "./ProductCatalog.js"; +export * from "./QuickMessage.js"; +export * from "./Reaction.js"; +export * from "./Reminder.js"; +export * from "./SeenMessage.js"; +export * from "./Typing.js"; +export * from "./Undo.js"; +export * from "./User.js"; +export * from "./ZBusiness.js"; +export * from "./Label.js"; +export * from "./Sticker.js"; diff --git a/_libs/zca-js/src/update.ts b/_libs/zca-js/src/update.ts new file mode 100644 index 0000000..7a8e9d8 --- /dev/null +++ b/_libs/zca-js/src/update.ts @@ -0,0 +1,32 @@ +import { compare } from "semver"; +import { isBun, logger } from "./utils.js"; +import type { ContextBase } from "./context.js"; + +const VERSION = "2.1.2"; +const NPM_REGISTRY = "https://registry.npmjs.org/zca-js"; + +export async function checkUpdate(ctx: ContextBase) { + if (!ctx.options.checkUpdate) return; + + const _options = { + ...(isBun + ? { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + proxy: ctx.options.agent?.proxy?.href, + } + : { agent: ctx.options.agent }), + }; + const response = await ctx.options.polyfill(NPM_REGISTRY, _options as RequestInit).catch(() => null); + if (!response || !response.ok) return; + + const data = await response.json().catch(() => null); + if (!data) return; + + const latestVersion = data["dist-tags"].latest; + if (compare(VERSION, latestVersion) === -1) { + logger(ctx).info(`A new version of zca-js is available: ${latestVersion}`); + } else { + logger(ctx).info("zca-js is up to date"); + } +} diff --git a/_libs/zca-js/src/utils.ts b/_libs/zca-js/src/utils.ts new file mode 100644 index 0000000..6fcbeb7 --- /dev/null +++ b/_libs/zca-js/src/utils.ts @@ -0,0 +1,791 @@ +import cryptojs from "crypto-js"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import pako from "pako"; +import SparkMD5 from "spark-md5"; +import toughCookie from "tough-cookie"; +import JSONBig from "json-bigint"; + +import { isContextSession, type ContextSession, type ContextBase } from "./context.js"; +import { ZaloApiError, ZaloApiMissingImageMetadataGetter } from "./Errors/index.js"; +import { FriendEventType } from "./models/FriendEvent.js"; +import { GroupEventType } from "./models/GroupEvent.js"; +import type { API } from "./zalo.js"; +import type { AttachmentSource } from "./models/Attachment.js"; + +export const isBun = typeof Bun !== "undefined"; + +export function hasOwn(obj: Record, key: string): key is keyof typeof obj { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +/** + * Get signed key for API requests. + * + * @param type + * @param params + * @returns MD5 hash + * + */ +export function getSignKey(type: string, params: Record) { + const n = []; + for (const s in params) { + if (hasOwn(params, s)) { + n.push(s); + } + } + + n.sort(); + let a = "zsecure" + type; + for (let s = 0; s < n.length; s++) a += params[n[s]]; + return cryptojs.MD5(a).toString(); +} + +/** + * + * @param baseURL + * @param params + * @param apiVersion automatically add zalo api version to url params + * @returns + * + */ +export function makeURL( + ctx: ContextBase, + baseURL: string, + params: Record = {}, + apiVersion: boolean = true, +) { + const url = new URL(baseURL); + for (const key in params) { + if (hasOwn(params, key)) { + url.searchParams.append(key, params[key].toString()); + } + } + + if (apiVersion) { + if (!url.searchParams.has("zpw_ver")) url.searchParams.set("zpw_ver", ctx.API_VERSION.toString()); + if (!url.searchParams.has("zpw_type")) url.searchParams.set("zpw_type", ctx.API_TYPE.toString()); + } + + return url.toString(); +} + +export class ParamsEncryptor { + private zcid: string | null = null; + private enc_ver: string; + private zcid_ext: string; + private encryptKey: string | null; + constructor({ type, imei, firstLaunchTime }: { type: number; imei: string; firstLaunchTime: number }) { + this.enc_ver = "v2"; + this.zcid = null; + this.encryptKey = null; + + this.createZcid(type, imei, firstLaunchTime); + this.zcid_ext = ParamsEncryptor.randomString(); + this.createEncryptKey(); + } + + getEncryptKey() { + if (!this.encryptKey) throw new ZaloApiError("getEncryptKey: didn't create encryptKey yet"); + return this.encryptKey; + } + + createZcid(type: number, imei: string, firstLaunchTime: number) { + if (!type || !imei || !firstLaunchTime) throw new ZaloApiError("createZcid: missing params"); + const msg = `${type},${imei},${firstLaunchTime}`; + const s = ParamsEncryptor.encodeAES("3FC4F0D2AB50057BCE0D90D9187A22B1", msg, "hex", true); + this.zcid = s; + } + + createEncryptKey(e = 0) { + const t = (e: string, t: string) => { + const { even: n } = ParamsEncryptor.processStr(e), + { even: a, odd: s } = ParamsEncryptor.processStr(t); + if (!n || !a || !s) return !1; + const i = n.slice(0, 8).join("") + a.slice(0, 12).join("") + s.reverse().slice(0, 12).join(""); + return (this.encryptKey = i), !0; + }; + if (!this.zcid || !this.zcid_ext) throw new ZaloApiError("createEncryptKey: zcid or zcid_ext is null"); + try { + const n = cryptojs.MD5(this.zcid_ext).toString().toUpperCase(); + if (t(n, this.zcid) || !(e < 3)) return !1; + this.createEncryptKey(e + 1); + } catch { + if (e < 3) this.createEncryptKey(e + 1); + } + return !0; + } + + getParams() { + return this.zcid + ? { + zcid: this.zcid, + zcid_ext: this.zcid_ext, + enc_ver: this.enc_ver, + } + : null; + } + + static processStr(e: string) { + if (!e || "string" != typeof e) + return { + even: null, + odd: null, + }; + const [t, n] = [...e].reduce((e, t, n) => (e[n % 2].push(t), e), [[], []] as string[][]); + return { + even: t, + odd: n, + }; + } + + static randomString(e?: number, t?: number) { + const n = e || 6, + a = t && e && t > e ? t : 12; + let s = Math.floor(Math.random() * (a - n + 1)) + n; + if (s > 12) { + let e = ""; + for (; s > 0; ) { + e += Math.random() + .toString(16) + .substr(2, s > 12 ? 12 : s); + s -= 12; + } + return e; + } + return Math.random().toString(16).substr(2, s); + } + + static encodeAES(e: string, message: string, type: "hex" | "base64", uppercase: boolean, s = 0): string | null { + if (!message) return null; + try { + { + const encoder = "hex" == type ? cryptojs.enc.Hex : cryptojs.enc.Base64; + const key = cryptojs.enc.Utf8.parse(e); + + const cfg = { + words: [0, 0, 0, 0], + sigBytes: 16, + } as cryptojs.lib.WordArray; + const encrypted = cryptojs.AES.encrypt(message, key, { + iv: cfg, + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }).ciphertext.toString(encoder); + + return uppercase ? encrypted.toUpperCase() : encrypted; + } + } catch { + return s < 3 ? ParamsEncryptor.encodeAES(e, message, type, uppercase, s + 1) : null; + } + } +} + +export function decryptResp(key: string, data: string): Record | null | string { + let n = null; + try { + n = decodeRespAES(key, data); + const parsed = JSON.parse(n); + return parsed; + } catch { + return n; + } +} + +function decodeRespAES(key: string, data: string) { + data = decodeURIComponent(data); + const parsedKey = cryptojs.enc.Utf8.parse(key); + const n = { + words: [0, 0, 0, 0], + sigBytes: 16, + } as cryptojs.lib.WordArray; + + return cryptojs.AES.decrypt( + { + ciphertext: cryptojs.enc.Base64.parse(data), + } as cryptojs.lib.CipherParams, + parsedKey, + { + iv: n, + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }, + ).toString(cryptojs.enc.Utf8); +} + +export function decodeBase64ToBuffer(data: string) { + return Buffer.from(data, "base64"); +} + +export function decodeUnit8Array(data: Uint8Array) { + try { + return new TextDecoder().decode(data); + } catch { + return null; + } +} + +export function encodeAES(secretKey: string, data: cryptojs.lib.WordArray | string, t = 0): string | null { + try { + const key = cryptojs.enc.Base64.parse(secretKey); + return cryptojs.AES.encrypt(data, key, { + iv: cryptojs.enc.Hex.parse("00000000000000000000000000000000"), + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }).ciphertext.toString(cryptojs.enc.Base64); + } catch { + return t < 3 ? encodeAES(secretKey, data, t + 1) : null; + } +} + +export function decodeAES(secretKey: string, data: string, t = 0): string | null { + try { + data = decodeURIComponent(data); + const key = cryptojs.enc.Base64.parse(secretKey); + return cryptojs.AES.decrypt( + { + ciphertext: cryptojs.enc.Base64.parse(data), + } as cryptojs.lib.CipherParams, + key, + { + iv: cryptojs.enc.Hex.parse("00000000000000000000000000000000"), + mode: cryptojs.mode.CBC, + padding: cryptojs.pad.Pkcs7, + }, + ).toString(cryptojs.enc.Utf8); + } catch { + return t < 3 ? decodeAES(secretKey, data, t + 1) : null; + } +} + +export async function getDefaultHeaders(ctx: ContextBase, origin: string = "https://chat.zalo.me") { + if (!ctx.cookie) throw new ZaloApiError("Cookie is not available"); + if (!ctx.userAgent) throw new ZaloApiError("User agent is not available"); + + return { + Accept: "application/json, text/plain, */*", + "Accept-Encoding": "gzip, deflate, br, zstd", + "Accept-Language": "en-US,en;q=0.9", + "content-type": "application/x-www-form-urlencoded", + Cookie: await ctx.cookie.getCookieString(origin), + Origin: "https://chat.zalo.me", + Referer: "https://chat.zalo.me/", + "User-Agent": ctx.userAgent, + }; +} + +export async function request(ctx: ContextBase, url: string, options?: RequestInit, raw = false) { + if (!ctx.cookie) ctx.cookie = new toughCookie.CookieJar(); + const origin = new URL(url).origin; + + const defaultHeaders = await getDefaultHeaders(ctx, origin); + if (!raw) { + if (options) { + options.headers = Object.assign(defaultHeaders, options.headers || {}); + } else options = { headers: defaultHeaders }; + } + + const _options = { + ...(options ?? {}), + ...(isBun ? { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + proxy: ctx.options.agent?.proxy?.href + } : { agent: ctx.options.agent }), + }; + + const response = await ctx.options.polyfill(url, _options); + const setCookieRaw = response.headers.get("set-cookie"); + if (setCookieRaw && !raw) { + // Use getSetCookie() (Node.js 18+) to properly parse multi-cookie headers. + // The split(", ") method breaks cookies containing Expires dates with commas + // (e.g., "Expires=Wed, 18 Mar 2026..."), causing critical cookies like + // zpsid and zpw_sek to be lost during QR login flow. + let cookieStrings: string[]; + if (typeof response.headers.getSetCookie === "function") { + cookieStrings = response.headers.getSetCookie(); + } else { + cookieStrings = setCookieRaw.split(", "); + } + for (const cookie of cookieStrings) { + const parsed = toughCookie.Cookie.parse(cookie); + try { + if (parsed) await ctx.cookie.setCookie(parsed, parsed.domain != "zalo.me" ? `https://${parsed.domain}` : origin); + } catch (error: unknown) { + logger(ctx).error(error); + } + } + } + + const redirectURL = response.headers.get("location"); + + if (redirectURL) { + const redirectOptions = { ...options }; + redirectOptions.method = "GET"; + if (!raw) { + redirectOptions.headers = new Headers(redirectOptions.headers); + redirectOptions.headers.set("Referer", "https://id.zalo.me/"); + } + return await request(ctx, redirectURL, redirectOptions); + } + + return response; +} + +export async function getImageMetaData(ctx: ContextBase, filePath: string) { + if (!ctx.options.imageMetadataGetter) { + throw new ZaloApiMissingImageMetadataGetter(); + } + + const imageData = await ctx.options.imageMetadataGetter(filePath); + if (!imageData) { + throw new ZaloApiError("Failed to get image metadata"); + } + + const fileName = filePath.split("/").pop()!; + + return { + fileName, + totalSize: imageData.size, + width: imageData.width, + height: imageData.height, + }; +} + +export async function getFileSize(filePath: string) { + return fs.promises.stat(filePath).then((s) => s.size); +} + +export async function getGifMetaData(ctx: ContextBase, filePath: string) { + if (!ctx.options.imageMetadataGetter) { + throw new ZaloApiMissingImageMetadataGetter(); + } + + const gifData = await ctx.options.imageMetadataGetter(filePath); + if (!gifData) { + throw new ZaloApiError("Failed to get gif metadata"); + } + + const fileName = path.basename(filePath); + + return { + fileName, + totalSize: gifData.size, + width: gifData.width, + height: gifData.height, + }; +} + +export async function decodeEventData(parsed: Record, cipherKey?: string) { + if (typeof parsed.data !== "string") + throw new ZaloApiError(`Invalid data, expected string but got ${typeof parsed.data}`); + if (typeof parsed.encrypt !== "number") + throw new ZaloApiError(`Invalid encrypt type, expected number but got ${typeof parsed.encrypt}`); + if (parsed.encrypt < 0 || parsed.encrypt > 3) + throw new ZaloApiError(`Invalid encrypt type, expected 0-3 but got ${parsed.encrypt}`); + + const rawData = parsed.data; + const encryptType = parsed.encrypt as 0 | 1 | 2 | 3; + + if (encryptType === 0) return JSON.parse(rawData); + + const decodedBuffer = decodeBase64ToBuffer(encryptType === 1 ? rawData : decodeURIComponent(rawData)); + + let decryptedBuffer: ArrayBuffer | Buffer = decodedBuffer; + if (encryptType !== 1) { + if (cipherKey && decodedBuffer.length >= 48) { + const algorithm = { + name: "AES-GCM", + iv: decodedBuffer.subarray(0, 16), + tagLength: 128, + additionalData: decodedBuffer.subarray(16, 32), + }; + const dataSource = decodedBuffer.subarray(32); + + const cryptoKey = await crypto.subtle.importKey("raw", decodeBase64ToBuffer(cipherKey), algorithm, false, [ + "decrypt", + ]); + decryptedBuffer = await crypto.subtle.decrypt(algorithm, cryptoKey, dataSource); + } else { + throw new ZaloApiError("Invalid data length or missing cipher key"); + } + } + + const decompressedBuffer = + encryptType === 3 ? new Uint8Array(decryptedBuffer) : pako.inflate(decryptedBuffer as ArrayBuffer); + const decodedData = decodeUnit8Array(decompressedBuffer); + + if (!decodedData) return; + return JSONBig.parse(decodedData); +} + +export async function getMd5LargeFileObject(source: AttachmentSource, fileSize: number) { + const buffer = typeof source == "string" ? await fs.promises.readFile(source) : source.data; + return new Promise<{ + currentChunk: number; + data: string; + }>((resolve) => { + let currentChunk = 0; + const chunkSize = 2097152, // Read in chunks of 2MB + chunks = Math.ceil(fileSize / chunkSize), + spark = new SparkMD5.ArrayBuffer(); + + function loadNext() { + const start = currentChunk * chunkSize, + end = start + chunkSize >= fileSize ? fileSize : start + chunkSize; + + spark.append(new Uint8Array(buffer.subarray(start, end)).buffer as ArrayBuffer); + currentChunk++; + + if (currentChunk < chunks) { + loadNext(); + } else { + resolve({ + currentChunk, + data: spark.end(), + }); + } + } + + loadNext(); + }); +} + +export const logger = (ctx: { options: { logging?: boolean } }) => ({ + verbose: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[35m๐Ÿš€ VERBOSE\x1b[0m", ...args); + }, + info: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[34mINFO\x1b[0m", ...args); + }, + warn: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[33mWARN\x1b[0m", ...args); + }, + error: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[31mERROR\x1b[0m", ...args); + }, + success: (...args: unknown[]) => { + if (ctx.options.logging) console.log("\x1b[32mSUCCESS\x1b[0m", ...args); + }, + timestamp: (...args: unknown[]) => { + const now = new Date().toISOString(); + if (ctx.options.logging) console.log(`\x1b[90m[${now}]\x1b[0m`, ...args); + }, +}); + +export function getClientMessageType(msgType: string) { + if (msgType === "webchat") return 1; + if (msgType === "chat.voice") return 31; + if (msgType === "chat.photo") return 32; + if (msgType === "chat.sticker") return 36; + if (msgType === "chat.doodle") return 37; + if (msgType === "chat.recommended") return 38; + + if (msgType === "chat.link") return 38; // don't know || if (msgType === "chat.link") return 1; + if (msgType === "chat.video.msg") return 44; // not sure + + if (msgType === "share.file") return 46; + if (msgType === "chat.gif") return 49; + if (msgType === "chat.location.new") return 43; + + return 1; +} + +export function strPadLeft(e: number | string, t: string, n: number) { + const a = (e = "" + e).length; + return a === n ? e : a > n ? e.slice(-n) : t.repeat(n - a) + e; +} + +export function formatTime(format: string, timestamp: number = Date.now()): string { + const date = new Date(timestamp); + + // using lib Intl + const options: Intl.DateTimeFormatOptions = { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + // hour12: false, // true or false is same <(") + }; + + const formatted = new Intl.DateTimeFormat("vi-VN", options).format(date); + + if (format.includes("%H") || format.includes("%d")) { + return format + .replace("%H", date.getHours().toString().padStart(2, "0")) + .replace("%M", date.getMinutes().toString().padStart(2, "0")) + .replace("%S", date.getSeconds().toString().padStart(2, "0")) + .replace("%d", date.getDate().toString().padStart(2, "0")) + .replace("%m", (date.getMonth() + 1).toString().padStart(2, "0")) + .replace("%Y", date.getFullYear().toString()); + } + + return formatted; +} + +export function getFullTimeFromMillisecond(e: number) { + const t = new Date(e); + return ( + strPadLeft(t.getHours(), "0", 2) + + ":" + + strPadLeft(t.getMinutes(), "0", 2) + + " " + + strPadLeft(t.getDate(), "0", 2) + + "/" + + strPadLeft(t.getMonth() + 1, "0", 2) + + "/" + + t.getFullYear() + ); +} + +export function getFileExtension(e: string) { + return path.extname(e).slice(1); +} + +export function getFileName(e: string) { + return path.basename(e); +} + +export function removeUndefinedKeys(e: Record) { + for (const t in e) if (e[t] === undefined) delete e[t]; + return e; +} + +export function getGroupEventType(act: string) { + if (act == "join_request") return GroupEventType.JOIN_REQUEST; + if (act == "join") return GroupEventType.JOIN; + if (act == "leave") return GroupEventType.LEAVE; + if (act == "remove_member") return GroupEventType.REMOVE_MEMBER; + if (act == "block_member") return GroupEventType.BLOCK_MEMBER; + if (act == "update_setting") return GroupEventType.UPDATE_SETTING; + if (act == "update_avatar") return GroupEventType.UPDATE_AVATAR; + if (act == "update") return GroupEventType.UPDATE; + if (act == "new_link") return GroupEventType.NEW_LINK; + if (act == "add_admin") return GroupEventType.ADD_ADMIN; + if (act == "remove_admin") return GroupEventType.REMOVE_ADMIN; + + if (act == "new_pin_topic") return GroupEventType.NEW_PIN_TOPIC; + if (act == "update_pin_topic") return GroupEventType.UPDATE_PIN_TOPIC; + if (act == "update_topic") return GroupEventType.UPDATE_TOPIC; + if (act == "update_board") return GroupEventType.UPDATE_BOARD; + if (act == "remove_board") return GroupEventType.REMOVE_BOARD; + if (act == "reorder_pin_topic") return GroupEventType.REORDER_PIN_TOPIC; + if (act == "unpin_topic") return GroupEventType.UNPIN_TOPIC; + if (act == "remove_topic") return GroupEventType.REMOVE_TOPIC; + if (act == "accept_remind") return GroupEventType.ACCEPT_REMIND; + if (act == "reject_remind") return GroupEventType.REJECT_REMIND; + if (act == "remind_topic") return GroupEventType.REMIND_TOPIC; + + return GroupEventType.UNKNOWN; +} + +export function getFriendEventType(act: string) { + if (act == "add") return FriendEventType.ADD; + if (act == "remove") return FriendEventType.REMOVE; + if (act == "block") return FriendEventType.BLOCK; + if (act == "unblock") return FriendEventType.UNBLOCK; + if (act == "block_call") return FriendEventType.BLOCK_CALL; + if (act == "unblock_call") return FriendEventType.UNBLOCK_CALL; + + if (act == "req_v2") return FriendEventType.REQUEST; + if (act == "reject") return FriendEventType.REJECT_REQUEST; + if (act == "undo_req") return FriendEventType.UNDO_REQUEST; + + if (act == "seen_fr_req") return FriendEventType.SEEN_FRIEND_REQUEST; + + if (act == "pin_unpin") return FriendEventType.PIN_UNPIN; + if (act == "pin_create") return FriendEventType.PIN_CREATE; + + return FriendEventType.UNKNOWN; +} + +type ZaloResponse = { + data: T | null; + error: { + message: string; + code?: number; + } | null; +}; + +export async function handleZaloResponse(ctx: ContextSession, response: Response, isEncrypted = true) { + const result: ZaloResponse = { + data: null, + error: null, + }; + + if (!response.ok) { + result.error = { + message: "Request failed with status code " + response.status, + }; + return result; + } + + try { + const jsonData: { + error_code: number; + error_message: string; + data: string; + } = await response.json(); + + if (jsonData.error_code != 0) { + result.error = { + message: jsonData.error_message, + code: jsonData.error_code, + }; + return result; + } + + const decodedData: { + error_code: number; + error_message: string; + data: T; + } = isEncrypted ? JSON.parse(decodeAES(ctx.secretKey!, jsonData.data)!) : jsonData; + + if (decodedData.error_code != 0) { + result.error = { + message: decodedData.error_message, + code: decodedData.error_code, + }; + return result; + } + + result.data = decodedData.data; + } catch (error) { + logger(ctx).error("Failed to parse response data:", error); + result.error = { + message: "Failed to parse response data", + }; + } + + return result; +} + +export async function resolveResponse( + ctx: ContextSession, + res: Response, + cb?: (result: ZaloResponse) => T, + isEncrypted?: boolean, +) { + const result = await handleZaloResponse(ctx, res, isEncrypted); + if (result.error) throw new ZaloApiError(result.error.message, result.error.code); + if (cb) return cb(result); + + return result.data as T; +} + +export type FactoryUtils = { + makeURL: ( + baseURL: string, + params?: Record, + apiVersion?: boolean, + ) => ReturnType; + encodeAES: (data: cryptojs.lib.WordArray | string, t?: number) => ReturnType; + request: (url: string, options?: RequestInit, raw?: boolean) => ReturnType; + logger: ReturnType; + resolve: ( + res: Response, + cb?: (result: ZaloResponse) => T, + isEncrypted?: boolean, + ) => ReturnType>; +}; + +export function apiFactory() { + return ) => unknown>(callback: K) => { + return (ctx: ContextBase, api: API) => { + if (!isContextSession(ctx)) throw new ZaloApiError("Invalid context " + JSON.stringify(ctx, null, 2)); + + const utils = { + makeURL(baseURL: string, params?: Record, apiVersion?: boolean) { + return makeURL(ctx, baseURL, params, apiVersion); + }, + encodeAES(data: cryptojs.lib.WordArray | string, t?: number) { + return encodeAES(ctx.secretKey, data, t); + }, + request(url: string, options?: RequestInit, raw?: boolean) { + return request(ctx, url, options, raw); + }, + logger: logger(ctx), + resolve: (res: Response, cb?: (result: ZaloResponse) => T, isEncrypted?: boolean) => + resolveResponse(ctx, res, cb, isEncrypted), + }; + + return callback(api, ctx, utils) as ReturnType; + }; + }; +} + +export function generateZaloUUID(userAgent: string) { + return crypto.randomUUID() + "-" + cryptojs.MD5(userAgent).toString(); +} + +/** + * Encrypts a 4-digit PIN to a 32-character hex string + * @param pin 4-digit PIN number + * @returns 32-character hex string + */ +export function encryptPin(pin: string): string { + return crypto.createHash("md5").update(pin).digest("hex"); +} + +/** + * Decrypts a 32-character hex string back to 4-digit PIN + * Note: This is a one-way hash, so we can only verify if a PIN matches the hash + * @param encryptedPin 32-character hex string + * @param pin 4-digit PIN to verify + * @returns true if the PIN matches the hash + * + * @example + * const encryptedPin = (await api.getHiddenConversations()).pin; + * checking pin created.. + * const isValid = validatePin(encryptedPin, "1234"); // true if pin created is 1234 + * const isInvalid = validatePin(encryptedPin, "5678"); // false if not pin created is 5678 + */ +export function validatePin(encryptedPin: string, pin: string): boolean { + const hash = crypto.createHash("md5").update(pin).digest("hex"); + return hash === encryptedPin; +} + +/** + * Converts a hex color code to a negative color number used by Zalo API + * @param hex Hex color code (e.g. '#00FF00' or '00FF00') + * @returns Negative color number (e.g. -16711936) + * + * @example + * const negativeColor = hexToNegativeColor('#00FF00'); // Result: -16711936 + */ +export function hexToNegativeColor(hex: string): number { + if (!hex.startsWith("#")) { + hex = "#" + hex; + } + + // rgb no alpha + // const decimal = parseInt(hex.slice(1), 16); + // return decimal - 4294967296; + + // rgb with alpha + let hexValue = hex.slice(1); + if (hexValue.length === 6) { + hexValue = "FF" + hexValue; + } + const decimal = parseInt(hexValue, 16); + return decimal > 0x7fffffff ? decimal - 4294967296 : decimal; +} + +/** + * Converts a negative color number from Zalo API to hex color code + * @param negativeColor Negative color number (e.g. -16711936) + * @returns Hex color code (e.g. '#00FF00') + * + * @example + * const hexColor = negativeColorToHex(-16711936); // Result: '#00FF00' + */ +export function negativeColorToHex(negativeColor: number): string { + // Add 2^32 to get positive number + const positiveColor = negativeColor + 4294967296; + + // return "#" + positiveColor.toString(16).padStart(6, "0"); // rgb no alpha + return "#" + positiveColor.toString(16).slice(-6).padStart(6, "0"); // rgb with alpha +} diff --git a/_libs/zca-js/src/zalo.ts b/_libs/zca-js/src/zalo.ts new file mode 100644 index 0000000..ab0daa5 --- /dev/null +++ b/_libs/zca-js/src/zalo.ts @@ -0,0 +1,163 @@ +import { loginQR, LoginQRCallbackEventType, type LoginQRCallback } from "./apis/loginQR.js"; +import { getServerInfo, login } from "./apis/login.js"; +import { + createContext, + isContextSession, + type ContextBase, + type Options, +} from "./context.js"; +import { generateZaloUUID, logger } from "./utils.js"; + +import toughCookie from "tough-cookie"; + +import { ZaloApiError } from "./Errors/ZaloApiError.js"; +import { checkUpdate } from "./update.js"; +import { API } from "./apis.js"; + +export type Cookie = { + domain: string; + expirationDate: number; + hostOnly: boolean; + httpOnly: boolean; + name: string; + path: string; + sameSite: string; + secure: boolean; + session: boolean; + storeId: string; + value: string; +}; + +export type Credentials = { + imei: string; + cookie: Cookie[] | toughCookie.SerializedCookie[] | { url: string; cookies: Cookie[] }; + userAgent: string; + language?: string; +}; + +export class Zalo { + private enableEncryptParam = true; + + constructor(private options: Partial = {}) {} + + private parseCookies(cookie: Credentials["cookie"]): toughCookie.CookieJar { + const cookieArr = Array.isArray(cookie) ? cookie : cookie.cookies; + + cookieArr.forEach((e, i) => { + if (typeof e.domain == "string" && e.domain.startsWith(".")) cookieArr[i].domain = e.domain.slice(1); + }); + + const jar = new toughCookie.CookieJar(); + for (const each of cookieArr) { + try { + const cookieObj = toughCookie.Cookie.fromJSON({ + ...each, + key: (each as toughCookie.SerializedCookie).key || each.name, + }); + if (cookieObj) { + const domain = cookieObj.domain || "chat.zalo.me"; + const url = `https://${domain.startsWith(".") ? domain.slice(1) : domain}`; + jar.setCookieSync(cookieObj, url); + } + } catch (error: unknown) { + logger({ + options: { + logging: this.options.logging, + }, + }).error("Failed to set cookie:", error); + } + } + return jar; + } + + private validateParams(credentials: Credentials) { + if (!credentials.imei || !credentials.cookie || !credentials.userAgent) { + throw new ZaloApiError("Missing required params"); + } + } + + public async login(credentials: Credentials) { + const ctx = createContext(this.options.apiType, this.options.apiVersion); + Object.assign(ctx.options, this.options); + + return this.loginCookie(ctx, credentials); + } + + private async loginCookie(ctx: ContextBase, credentials: Credentials) { + await checkUpdate(ctx); + + this.validateParams(credentials); + + ctx.imei = credentials.imei; + ctx.cookie = this.parseCookies(credentials.cookie); + ctx.userAgent = credentials.userAgent; + ctx.language = credentials.language || "vi"; + + const loginData = await login(ctx, this.enableEncryptParam); + const serverInfo = await getServerInfo(ctx, this.enableEncryptParam); + + const loginInfo = loginData?.data as typeof ctx.loginInfo; + + if (!loginData || !loginInfo || !serverInfo) throw new ZaloApiError("ฤฤƒng nhแบญp thแบฅt bแบกi"); + + ctx.secretKey = loginInfo.zpw_enk; + ctx.uid = loginInfo.uid; + + // Zalo currently responds with setttings instead of settings + // they might fix this in the future, so we should have a fallback just in case + ctx.settings = serverInfo.setttings || serverInfo.settings; + + ctx.extraVer = serverInfo.extra_ver; + ctx.loginInfo = loginInfo; + + if (!isContextSession(ctx)) throw new ZaloApiError("KhแปŸi tแบกo ngแปฏ cแบฃnh thแบฅt bแบกi."); + + logger(ctx).info("Logged in as", loginInfo.uid); + + return new API(ctx, loginInfo.zpw_service_map_v3, loginInfo.zpw_ws); + } + + public async loginQR( + options?: { userAgent?: string; language?: string; qrPath?: string }, + callback?: LoginQRCallback, + ) { + if (!options) options = {}; + if (!options.userAgent) + options.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"; + if (!options.language) options.language = "vi"; + + const ctx = createContext(this.options.apiType, this.options.apiVersion); + Object.assign(ctx.options, this.options); + + const loginQRResult = await loginQR( + ctx, + options as { userAgent: string; language: string; qrPath?: string }, + callback, + ); + if (!loginQRResult) throw new ZaloApiError("Unable to login with QRCode"); + + const imei = generateZaloUUID(options.userAgent); + + if (callback) { + // Thanks to @YanCastle for this great suggestion! + callback({ + type: LoginQRCallbackEventType.GotLoginInfo, + data: { + cookie: loginQRResult.cookies, + imei, + userAgent: options.userAgent, + }, + actions: null, + }); + } + + return this.loginCookie(ctx, { + cookie: loginQRResult.cookies, + imei, + userAgent: options.userAgent, + language: options.language, + }); + } +} + +export { API }; diff --git a/_libs/zca-js/test/a.png b/_libs/zca-js/test/a.png new file mode 100644 index 0000000000000000000000000000000000000000..ec6fc5c429cec78613d18d15d456029bb6aadf87 GIT binary patch literal 214492 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

GDth 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

9{_|Zo*a=^IAKRp1-K_Ud zx)1NA7qjrN?$1^@IjdNVu?Z17AM4Nd-~ao6P$$~2))pY5y7yd;rx$OT(N?y7rLv>G z0msfnck$Mnv_7hjAFWOf_Qq260eLpQaLMK)LoRcfQOZ8}-cqY1$z7UNp_1MQi6nA- zx)>Bik!4xpM7&Crz71V1oR%9GwnjHcNNv|{ruM@8(4c$r+FNgAe0{XB)b)oqrtkMS z($(UFdzWSx86vHIaN1LS_4-zlqut8!G8n}R1;R^_LOVnO9iy*b-@5hh{r2=} z_})MMWBdYsG%$5r02F;=1O*ej*fhhPO;`J@6F@pvA7T}Kj8r>aYBYT}Rg;i8-Wz`S zt?T=jhNI=KsW+~b*T#=__a0$NRh7j#55|Xb{X{2cx;ESld6v@L#6iI2CaETFd(i(X zcqi)~kB1-ftCe}XZ|{vKYum18pWPv8iqsToUipAO$Kn@oK3QrqN{X+V$!{FVEnWZJ z)t&!-_QNixjqa@H;h-yDEpOZi#WxyLgzonr?&lZQmv_V1hCy82J)`5*m#gmTD>7Yj}fEG?Qo$gYBXam=Fo{oFQVvJINNafhPyMmrbuvE)F@4q z&qRr$EGh~H!4SSkSytd&3u$~WInr$S$Vb}*VuhU2YEmkg)cL`2Bm z*Re!fRR^Dbyg0bGJbBV0=WH^sDorRIq_gV1_35fhMy>m$e&?Mr3VIIDiAao5Rjo}x z!slEPV-)eEF=3lRz|m3g#DqWy&aiW4s7W1t$H5zENIb;^hS^`Uxlt@SGnm!xAf&TN zU)qqGWxo~)NZIUcY`k_cy*dtz(AQcwX1*TVY_y8Y(54mBz}6Xee|Z~iJuI^_WXra% zfCf0VAz3xswS2k5u%$$a=MZQDYEF(xYqJD0Ax&8yy) znr0ef=qmMXniXl9B8C{_jSXX+6*9@tj0&@?R>u#1($4mSq{hS|O9y$;)a%uQpLI#1 z%^FFLmw~NO24aNvLIRT(!|B$AYyBXf{_2g9!^8jmk01U1`)fn@tgc2SFJ#_P3eMPX z^XeMMq0YABy`SJhOw-B@K3!70W4EIDsV(g??Yfp3!mP2EKAwH{_y@G#bA0`fWGZikyw}QP*|86SBpH+iy0rjfY*gIGXAD{=5s5$!NTHdAM`EqrCBY z*3?!1hcU)}UB{-=5R}lchcl%aGHKhk5`sicO3fV?i5;e|&C)j5WRRt!etEGvc{Q%~ zsD1Eje|i1c{$`qGiC+%Kj{r%B^|AkFkM8;`3uDWIpPuYj^~qOWDe|q+Mg0GRLrl6j zOcEu|zV+7Dw|M(QG5_=b1{l1^Vs*0N)d@&LYeTJSC9|39&Q7R{F_Lv@WN9PHC>1kNi*9{l;cB zp0=5dB(C-x*&AaGt7;TsRUu_&B7l&G%#aJ6x3ibMklsdXT3R^4*3h+YfMdZ|PLn9@AS2Zc**j8Q`vEk}{G*RBY*>sgIsa14DU z1wbH;0ud6hG0YrQ@I?wCFvAep#6eUf_VJw5p-WwoS;rE!TO-!FiO$j@w~i#Vb<+lo zl4Ti(rl}SUPRQ*+QX89T+1?yfW18nK6xu)O>Yrl1CJxBGP-#*ZDoO|deGUK-0niv= zj2IWPf!I6-@3WD(QdOB5NoH&o)dtdXvQ=b8Hw$(!3B|>7>ANIJ2nK>DXXAeUeaLhbDFx|^GRuqxyGxgWL$Uc1e$>E&D7>1gMn-QdkP?k`7A z7pKEeoaGlE`K`;>elQwkTw=cxzlC7TdkJbhf(e_&Ybjynw0w*IbH^~R!K#p#uyMqoggJQx^T2L zU!vK;i80F>eUK+;d}5O`=`k2(Nki4T=Vg6qm1@D=N_9nM;j(pY>r-1Sat{4o% zu|2GssjTzZ&g)ZCtq;=sQZ4PJE3*=P!b!vqu)2HNx3SOKo=l-Gjlt7;urUs>cu6AR z3zezusygjhQVyy62xw4C-vc>bWtF|ibhTG2)&;aGL+PmY>S<-I5A zx}AJ-><^Yl9euyA^6c_0n!GmD{{LhS&SHIE&-FXqVt;aV>&o0(8_kN90poi)^~X=!XTI@8Ua7>CqAht|&3EU!`Su7# zQylQl3{bTxVu%}e-*eM~v!kKz|Z@xDz=6785 zo2L)%f9DS#fA~(*t|HV!U@MkTXPgrxtDGxA=4`zl&7;tyTMYA}`PY7Bf3!RK{nPpn z&$l{__&x$a1R^k_7t)kiB|%cR8kR?CXvMi@?BdW?RkaE1Da#R-r^mEjboB^PYUNz5 zh4V^|v&`1_hprmgzIJJQ-0$XLK8chxwW#aj#=d#uYP02_l43~KWMEf}&gVs$7br5! zsrl@U=FJwcl(X)TW@K-z45rQdz}`?m5EQj z_ul(=-^rxioS#{UF>8(+5WM;ny)~Q}lYj8 z&SC7L-=S;>&YRK3@iSSxvz~69{NnLUcFh$Zsq4d8+n~i@QFZaqL&{)bQg&To!85+9bUGnbh~*Jz54sB~5}HLGQ_`QYKh zhq_$mlSyl+i@s+1NaLt&ir_!`qsPF(F&AKd%sakqSX@7llpna}b7N#l^3OYvuk8GoGEV9)0WgLvs>SXHAWlEIz?t+buuLgqvCZ zTid?uy4ezMb)y5b5{iak5P68$a1k*vysZ?M)uXjLD95l^`o^bqkY`Wwt3EXI)d1G1 zai%W%;M%tDB{9enGZ6s_6;**I4klwT!zoos%Ap_DYn)I##$^g|2=J*3ag~=9Gs;R) z<(8KO234j>S^m`E5hZVRWKuRHZxcBRb?ImVV?*itR9uNr-}TMM*$xowe2t%itlJ!PaIp7Hiijp0?rD?b!BxWX*~? zgwU+dN|LQfedh0E>8+8S<-@abx!Ow2mVE;IXsY8OyFRN{WtufH4cY?9QR8V0fPNSN zf)X=1%SFYxLlntJT8tzGi9urwi3UZ?Y*y8??d@j~x2D-#_vO(%W@gcafu%#J+9M$| z9$lq;d=5jd2uaQsPY?KTd;VD&Y^3GZL?`9(B)u{n+)#+BibzTc0KMc~CZv|tu|y;c zZE6-VvmoW@Su+-9CT1xzc2%xb9t1JSO5y-rMdIjdj0&C;yda<8~`O z$s01K+uCk8y|**F`o=_gYj~^z%CbyZlwk;liB-noOhh6%$8~L_$V|)%LwxU~DT%Ml zr$*!A^k{2b)RSE>>FW7bmRI$-k8(En#E#3UW{HLkeiV`>1%bpG>qsLqB?EF_H(#HZ zUwsRKR|k*#w%L9q??&SdEzJ?sbuo3)`Ss2E{0q-B+5x@hj3#N?P3l8djx!eFA)2H* z&9kD&Dz)qktHi!(O;*{wX#39Pm`-XU8dgVs(~qk0jF+A7SH5jAW(j${O~p7KDOn~4ML`td6tesj{jyfG zosX5PgH5{6#pLF#J5TR_xcJ^9eb3dC>&OQ5HwXW@E*;4-x{9o^lu{qo6gJcBWVaaM zMb*dKVFlVX z0Qp$1n%zpxclE=+Rz687H8$;^C!if!+l#0QAh^IphCViuN#}>e(MVx8WnIk-<2Vhb zu2V|lg@`OW0OOzieIUK$=jEO1_N?FSO*_RPaod5r+@tLcykiwzE708o$;(0B$?}NHgaLejJaSl3jUTf1th!<`@hY<2C%>0)^IgXMDa_2J;>c9sV}_xiX0`d9CN z``hh*|6B5I+FLYRKfi1KrJs26OTU!9`5^p_zc)Q=ub_Ig9NqYG_8WiwuigpSSAYHf zt;btmJ!~AAhgrPR^nr3v2>0SN!N`x`NDP+Dra5#ZWZ>6ogT(1w?7KTiMO1!r5h{%xkEkA z((}CdLjBYEpZ|q#r0@2>{%fz(>hVAR8_rkHnn$;T8G78UVKZ8O;~Um~>#*>)tU`yQ zzw+nrfBq&m`PJ#}=%o2`--}x&axp$=)ZY2o*GAW_75<&y{9p61O2&B6FvzOucXqC< z&QHSf(2;cG8|C_lQhUC4{q!fl+`avVjw1Pk;^^wW?{BWp(}VAw-g^%|%-qO$JsnP` z(`iB)hQT_gOfg2|Y!i2KJu!6HQCPdNVaG@B9sm8i{Wi<5{O)fU({1|RPKKwC)Y^lx z)D&N^9b$B6hzI`c+~?D<{Q%e)F1V0nW1lsB>ND1XsKAgqbZ}rKM5wDv4~@EpQ{RW& zWqq@m?ah{Q(zAT)s@<)AVsGoq`@1*K=h2vE()IIAc7IAn`^P)vfB9DZ+P?pdZ=L>U ze|OY`d6k_te*EO*$F#}`YaMu21e>t#831ume2hSpRFb!QxX>%$`!Yib;QZ)dKHX|! z;>oUL=kPU`kLE7d!t|95h77mdNltsH&i<>vvo#)tuUuLGhrcux*q~ibZtZoP#gLLS z_yL6PGfD~&kpPPCBY;xGWd=j|VFUtjh(R>YXcR&}b9L|IB(n-ELYSzXSlTopRxTMd zUAI$~XOl36+aJ=SvTT8(!`ZdT+b8a%lKE1~vc4PFDWzMKH~o4sW0;wD`lL7;nS6I$ zbz;u;rzZ64yxMWxuFi`2_QA7eVI0|m6_pNWES%&`C zpVaa9_sWOkeJ7LDuaDQ$l?i_A=LhL@e&ZL;^)I{M`6c`#Z?64slc(!+d-=74;y3=s z?UF{p-H`EOJmgRM=8ZeQ_b-0YoyFZh)ck>8zCNA$9xqvZf)IkLiU@#oK~?DmgG;59 zh)7j&K~+UWRecD;6jW4oh#{yDk*a|3sd(=lIpa)>u^U=a&hxySjzuK&eUWFgD|46# zq!^=}&#?j4{^Z$Na~>w;B>AC7r=0RSiyBK;7FiB1kwR#~@w2nrqtD=is^WrP$VPeL zHRe=hTv<2Qd@f<)U5BI>L_}5bk~JJ-f@an@X6sW-q9Qq~0o1TzOO|0^0D>k6F6Erl zkFkP9STzA6qC|Ls7=(`zF_ef@A}*!TSyUnAKEy6&PmfmpQk%6o{@~u}I-D+J-`k`~ zi6|+jIO>nfyevk$v#sfsTl>XilIv==aDV9W;#Q!$OHjU zfSX9ofTj=qpdgf)$(#(h)YBcCoJGKiNNjmf!!>3f{Wh8IejH)`RM9l&wqipA^-#Mw?o@KXoVx6aHpV~Gd=;bVk9!zROJ1b;^HXFd#w(I)9I46CYWfDY(OqXsWDWoCglP#q^7vB>}i28YAu6vfbwDm_`%NiKEY-|0~?gq7KW|pdU594HHMXTk^E*=fTc-8yXm)jNkd3A zQZnjY-kIIjydtR@{CVHV5Sp%CMTg)S(r=YQ8)-wo_8TX|uG6t-x$psh#^Mu*NJ>dn z0mubaB_d-Omk|*FLS-~XPs&*V zPRcQc*!BHn)to+A=Tu71M!3i_UuLG%{p)*O73x`C)=l;r4fcp3C62gCv*DzJeQ^_^YiL+ZDDB3 zR8r({3}{NMA)O!PRn5gn>#4KDe)e=hIqa?ImzCCzc^m!X}K)=9rB{T zwwtc)Wt3%WTw8bVT-V2YKE~?pTR)o*=kFiBw>o;RSEh{w%RFL}fB`;Qq9S|#7k*;K z^=NYKl}#<{zG!4TV9=;)eA6a)W>!I$RrQUxe6wFp=9%`|9Cz!53=N`(tpbctpD3z& zvtH*%TBicJlYXkq$P-8D$Dxln^;M{L|%6c@3P~Q;*alUT`by-$+CS-H*D3A7|sr)p$V_AOLy+ z7oO5GSFZyFWh13GCLje(DJcLVGR6=QUP_)7CeKFG+0}eBACf8|EAmX_d~tko{BYGb zl7eI9kKvQV=;DiH$;KvhMfssd0|YcgCa_Do5k0kR3<@zZDMme;Ob)7SrH zVVhCLut^|kU=gFSuC@$o7}kaLv)aF5@BT#i?JB+Zpt|`a^DSv&7Pvr(NeyBE%E|Vv z>(>&}yHD<~mq$fDc11Z)&m!<_JwCbj^Itz&KD+VbwS1?RwLwl!kJZ+LFsF z!J~}S?9CW?9Fak&t0(}oh96Td-nbnbE~3P%&XIr!_DF( zj5nvkJ!=NZ6AdJrjQuFb_W1t4^XF$zy0_2Gm);B8U53mq)6fph&iop+t${k{QdT?X zZod7~Ap}#6-EiJKd+*@Eham*-JzVL5keMti^VZnIwr=j?gHsZccJTB0?Vr7FXV=eO zd(~u$hXw!MgYsyx{g*zsd-Yc0Y|{>-qS!4>g&~t+OJ6@cy|+I6dmH;sJ?0G5ETm6c zGn=K9M1(<>4HG9x5LMO-&sv?337?8yAbg(yDWw>rG_4GlGRM|fRf3XXdO=~q#zG%kB{)gXR{Lu&fqPKla1kP-; zTzj@B+u7zy`R13tn1Mse(NF%hzt}AG8y`MhoP3WJKB13E2pJny#Ra_pKz{&U z(ifm05Ru89EnLMk>IZYOT0H#d{P<)y8z>30O^P*Nm2r!1P~OeP=ilLq7soYMpQ!bhPfCQ<)lp3b7fGmUk-(> zu@1wcX;*#hwOe(D*jQqhrrUKk$%YyHl?u1MA-LvAIUE~HyizcM21Y-Vj{opnZn zHvp3=DFB5;g%U+bM9MX3OzfBm2J`|xLGD{jll8G-TVperXvV9w=nZ<+ z7`h>hgkCVlSY`syi5#qf4USgGOYQ=4ONPM=A_n0TJiX%dv`Odroa!d!`O~xZtVt|c zzaCVnkIBkfmj~k^B3|)w()HT%-Z82!efP#VWZCh1>3WWWvzYl1y&FU_vzQg(>Tb5R zjZ|gUj*d4s&vjn97_}mMOs?J}CRGO8Lv&C8$`PGY0gIb{R#B0sIIcRC7Ylt`EDXh=m?4pn9+Q>u%;U!OVZM>Z8B zf3|9xlbNaU>FEoAKoU5mL@`#z5EF|~1Q98d0W&c|!i5A)ASDoxLhy^8E-*6@F*99= z7B3JH03yOmL*KYV1p?4wC`GrncAuI1(3r6ek|4%$PD=Af8xqx$ zGobfzQNG*hE zcFh`{_Ff1CBg8R4iHQsuCJ90;WPbl$}SB3;pNQ!L;CR1ctJ)OA>aae}F4PEQKN0#F<#Hs=jQ6*Ic7)k&{ zKrb+eERhl^T>ug;V=RW)GqH76QZLe`*sT_aCx7?90iY_7C=!{JnXDnzTuiQ`9ewTX zr{$ea>OVf(+3oh9obyr3L&=4zA|wf+G(3Bf}mB_SQ0iIN6VCS?(*kb*&i%dJ*zS7-C8 zs>vQUZ7Z-DBhd(09WIvlj&L@ZCYr2DN&(_FgZMz|5K>$rJUv)_?-2c&Eh@@$ZUm%5 zip-YC)i&#W#AV9CZ@dpF8RgitNB2L7_a0iX{Q{*_-`UoWa}8~3;wyE0}XiY%%fP0ZV`#y4JZqwQb)&2F?i9;d)#bAQ+L>uw$# z=Vf<(_T=t+Y4I$VE`g#OeeUOfIWMM-?_f;5d-LjBU&^)d-+MP~&XD?1G|pPe$<{F| zdn#k6(hS+=e0~4!xAp!rv;FGm&9}MU8~@y&8?!V#KV7!m>$R)f=+2(K_gh(Z{^j}G zcXt0)G5;d(eR=Z!+wcFuJInKrz(kHyh-?ir3qg`9lnfhVKs^xw3V?_Js46}^S38*O z+O4;4y#0meUb<1ySNht|ZQpwZ>z!?Jz~fE%&FB1K7tXsu`rF=AU2opl(O=$gZX+g) z61-sbpZupgPd3Hh{@UT+`iJ8WpY81JHr4i_bQtid5D}mNM4?a+h=`dbGy1WlF#_Nh z5`#GlK_Lo`DgwjS65z<}MoX88f)|MJ0ulZM5h*UmA^`LP09B|0AfE=Cg6TEJfD92x zk}A%Yu|Ha#jW>f*?!8B5qeQQ`LwQO#NIpY0DXKH166rX?i$RmBBW#Ra`!x=;r<%S&U+>5ydu zD2i?(Ia1Y>5&$By)|w0DY;QcCX08riV@lRq0%G5JYhWp>N;Y9Yj6R53Ny@mKTu>oZ zVz$;eYcpn6G5P|MNEC^pNkml>GcIN9BzUGQ&p7KmGUUeO-T9_cg(OkWP$Go^38@18 zQ4?T7pT*abRCmwE%ZHP^*>f>HU)#*^mWoXhQ-m_8kTgg#XU&-!W5**vWYZVKCKvKY z2r+iAbhK}-T;IEKHP5=8=bKm_cXBeWZ&FeO9p~5n;GOW>i_cGRSmb8Vpk?MJ+ow+- zRA&$J(X7l&;WA56Rf8e{3D(&t6hjO##-Iq6P&l&IkMaZ+7#)I$Fe#}Th2U}_#epmg zmyujjawveVi5LP7H~8=w&RVXfR1}^Q=HvR@)J5ryGeM~HIBn|FZty{jCCe!@u#}9d z3Pd;xKEciCPG?m!pEWV9n>FH+H_z+mN2~4an6V?u5=T;$jLyshk0#T_lD@5bV` z=#THk*|k)cWsOZQg;PV46y-=>y)(S^mYB&;e`m2C^J=*|&GDcZ`@VBxSGm10E*=l7 z^}$_7J0?&uh+_Aw`ax%pYy>!A*N~lkAn|(K>r=d*}N6_TKQVXI|N5 z+Jv)`34jn3U}J4{mc&)RibyRGF`?tlSkfHRD#%=yOFXm_hV33RqG-py+3q4@Mv6#x-g zP?lwRQ9Q|#zGOgv;KB`%fE7s4h>tPAz|ge-hzI~(z@M-NFA)&{A_5>VxQwdeB~`^s ztC~j8=nzee4jv%o8LT88b#XT6Zf4sU5<_4J=)0Bb&%0PS<{E$!6v+W>R1j5@WGzq< zj|f(k22G8|HPI-}Z7L{=qeRJBYYb;pa>?U@*`hJ%t($pc5mpR)J_#uxyq$`_0= zs!BwHgv;nlB7&EQNJZEf!%RdeB~_&hs)|ozRMveMq7Oxt&vs{9SLQ`kV6#ch(oDu- zx%uF|kFM?9id`zWR!_y$Oy;B4-gt#G^&F9*Yx>7W&(Du-zaA##G^_F$Dk1=!b7fgpRh4B~4_2GOSRXnODe{ci zB(LXv3k@VelK{wEm5p=Mbk;6$8Dk7HLz&rTt}KeGC@SY1X^~P0(R<$yzVp5hVMwZg z;4+5BDHP;NOSuN?kUPp(>o!cUB?>Bmp=1E2sFDQJt^LhnX*5$Y?A0&Z#o@Ya+Kn46 zySOuAn_$(Oltf4rzO5B;sze=FO{tFGMnTs6Ob{`gt^;A~Pp4c$le>oa{qR$!8d#3(7T zqb##7cM^uKZ&vHoYSYJ{RZ(nB^Kosf83mDOh#^s-0#R0clIeVM#Az4?>Ae%OfRCE) zJzlL1+5}f+m79+yi|COe>aoW4(DiF+hvx_AdmNnbs>`7R)-VX9$Q@EbQVlV5Jxqqn zyvr-mqI!5R`nB&)hGl+ux}1h{iO@_P8EXt#kO2eWdWeJcn=;95(^Z?{cmD9^d+z%0 zJnZ+KVHt9p1{s2HLp)(R6UERT&Gk?gCwIR$G8|@ioMsGKK}e&CZN+}8B5UzszEqEVHxp_q4;Q0K*)v+OH3i)$k~J{s<}n}2kgjyGp#bJO23 z&248nn=T8x#L@XPSB-tY`S8)*x_SSVa6HKlR}T+SeDOH#Jig;k{L$Lnb+dy}Jz1V? zgvLpdNCXiKgD9pXDb|KAfG*(wt9mxd${pqM_}ugLojX4^AJ*MRC+}SO&+qm)X&p-U zYK(8njos#z+`nb%2|c^}&Gb*+vB&SpJX-H_px6GDIN6lk@6^?Q^e^;_&As3FL;cRf z+e7UQ<5SWL02pk6%y6FP+i$%BfQSr;nF-Jf&N%`MKtu|N2*5E$|HWJQ0Yrp9Velu2 z2mle`g0jP9L<9g4;UyvhFzNfLC!hRgI^rAwNW(x`F<5CSl#~W58y&om;A2*XYFsR* zIRUZ5!Y^)Qv!PX)lJ{PEAGsaUV6v(x@=_T8G^&ap(1f9IZp)Q9t3=w0JYvqPJe~+h z03ic1#3rdG5wSK}Wh2fYS20N?=-`!j$n7BLJJ!TKnA94OGY2!bA&+j@?zKp?$<4ZU zapb0(fS0Ty;W9}Q2*whiqETYR1yL1J5dmPWHO3H;h}fhTph|c_0VolYF#u9Zcu6mi zC;_SpfIbEy5TT15*91{YV$~p;K*m*D5sOW;9I~!0o6r5^o3LhtJD(%os`9~c9+GNi zpgd@zm=fDXyQs-ejjAoW(J0@}`t{NCd+$11;W842vKobyR_i{i>haajl(TDn;GBBr zY)RU5i}vvGrhUS(&m9J6N|9KdGa}-BH0;W<^4QeX?qL1N>U?{b& zFC_+gN5j1Hy9Rb2`Glvf`hogI8FJbqnOopVH^xVk1sVn_qv`^6Obog3!)lheiF z`PE`8Uoh2BNDsg`7zcn;M2gg6w6)bgK3E<;Y<4R=^{Zxj_V0dWvOAgFqc;!DT&;D0 zZOMRQ54O0>)4sc{<<646Q{bRZ1@QWYzWGOsqSIa?Nc_|snhs8p2Y^{j zAe4U9EGBiHHZ@gESMPnV-~RBsz3^h6jq0+>4yNX{yX_cy8k=deI=kY=57M|Q7Dw-+ z`QY^A?BFxQgTMH*?{)5%zu7jR2$(!;W0K^m`Q+{&{dVa%p4^!4-%_ISY;^kc)ERYE zp4R8Gy}SSJ(d}1tUY>rwF6gRR>}L4~4@3GHd*$igaCqFu-+im9X#KDJ_VJtTt%L0_ zX_Fo?XgyG{cV-+p7r-dt6N#K8mx|)uB;B5-THbrYzQ!TNnk(#sjvu) zT4qCJQWQzBrU0O-@&oi^5D{Ev(#n(W z@_6rO_w7z$Z@T3#RQ};{|6l%}+t

&Jirz0tnYk5Nk?E^M72n9|Jv5$W!3>3a8npJ(6pZM(l4=iGY^^@f!vP6S+pxXtAo_nftYC5&XtgoBx> zzSQg5*P5=9oX=_^+k$`dl+LG0)8KW%a@QpSZWs(_}H)7fjXpkPKHC zW#Zh#GCr}8Mz=8V^}iO7Q{Jt^^W@pSX@MoGs5sG9G=V>vf|b&`{wkOg#r9Wv!D3)$ zutSaaJ+}Wj_iRP%U_%aTODbw3)jQl=X+9c6&VW2fKv6)XF;WMbdomcH0AP*uOprbGH2=8E0Ft$(cH--x;s08GRzaik*-g5{3Kv(|6(Dxb5ks*~{zl^S;aA?L}@t z*V~zJ#$PM{*=aS^JhxkL)fs0OH|c*}bkT%yJRYb%3RFdC7r;Bco!|HZW zku!)vmP_*aj7^D%d_Up5P=dv=x~+4s-WT{$E7~BCnk!?uUx7< z)1>&7fz+1cyRufTv>Qce8iw6q!%uXJ>s9)vSx)Y5&Hl-%WGM5yysn;hRp=*uGBWVY zW+#?Wd$oFaSda7$)In!9&Lx(Sk+=t$PhX>^Wk>sx#1PXwJS#nS-_G!yFJrsefcU+Q z4t|S~^p4a;gITp|uk=Z!3JB(2g!r*%VZXSH^89&4Ij22-XH`;hcW`3r?KCN9*KDw? z`{BOe&9*FhUHEmPyYMbpXeUpUu+QP-&%Z1?W&Ta~yB%BG)$tn?!j|zI!F$yXwHPfK zrnJx@qkqfJFfEqItIYxvi%d4G+~!w3aW!$XozSCR9Y0qeSC`=oQ*pmGlb>$$KtH*tzLh*b>Bvj zBQe7KJrTin%-VFNB=xqw_1k||L^z9>oYVVOG8Ha)E$lU$w7H#JDGGsx-oU4}H z`0DnCf_p*G7T+Fyx+W=}o}9go#Op8IDmlps0NuIv7!BiKB9E1a1!wkJn`7RI)JhOb zIye>mmYOC2bJc>LT0AcBTiv6tP?-jK&wyBAd`K96n|y4-h<+gpb%deU@(o{x2`iqP z0{SvRj;$I?k&`8%iM9Y|zk|SjC9YDFNIPXV?4i-0);@OWV0o31Bwu&g2{tP_q&xmL ztiiVdS=v;BV9|-o-4GT#(o?Ga-eA^BoEu(QWfWgKR4UhiNrQb0$&9H4Kt)N{sshPv zA;OdRkO;HwXu;*YEy@K?Tx{^gYgTEMGhcuCvYS7#JC^p_cm5vfp|mk;lh+uJbb9Uw zH^b3>&X<4grf~tCRP#}O2mFXtV=^X{JcLITl-2sIoPa+Gvs6b5cc(a>i9sDFgxA{M zDLFZ8%^5yT`J!Ll%A{ZrA;qfj+1vBBmk8x>&MG+Rxbl&!0|yatft4oAqOQ?bM=<^1 zcz2&F(HQJ8%kmf6<>caM()Tti*0{H6A@=Bdyyjo_hVlZ=^S2Ouy-gC!ER`!DGZX!i zF}_gOr-pZp`6kCEvQ`+)BkNLjM%@9vSp-uPLk%w2_(0mn@%)vsWu3-=jeso!wi%!N zhqZ8rL4K|^N9pPHA3-b=)vJ~*1WU#yd4`7Er*AC2d*1A2LpgDf8=YtIE)Iu~VJNQQ@TsM9;-m^yme^LrhN8`;7#SH#5QCU=(&N*eW!xKN1|5E*T^I#_?M<5=d z)+_YnvnSOmu#*<3r(L5PW6s{_n?2l78s?j8_RBAM=w{l2Qa7n5wa6oD^jlhP%bvZ4 zEDgxLQYJPtwu1=0i;ktw)m*baGy%Yoc|kHM$ZciCg-iI$$cuHi(cA2=?&b;FL4lFUlpwvzT= z2bop2hfn_nptbFVy;!!&;p87y7iDw@J~jn^dAru*&Kk`6mzVM&Tn34LXA_qZ9`LUc z=JdeE(q*Wo^lQzAE#P8$&flp%Pe`1!EK~>bmZ_Ed6gE%w8|Mt@nqkuGzdw7xz3N zRjZiYikHP7K&vj8o70AG8%HU@tNZ#+kxP- zFj0G2pA^Hv9qKxK2KZ3OJD;@2y$Y2ZmwV?{iA)flO;WL%@$C`4W{ZqGRX9lh6-MppTKI(`duc~ynPw6*9QOZEM4o(!HQ z{9)I`!D%mf&f6_RZEc-!;38{)Qp`g9g!*4|!vqNoFCWaNN^~u&uQBh6XwNY^=CDFy zI6N5XN~EQ~axiFf%UPQ9(mdgs)}3dRNiUza6SoaGA1?^4{R)Jhi`(_enk7fBGMtPq--CgD+35UWT`yyo(^4~T2HinMv#x7WF zx8YK=PNi{SE9p;|NQxu>Bb zA`4dfB>dPb-naWD_5^=o{`I#@^a^lBq`>oZKYF6x1FS@r`h0M5g>$)jZgTNC{Xap_U~Y&*7uZHbn!3;Mu?f>w>% zA?^7L=S1G6k<8jUT9K~U{?hL99B*_{F9s7LrnI0t2{WOq-p1~Wag{Q3=YeqFw7m!mp`KwNK&JZ7^p`-gi zt&nryk*6Y|1nWLso0fZGR*^BJvJVxCRi|Okhg3m?XkI0?hY{j^qeLH)OeD;!G|LqG z4W$7%Z>YgchSp%wt+&0A-mBA=yZdUXz+1<^tuk3fxUD|MASb~3+I3~envpeRd9hGs zmu(l@7>9z3?YwdZ{?oAIO_9$oyW;Q^@5XYti`3QLH=|_iOkt`(yWnnMavE25_+hK& zeP{hB+AKlA%84dcf$0dSe0#;Ddbo8sM>~yd=IZh2m-x$NXX!S$sg=4dh7^4e;{v^G zr>9T-GZN90qo#=;I<@q9?1$aBO2(2o_MzHfn|&XeD3cf!Gv-_UB*fLq-4B4 zDSK@9tN4pX7_{|>yhl;KLk>8%v_GRZJx{Q1i@K2?(d&s5Glr#!_82K!jo9Q;9+41W zp%k|YcTvVKWS4#Ir@(vu%*?|tnZKU;whKC*Cq((-;5L;)Zzq!6mVJ5pU#?s2T~F#~ zCnhL=ufrR%XqRlKT#>kGFyH+17sz|y+Ur1+z4rV@?v6*f-^;i<5oad1v>rS&!DgV^ z!_*pmUl?33Rbsc`%bvey6aB09*aJq^d7ofOET{$@{>-3pMbE-ZGT>n#0(2tuK<#Uy zvag&|l(M{gn<|wUcP%tMS=9e%gG+y^$W~h`_W2~FMpbe&*~5@JCT#%gwR78=sj;!L zCrIcZ+lFn1=PbSIoMPSI{ruiZDn)b5Cg)gam%NJ(RBGQcr#aiMt2OjSxxbLA5IKRz zsoY|yQ@qibrRxa*GW&BQaHimp0&@&%-nYQ3lD+RYU26LiAyn$#;N(kSJd zZOzMPfAPyR)2VgP>|#kBzNmVA5`=^;=d81Cvxi)0&92M0vG2V5UB;)+v>a~-bgx^q z#;5X&cbk4)b6Uof5o0=r72TIO>%qs<9|+;>Alpe@_26S)(c(*itNx;5Vwb-UlKKg| z_f1XMuMb}YPgnMHn!K_0TdPY2NXz@0hFW{bqyY?*)lq znLWSOO~Rt_csu$|yo>l~{JP+IV+W-_NwtnLYWt?{=IzRVzj!@2qMx!ut}f_#`}}mp zr|F@%BDe0fH`?VbLvEOXW=hM0WZRd{!fT5b4WnZR%{i3+u-+)wdn}>aImV?mhc22Z zu`w7HkBngbz@jR+{xlP_>#K;I4U&G+TaQ&SoTeTJCejP)=|3G4(s;mI>b|x>cjbJpHSCx4ImL^(S4K9YPG}b_2K&zmb;LhTYWJkS{(#Fn+>nO%ty5)EpyouDSY2k{ zU#+@4q3XOzEo}$hGr5C%QvMeVHBiV zicuXOHc_{Muie%t3cehNy6b#0H~j*R_6u~o*!h%RKN-%q&10G{0HGL$k@^OKox9ULt@ zm!&}UdD&5D-f}vUDD@e8?X>F&nyqwaE%q>zx%R%Bl_p^$fZ9(}wPqzFi zK|k$V(%Z#WUDtm8>kJY>h>m6UpW3OH=_W2i39c_<_CC)|r#?8#N=b3b^*Iks=j5Uf z<10J&zMsfh>!(KL*#m8uP3He@^!oby1Zdn?2V%u8lB)=Ltu;{V=;a3Iav@Hw!_$+| zE-cHTAPey1L8P2ITa75XUkB{j?H`$+Tgq z|6SJLnV4U|IWg$s==JPm|9TNJbC}czZVoIP`6Yg`3zj7HCELGf4)F81Jf0dl6>&~G zXl}Q7;y&eJp(KlEXJcdEy%$bFCMn%MV9riXPvAeX@=|l#P7hjJWe`ecdr#sJR1nfO ze8%vHHB9*KQ6`fjLWgJ{m*m7__6RK`;+WvGDBOF2Zo;75*(izU@El zbERtjOjN#H;IdP;*p75J#2$me;PbozC&3zus@r`4!QB#&b!!5>?vwPw{%h)h`2FJL z%P>+>ZhN+P!u8#VNfc9Q$(THt{4S)m-Qe%d%cc+UXHqGaPHByl8p&l@2-eeAOZGJ5 zGW?!*l(CYV0H3X&L-mW-(fvx1%vUhnEmop%0} zc`Qa8LU`Fsg8XJg7i#20eDK!{s!b=cTYZ*hORhTq)|y##5XI!IMG{G)Y%Y-xumgCo zYcET*c*?k)v6j|Qht+B?`P1+J;FE9W7|5?B9#RcH-r_C&e(=tN{8;<~+YLGwHJu-& zC2#(gN`EMej+p?<78md3a87I&Z1eY1J!IKR)N5dv7%(i87;28p9S{rPcz0VNE2}PX zNyn!h0`T@z4{M^H>c4m>E697==ruL2N&C;}c`*Zkqfz@lWED=r8xZdZ#i03RrQ8Pi zLig@Rn$O=F)pR+O2~GLJesg0{Rvj0ei!mf6wnqDXC3{9Y>o-LYFy#gNpWqo^R6718St=T0YoD-MNf~8v}@u5v|%r2X3S=$ymV~jj&$JS(@r*&lk zx1Ozwq(d92B|wV|Yhs3CMJlThZEAiSDlV3q?3QXPRZ!XPry2}ImfEG?oh59GIC696 zWL0Jpn@+jsqd>JP-j@$~9uWtgmpXYmMj5N)#u?J+;L{}!`G@ti7oz(U#Gs-j2_@OB z?NV!sjzA3{3VH6@%$1Pr1p;d{O?5{+^ z{Sz2E(&9O}u};6bjF2 z)3j3eFK?WQx-SrynO|KVyhEs1iwjo~f%cnD9cT$kv}K(!Cg-a=zl6idk#j|S|51>4 zuMRRZMt7ND`C2VhHCh%#pHmim(IX7lK9qzU@M+sXzl{l3`A1W}K1PqbyHC|6cZ;_k zk00xuvqADKVOSsk=3F@6iw-4iC;&%}MR)j4$+&?jj_o%Ym9{ntCCvBPxO^1O+e8?U zNCl7FeNDPPEEe%n3p)@NklBXmv#DZ@(F`|*$fL=&+kmXw>1Or*Vru^C#@||1z2=ZE zd@hR@}x4Sa6*Ef}V6j-=}( z%~LHE8VupDXm7xxQ+5^*?P@db`e5++{TR&OxV>1U#(1hFRTv3#af@uo0v77Z{BY_#ATSjCTJN#V!`QLgt1<6Y;yAy| zY8=DK$42n!%d75kt7$G;RIow|Oz&d9llArn2&$qu4z`&nSDN3AZm4mW~?v`>QWh_vyHFjil-3!Bs$+wZ-s z_OH~CpNW~wHrO6f77#FJ<4CSnjjT1)SW#oR31O-ITll2$!S4>&fRsPG%|zf#0!~Y< zDe7D2hMfs{C=P*^UKJXeFJJfLm+k40ef&~TksNC+v7hhU;^9QoyMR~t9O8*ayCrRR z#cNR>wnn``2ZtBjV-X4#GFaF@TH>c8|K2MC<#wp6p1enV!!HKtAct1Us3h~|Hz!tG`=`4z`1;%*Wt z`RDSf`(%PVs^;u_4xoO746MKQ{@XlJoYz4}nroK0Opf%|hGCMiI+ddWs#rZDi>`W< zK$=JrfYgDJSDK(YGbBjrtvbS=;L2HC*-|OUuwV;JVoZ^?Uuq|{9OG4DC5FEm9uJqh z%g}O}ZDrd2k*-1#9S9}6_0@>ZvUgicv0@}oLlJC-)IW{uQG^_M{XoDy-lrInf=Ty?9%5fwIr|T1O~H|_w6($Tx|M2wDAL5v3SpQ?58Eg|QtzZ9 zuQO+^t;QqlkFs#Za$HM74Y-J7s)NpbGIp93L||bN9jN_&9oZS1hDWGYr3rd_;3u?N z6p1zeDUSg}4=0(qx!_Cq_5Bmhrb`D*GY7CPUE|5*8(Mo#SnLA;-ON3m2M>24!!*#S zXils^=-Q1&fB|`o$#TcI@!O62H6w5_&&1%g?!W7CJ>sS{>P;h@Ibj%?1>o*yr9p@} zxXYFs`;l*ZphLnoe5(G9vm;z4AG|A6n6GkdLcwdNV7Kq!hU_E9gpmo1mt;Ovt)cq{ zx7$O)d1U2LCoU!^0OX2B*Fw;-6S&>UNx-_!RbtvgU|EO%eHREcW3uO+sf>`oUmlct z{-ZF@`t5tFwzBa4*?Mq$&-ahSnYpRowb-0y<_z~O2y?9{vvEd;Qynf|T|17fGI?V& zG>3gVDXv?t3Hs50C{%EAjsuoT1qFoAB#lT$Zx*6s3O(5f2Cw$U3rda3I)FCoT# z_E~fvJ$Mn~7qm4TSWC*e;$gnTrgh52OpN@}eARWPF!}I*SHCjN}YE<9psP=c+0{O|H%Q>f-I$$5NDsiAz+ zcP)4zF~0SXZehK>(93i|L6h*$#UKVYHnHV|x6^Ltz161{m49}fOR2{z(2G{2)-?0J zru)0z3^n%4?w8o(E#Q~}xId4tz&?qEH$fMf6XnQ7ldQxkTufN)O{na+31pI(5x<7qerQvQCJgsdI_>G(s~9({daMFYfAm*>k^xj9Mj4;uRtw;v z#2^^Cb#hyglT2^vk3h1O<_w#eVt)0DLF|Ye8Y=^rxjeE#i6p4R3kln`rQnv`zCVaI zQZrSP3vvndYG`98ALcbn-GVsJYKNp|X20j}3K&o~Xk+pTDo*bHFeLUTwxHxb@znYK z=j6*#2RbA*HdKADT>ToIexgg6gIpkfrCHY{P1A!7sdNm$>A^S1@8+7a=qd-U;73YE zt@El9akrLu@up11xZw z@DT5@axq~USAB5V*kKx-H^q?omXkYxv~~WPrnjQX`5=FO7;^J6JsMR?{IQ9U!BV!| zo{B@GBHnC(oGi~AR)@tLdD6(h9t}BJu_wAU4+lGc|9fSdH}W-osjgsv?M?#OL6Gyn zRl~8`lNAqdvT5pN)EvZ)j}~d(QC6oSn+Y0r2UyH-1)l)~0xyVk6CCwY2uQEgaiP;9 z_*^(2^nU#`Fq0%6h?10)Uh~&!NlgJkWCSj%ib;rQi~+SwlcR1Yb}cwuT)f;mH9Hd3 zELK~V_N5rKk?GLiJ=%?Mu3wZY2;i3%ZmRl22O&{h4eAPmg5EwpKfkxeP(}+zY*=NM z@~F;m#9OL=e1;3V&JpMPi#zupQH09NUO{~8W>j?ccd z$D32eHC#@P?Sj%A%&Iy6I`T3gRl(u*BGPbKaRA^yRXn650ap&$0z+mm!JCV3uPMbQ zk18E^pS<5M))-?9*=0xB(Xbaefir3s8>$l&HGPfs?c6YpAqBy1-_O# z%edXwPj`y#Cy3whczGi`d*Sntn9@GC{)kkbS}j+}xPGR~Mv|e0)&7LN4uZBcnUZe5 z*ktV8kv8>OygJK%c8Zrtv6v8M1UgZC=D?G_a=n`Nh5A}hXE9Dvqc0`=g3<07)n`wV zQJJrqZc$w2E=GStgmMU4EOpCCs`gf4c3P6;eCXreFb`!&w!pcYsKE-KAE!#!c|AsQAKR1 z$dLU_QKD8l%m2T>&d?W>6BsC@inS@{unX@#f_<|cQ7@LvSwP{QmnOe4)I%* zUL?bVRftv`cJG1DG|rP~PdLC^gNQVb4@Xe>s?{xFt!%h*7(#>vgyR@AwQMgzq`}1r zNBR&&CqqXPb(mWRuS6#?uAomAhf)}!J)WvnTM`%gaR{~N3{WTx$j>n0<=s5`yc}&0 zmH@Y&uax?+OY>y-@?%v%VSiXq_E66xT8d#~EH%Ayeq3FLPo#u)FswHa;wsa9%*Pb9(iSdi=^=H|bpq>;eXJ4a4L?A2PgZdY# z3N{>W|NU$tf(kVob4bQ z&EGI`e3b3>gfL-TA|Iu8FUCVbRcN#D14u?i2X>*KYvmn1!r7rY2!lr_E!fw|K4*{J z&VG5Z@A5tSqciV5f0K*hm}WizNRh;_CTi3BkPFp-;1X)inxhwnry3?0a81>dm{^NZ zY(z_yTW9kiT(E3~$5QUW#l1-&0e>}(LlC%=S49XYw_fmS&SrfV^Db(SmOjSOI(V>- zN%vZ04;QE5Jgg$m)_m|9s8yMt=F;*)j6xvd2?c#8m^4Xr%k0D%;r4GZTuCieuUBIa zg`*8`*I_c@`$UJlmkiZ*V0^C=E8i}WTwzJc%Lf-Y3IxVvk~P6>c&%s5!vu{u%MvkD zTg(f>TiNXdI*C;FQsahjl&p)ZL5iRVVMQt~iN)&gx;m9(15)(bS&Rt|w3E`vzhtD> zdMkDf>(#c~otEr~M$j9N9%WTrf^Mh33yx8m70SC=h5#Tr$2)o6SWg`IPd_+$*%YZk zM?%gaLH5rFQGACH(o!WyRhN#>r>`~4MEBdo zz0aMedq9l59Dmc90cHzMHzbzO!1>Z5f!mWj5PAhZAqkEYf%2=wX!FKFg3o!r z*5lEg?AbLfpY4sJ?pWvB0=!TFFj8Q8(0BUimrHi(oZ=o<=j*}7fkInIilQgC6Fc90 zj!2K>&pYQ{>+c+_qmh=`1KOVwX=NJV!J$yP@jbBW8lo}M47m$_!H;QG0%D9W~nAiaTt?w zBnBd#b|kTS>322{{}Gk$FYvodF`gR+%;rB*n@e^5)E6deEAvG z(u2-|mRw&M1(O2zZZcz~BMmGta6w*)2uf~kQWu}e_3r8Gt43k91upKBQAO&NF+IH~ z6HX(KD%!`$W^H{NCPeDbDxYd}bl`B3flW9$6u#K$_S&;?y4(oKS%bgL3*5*o*>$ug zjYt<8Cn2jI9jb4j+ep4T=ie-u9q(|%$^LOx4T~Z6i$$_Vnc~r}t{!y$jhKEc61T7& z_tMfrn6Dof#M2tqY@h^g=9p(2Xw3-`zyG9n1Y{F0FPMJjCOnX(IJHK*EsU-4nK5o; zF429#ZLXY+`#y-26qs6{5qw^5#MbVOC;2xb2Ce)6v{RM(OlNZXSr>)gf~GSY#Kl`x zYDN}s@Lr`|1sG2#!uO+Zsv~FT*P_U@TqDn#J^o@JVEXf~pw7k*(~%!{ z%-5iJcq1jG;-CJg;*iP65#(|}a^Kxvo+c++DJ(+G=bFcdrc)W{A^SAAg0_^N#w-u-1wvM%DE%Pl$&a%)iX5d(St&h2ZI|GR1hj&c^9*uBSk4P^D@DbsVWB0vVTx zB_F|QY51VBwH*1V$ytzmXz}|gpIx7r3Zb&6HLWYRn_FHkTNs6l-#2sfXw(LvF?ess zDIqXjC55yi?h`z)S9w;kp%WKyhJ}+$mfw`lV_)7d^*IuOeAW|7ESZTzue>mgUXrxZ z(j4T&n2o>KDMXNFd87(hY0m6L-#=lI1>(mW~ zuv%^%k6W@Q@>+~j74@cOwxDbb3vBJMwF{}g*8Cdpt~Z}7w3eInySH>MlZY|lgRv5C zrv)Xj_dnRe-uB=FZg@y&D^@ho*`tir)a0etIEg?x@75<9vIuw0|KKHfG-YZkBEN}+ zVN`z|1LnK9o```D7|1K;9y^)|geSi(W%7_LBMfP{f%;a6<{;IOix2F>dxxs^6;WWz z0o*c9Hm>&1s@I1o7cbFdq2w|fP4+ao2#Gw_!C zgcd)d-2F_T8Q(CLHB#bve)1e5+|{!R$ARkKv8|+mp+`m~K%H!J1^-s(EY8_vEo7%q zJJ{TVIq7`Qm3lU?lr@=DeOO56sC&7Ub}~o674%IC6}FZY7c)SVCAgofN%P$mQ-V?E zzsw03t@geq;`V`@Vq+(EUnUgTtv~sZj1?ypG)7)GH5G4fGoBvRvZVl%AvweV5FfWr z;HCGQ7p{`;{0UoE;??=ftXYL#l957{YgLJQiN+sv?zd{pH<+r$xtL$tE@>0P6gH&t zanGMeJiAVC$p@%gp8kDJ<3Nfo(Qz@-$^?xIz^{3~FL9PA9vS0W6H#%cUL@6eVr;oP zE|b}obCe(frQ(VfuHpuAGq|rv>+mfwsC?o!qQCItXR>I}=QvzA{{U^sc+a39lUH6- zTtd!5Ik_z2T(5>Dw@@~QaO+0mhNpO~>=#`BcDTRm&rmq}S|c9AtLM3N@8FXVBZ2hP zE_@3UoANd;o(KH386d;~)~aN!lGnks0**cWT?Hih?b0m2oxg6s9Ty6U`#&HIDtI4H zdi~z6OrNN9-huf+oNjx`e=AF86ku!{j}{NSIGZx^4={Bp){xN^9@xP0biW&7K9Siu z17xC#TRMZ1K;SxzD#Ew39QN)OpE_|PuV@LMyY7eAJP*OslkZ%rU)tYg-lHWIT)2L5 zyW%S42Y0;{yfk~E{}?;saK9IxYDn^ly?$`!XX$y_eR!l|e@k=e`bp<4Ch;s+@P&B) zJTKR;%cG*LB}n6uX@j$d@)dkuLXZ)BadN*sP}eNjzFMX&9H+e9&lK#$>D4jwul_7O zN&NA!tbok;%hZ;*H!-2-qw7Ig+3k?7sjQh}ii(`}%X4V=vMQV&iTR~j5aj7gtNq?= zDR!E(z&cY)Z#_1(Ii>fo3T&`(!#{yhZNsaVhCP^R81DZKB%}=oGG~8CS~uF zmT$cHsoCn2vaKjfu%2+_R$?@wDjNhPs8AtIR^PIM;eeYY$yTtRRE!3`XVopC7Vcnr z1575aNy&HS41#@*#OGKN3-SW%VgsHwPFKNdX0=`HJwF3EkmUUoY}Hys6@{A4ykr|! z4K@Gr+*qIG7sCbW+;BOpud)C>{>?=1mvRNZC-HJPp+m$0c;O^NK8@rh8tfIB8hw_i zCZldJAG(2$*69+M+!u^&7GByzN~UmgDF`R2>X*Ra$R$5Z47^I^iPsu+o7=JmLTHHH zawvg$+B~FzmyW+q^_Gt9IoF}CZBU4MB&)UtY@fnXb!P4n9nO;eYzHIm1AM5*Wr2a- zu#++6gW~75+CJpdA1$Ae@MtvKiC2_2Lp~M`P|;AM_F&MXl=l3$0)frFO%KI*;^21f zBY)HG!E_R&^B~QFdc~;4`A;*ruHXRST*L6V;w?gu@5^o?9R88i%5dDec?S^FkDY`( zq*)4e((6ar@Ov;jAMKsB%SyWj2wnmB816)RFzw9qP>IDYK(f+MM^NR_8Fd7a)g+@C z?f1&cm_kEn^|v0V`oVhvfDZYvn~-o_<=)&T(8C7Zn=qLv#-5Mcn}G(1d?F_#p8J*B z>T@;2Q@`Gnr!eQ>x2Xze2~GmLathvnZ$FdTd4<2t4ms+wuMM226m)8GPaymK`v}3T z6vO2BC3PZcr6i6_g|k8)0zr#~Ah-^{_eR;!8z9GmKZr}oDMhoc$p?8BZ|F>T$~EW~ z5@llWC;#Xd!!S9YqwzD2_k}tg!%;D6JW*z zrnhXGS9kYfMKQgWw&ujK>Awmr!E+&&WBuh|>5s3x>g~kGYr0Q0^$8lK?bbZ z81pqP9;wd$1Mh<7PVJRoR~OR{HfP*U8c_!+>K8uBr*4A{i{#=9D^6XWosZP+{>H&+ zl&?H&a@XMYAB@^`yV~yO$lS6ORXv(OIRF-IwB%^8v%{$F|YW&3fIgu0a;P`T1TyrZGA6WW0_hQs!BfH5xD6Cy_BMCawl}-} z;P@c?a+z|fkU{yY20}~1#?4HE{-?RcetI>#Kvbblb=Xbht*u)@6UmOrjyb_eO5Pxj z0q3^C_#nGaTyDc*|7Uu-oEPBJvUGG(r#;S6(pma>*DmGW<6LXLXkMrNr|6emEN@+@ z-WXzJoXnM9spg4+OtFKa$YUP0>$Tz3;$1OG?9O@4{6!+Z4q59CTWE$758-B$}wiXM%tr=PDrC@RIEG5IVE}Wi01%zT~yBxSz{YFmkEXjrKZ|&E4=Nku)5t zsua~80Ol4AcLB{Ww)L`Qvc^Tfwd^%5MdI&;E)2(6!YV@n!$y>pNe3SBvq!o<3Y)B< ze`F3!yGz!Z!&9lWui$9`6YEhcjPpNypSToY`L!dZ!k$K@RI#@9%voJlW6wh!J7u%8vSP+~fiS};*#0RKH6oCj8-zujx#$J4NpUJa z?wr^tts3S;E~V0D&w#8tlMQR417HcUvNGtAnbJxSdbW6}FroFdiT!ADG&w)p*jz6* zow#a$w4Qm)4>Q*dU=Ym@YEK8ZOig9%JD3bF06|D40|@f;Z9Qk{>RjA7l_t7g9^PSS z60Y1Rb}hc3oIP<^xg0VFn=NUn57)Thrn{NH*C-;yd|l7aBv;IxF3ALJ_3WEZjsHN zFC1LX*2HchGxM+Q73Du46OJxt&Tg?6)GmJ;D+(!jM)jjtR*y@2SKWWDVsiC=urI8a zJYWPN7U6`-$MafHDrIH${x~~A4zb#xx~=K&+B|G$>WLabGoIP2Sv=WM^$Sv;WKxXm zTXGwvbN{0_W#yVpL|lG3wF7+04An#f74z@+&Bnb7bH?+T2JKUk=Ctw ziq4r{H;hVbO)3~06cQ0Ru~kRPNM4!N@zOue73B~l{RphXc2{XkU%;LSkTL>sXWbZ* z9I?tM;4xS^jX3n}O`KomU!%`h-IYtEVf7KnqI#Au(95t_G`|)bOaX z03s@Qxh-@&=K-keL`-UYoj}U8Dg&&Sc4fu+uY<{;#!CsGum~z?2W581F?j2}k<|i$ zT_Q9Z0NU_yR4R^Mfu2h|ng5K1Fx>=ro=C4o@SVbKt1$HBT#B4drfY%O~9w z`sUTWs@ep06O=u-8zW!AqV$;F&74Wx37BBy{&&8fjQWnU? zd@d&4eFZSB#8sN2>eVhuo=|0B?Q?tVC>ui~qs%E`K4CSKZ-J;& z_UV#v{sUbN!GdnDSc-%x2_oGewdB?udGte<_R|4frpHOz8#FyS8^ zkz^~*Kec-+b9N|Z+^T$iC_`d(!ce)YwZCMIhQ0-VKHr%?_HpjK9Z08dAI5m=ruW|u ziw5hcrNe%$G++ZaTS7=1tEH`7Y24v~GB!PMeJZHQod~W1n{mm)p(|Vfny_s!Vg#)G zn?)663Lp+(xfA{D!92qUvbDtK$Uf*2`LAD=)P)HnD zNK3f;)S3dSAy4k6uHa~?Vk^T-r;p1|+uoZwf)10two)t8e%w8Bj!Y-!#bm}&CB@FMyEw8 zUx=*l$@%hii*WqxY9fICQCh}Qkj#Wn=!_>ub#b-_$sJIoh`z1cIQ|ZPGG?rc=A8{EaS4TJX0LXCW0a?)4TtKvNGIK{5zUd~zI5d-3g3*R-*e=_B z@+`>Lw+ME#k=vBDD}vd7HVD4on5awF9CBMZ_0qOm#+3xre!{a|?d|RDJOC5c!BTB| zAOuXd>D!NDo9YV2;o>M?ayx9`3xN40~w?~C#%$tc;e43ohY zEOuinWoYUjxcRQ&S@8nlV53EdDw9V$Ap<~hU|^uWH(s(&+1a&@48zjGb67>Ws81|M ztve^Llv9q>i2Cy=Xm6u(P|TVX1U}(Aoh%4k>h^0rS$b+ylfb+^6tF0ucz^3QoWLSQ z@F($;3`s21LUoS{%{5y1*#ovGuLo@){2xVE8P`-Fg-1w9jF1-T?(Rluq&ox|(j|;8 ziP0&YN{*6_(KTX#N|!W94G;n8ckjOK%XatMz2|?^U`bd>+4MpV%h-Mv-saOk-h=Iu%7rgh+7bJ%srz6#2T?(5Ce z?`5e^WQG`;c=#S3engu@CYt{PW%Xg5*eI zS!-$a2M5WEVg_#Z!c1ZVuPBoIG(GLZ5fwKrnE4fyM&>AO;-A~C(!Ur3IMF~a9fstO z+@+-g1t4~3Ut)1IYR#zM{PsEZH3ReUAOwZrc#>*H2Gddn62IeP>>>|%s}BcE4sMto zQmTE)u_w@MNoU9U`Z*QLl%Hqd=DGXP&mjTKKF*oW(iA;oP&@7%**9$BcXDu9ozBf_ z$+k!x^XodvrHp_q^*2k;#5~1$v2$_;S)a*|RgXzw9SY4--%>fcThS9*-HfO9xu(j6eJk^i}Nu`EX zyrWBdnR?MYhFt|^#f1r@dg7uFRS`eo}bMm&M_>OZHCp33z5h_b6mtZ<9

se8zd6)OSytf9_8v#7@__;_u=0vJBEsiN>$^?ytbFmS7*ul4exO zvlKKedkd1*{S1E&pFSY5kCLq)R?o*yT>W}ve(^xJoq|_(SupVXa4o)8qiObe>Kjq) zKlmb|A`BS**r@62B5V^{faX_%0yT*i#y>2nw5Qy(IRUgD20rPY!vvT(`26C<|Aexk zux*h5*A|*R>=-;g79YzGDc#onBLwTAfBwd-lCyn^_sIIA9WhT{Ud1+UX(@1#EzOnQ zEW|^hXXz(yN}JyuV3oIaGuQivd&+@wVZ~C)+^AutZr(LT#nP3JkCzaMBaz;9V&LQNt>gl*s{X1x!d5YZ0nl21J z+q195ZZc^z+qcNUSR~K>%9}|76Db%3dTsO&)LCtUaKyI$2^lO-obThYFO4PU(+(+! zM^F{pi*!_VWM{eT7^P#$2Q5i9-7^F7#ycu_ zcJt!{#>hC~i5a=JmEDW{gxS(VSkbb(t<@;37Ui7_Kn+ER-?{%o6nc1W?3!$JSf9fc zNgNkpiUmt(7dgUkWIWF_z@rJscqL$b&HOPOvop#~_4yIwKUz4$xh@@14st|P$||*l z*Ijp&^Zt!T6eK5#eSb%#$_ITvADnktbE5dt7d9zOAfW@LRkUFhGM;?C3`=u{VU{ds~t3OlVL@eHSJmWEA(}J zVPw1(6Q^S4f#8H4{t*;v;L~ljpTAa+6AMwm?ERw68xD2Ln}ARAJzj@~o%XD|&c_!M zIG<(~ESVA1>PWCqnwolqe4kpEv=H_EdtGRiu3tK? z&}cw}Y}Vy?jYJ*1kiJ94spGb%}02}A22ROc5tB{wIX!v7r06ToG6X+hLZc`0ahg4gzT3^Op`bw|wsF+S|(<~#Z zk!V?9K`3pjl4XrLp)x`Wo+QsQ!c5x4TD7@PI3X;!$(IWh#sDb;3`y#MsU1@^f;`YA zvhmM*AE=vm&couU8|TK`xMj~gthX36LM66Xv#IW~iQQ~#_OH7oF8?UTb3U`JR&-OG z+kR~%JY~W8C)<$HaFD}`VZz@FjIwFyTRWj{+7LmZD8EG*2ecq`>S&j_U&1#JvzPTQO8f#%q*`@+*oJQ{p_3#)Vqd}t8%bAA(oY`JUhO^9e zy({*K`c4$j4>_}|bNzzP51TV)=Pb!SY7N6L${p!0b|((LKHWW>yegQEKL0%&*H(P+ zw_o2aEhmkVXNjoDWoP>||5Z?DfNm~)`osB#N|s#a!AYyEWU;DONbIw#F>Sr+P7@&m z%CLdB9DQQ0j_@2^vb`t~(lLGP1VQ)mA5wl0mS zt7@iD>a4aj3-fQXcW|G7Bj;NSbUmJYBcb1JL)7hIYf1Wr&ZggML9F2s7wNpej(*p9 zs?Ak%{2e5-pg7*E?^n=1R*t+}zkdmixp{$x`?R%*1jDW+f&3Y=NxliY+s!0zI8IqQXA8-#QwI=D5Qa7{@h`>+GBLCzr1^0 zRnjEpW%h6jCkNRkC4hsHDSyl4z2*=#YgtRU9-?J~FZa86M=q&%3b2$Mq{mk4St-b# z7!_N~5Sid2oGlU8;a^IxT$N#{O_JCoESz0F3f7p;G_1>)=Z3`E9dqS+J%v*x9|x2GxN*gg5BN5 zHEGMS1CSJTJPzQ;fodyT{Q0kQqWOpWIgq~<=faKhGZB;Wm8*N?NrQ7|yw8n`jCLiZ zZ4X0GQclU9aU4hpoCOeCDK(^q0|z$G`^A&8Rt#&p>W(Pw)W_BgKSQ!}Xpb!xXid|W zc9D%b{=|fe5ZOJ!OJ#$hiqRXJ1E=>2>H9Tya>r14mkU$<1wkIrC%g2l2V{5W4(x)c zN1bAE$T&Vmjp+5ThRo6{p@FM(n*zMM7Bk&jgJV77&9hn_DVFROB z3K}6LhM_|3TMUaM%7~P5OcbkUttP`DF7u1>kOyM~=4vDZal9HB(dn1$S=Z#s*AVQLa5!qt}VYNylypoOn%+Q~RaYrDn5QKP@8OUrnq5 zS=XcHDvh+l;ielo9OB_oZ$AneGL>!67N}SXo3FgAYdF|$-*Kw*8I{)6Q%9nS2^f{= zeXfaj&8!)jXU=k=lRVx{3Qhn-z%b|;f${)`==!QcQ32X1w%^ajskv*_shW}`i1eqv z&rxm)n(sm|kQ7Nirb+r?#rL0=iJ=piN# zKwpd{@OXc(LK=}GLFJ937i%m%X8|3@^iFp;TpJTNI-`j zYAYV*m2Qp=(es;RobN`|3RQlnr=rMq5 zBtEry9FW^A;R3H;-Leimk}B%{BYC1-u&`AjgRE7cL zxE+X)?9+hnAoA~Ae8YQkbA%4M0O_LlvMX(XhX-KQ(6F=BthN<)`NzihiiJG7DOvg$e`ovYPA5M;w=i8%uk-MkjGR_UIK2+fq97UeTfh#Q`&4pf5eu9e=kCew%Q#6$9aTHqwdp4q^k?r`@|qr$ow-ohAPcmujfgo^4gVzr>e)e$Y~zKuD$o9q``@43zjg+9T#Op{^B*4_V3 zLiH9dx6bFuC@i9TZ+bK8<-{L%>t9Wg!>(UhKX#wilrJW*8xkLJjVd~+48`R! zJ+FYM%?Ot#2v1z~J?<0A-F>+~-VQPt|01UTI}!ZS4KCQO^EuDNR_M>b)VIjq59k6? z#6mHn3SL^aAJvd_c-JGQ#2Ci!T%{B;{AoeIW39!+@~8kVHB9&zuc zX~hc|`@Asx99s?Da`q780!0mX_>G}_u0eHB_EMPRc&0bC3NsdXqTG?XHp2jpr@<>R zgTK>|h#jwJxsKTSld_JpJoO<0U@_Q=gWDKB(Dc)`D(H8$xsTj)3#zx-#95TzjPddN zY~F`F8zM9!p^xDd1S`J>A*YaAR@I+kYH*$*w=yBP2wT)9^8b|$AQ-L9a-;kx8kHQ? zeYbBS+X=4#JIqZ`5D__q_^KTbDUy(d!3(3Y*)`U^6fTlR1okcQPT3M%L6>Ang3dQ@7M&6Uxpm1l0)yQ54UnARZSfp?K6 z^xSRWZHK`!JMEUXGHYGfVptxK5e#?!yNoB7zu?K)BfD?klNY^ouylJMbo=lsKd6yYMb52JxTFr^HaOf22W1KkckUYC&N`IrL?0iHomhw*#|9pqzNw7rwU!bR3 zquN>kVI|f^B$u`atHseK+KU^PpV_9#98wxP&)TrlUV9n{z#tg--^5bFf)^tgOVjL< zwB)WDgy+80Gt3NGe&Ay$>Vn#l)pg-Y5={D=5%@36=dnm0x&>#RcztcKa{l6s;O;BM z5@D*gFLzTXXToauA-r$96N_CetdKcNV8!*Fi6wz0am(x(uYkHPqis3R*?!PkxCF?- zfQ5^e>P1n$J&%0V4Bi4)u4#HouF%Pi6}W!ge=3p^_x~v(g&e$}KzL6{1uJQR4_{*> zhBM2XHM`8TN^{c^j&VH{S@~^7b$dm=c=rbKZmgUmj#)l}Ijj(A(gy80csV7Jb8tvv zo=G;Gf<#vUPLno9MvdYid56>#yRy;(U@|1Kn8cd`pd=!N;1_;O+%*VpMp}#pNmNrZPH+%J7VNhsxGMZ2|vz9Nl`JLo?n; z97j9_CEOL&FZ7DvX;=|TB*pix^SMs=b*V5KMer=OFGw%#;TkV1c2KV4+oqsJF zpZjO*wR!35MxOlIl>G3Ip<4;GWw!CXWh2k9!BW?WUhU__*xw%|gRh49yrxjXM1hOD z>r?Nkyo-xDCwma`m#|h|Cz-9U{oY9cM*o^8(beE)ilVACj#P~a3z#TeZ+3e#fI2Ha z{&~3Hr94U6{XN@Lwb!!MEPiF;A`zte78j42}>^z8P280vMFsW#8Otukt zd+%a!dk2IFZY4q3+DFqGs?k^vsy z<=E08_N#6`vxjTz-rFy-Ttv}^3E;OcSLEbRWG=6Nm-vel`CVm=ggTw z*vm>Y%Iz1JT}v56+o%t(p!t<&w=X;VT(NJI$O+OkxRsRNHc8z70$5Ri=?=ws%1efi<_E9nd4QA%K4}=mHgoU!hIh zuHtc`^KAwuzo&n{Y6{vP;ww$AZZ$}wGKbp;l+W1*8g-v;oe$Y^ytTtfIR|U8y8)y~ z8=gDv*nilujnXleQwh~8A(yq3TRRfvOI0#9epyuPWb~pVx+=0mbZMlnKjy8WbhN~V zQcY8RW2!-`lSLzm=}S{7jiwwLboXMm-OHhku0>9eV!;ohR~4@>Vt zFwC5^G^4Z54wbmJfkaW6{sA3!1F=KBGmi9W*LA&{k^_Qe*AgCxMwlJ5E{xrg0DK-1 z?*M=5r_)|DFhy0M?5l&n0k^}GcPrwdhptcX{rl}#6TS=iMeY|uqD$T*rZ-o4$i$q_ z`4^49)2edCRh4}>d>GznzRT6Q1IL7{J?$ljc=uFS%#x_Bn7zEd2yE#dfcUlZurm zU-3Hu_s~_BtcmI*A93K!Gua{ERX#0o7OyRnz85bto+V;Rk5eVTy9h)*{oB&_Q~Y9b zcc|RUt@zM}s+f~jM9A{mm3LrpF3fS+~uMAODgz6L2xVn&} z=QcRbmA&Mv(RW&n)XH)3EE;nEd#_H;R^;a^J6Rgbo-?1loZpoBOrDhK%{x}VhnlGg z>%VeOTaH3OhkPL$)4eSD7raD%ibQ7wDAw3}0Avi8$YK*P_1E=iMVnWT=0}!5Qa$&6 zoNtG%q%grS-~t*D>9Wk~J4l_pGHVb*n_Keojss8ZRi7W6yFFqTb=@>KiCO`u zd^%1*l^@#nN=R;HL*!1X*9Vf+gH!}43cam z<}iv*ZrtF6(c%k5q;vG+){6Qih|?J=4ob9X&Q+`(l(*b^elS|%-Q5mdin5Q2w->KJ zs5a4guhTx0drn!N;;qdgaM8%kn5H)_@k!^^{>JlPuFH8>$4kFkXJPT>5-*h?piFPe zT2aPHL5DUEPHW%6^g3svwu_}b#33qhR8FS%H`~YHHbJIZIy(a{q>qV@J=6~&^~gnx zyX-SRYe&d%e^8B4x!#q*rt^#a_0QBCj;yLH!b(QowHSkPY1(h3NffH_cR#~KcmAiJu+DAn2;nZ@IeZ}hc z8dA)|kc-DU=i72eA~F3v4Dyc;(8S3uIdxUzHHp_;)pk`E2C?D+SvXZ0f~R|g{Y3hw zEK^dGWTHQma-{bh)~WYrY~;9L?iCU>(G$R%0I=4n#vF|URo3pztDt~vVIR9*Dz&dL zIInO}IW(Scej=44fc}e_^AweQ0^!6>reTKm}7G&qY2_ZHI57p zTAaD-BLF??MDHWm-Xo0h%@4Xa<&(>G&-^X7+xjzzS?FkEGYfckt`1ISk zrP(y=e77kCjNBey*`Q^$h0pVO5>5+h{q z8Nmnjy%Qodp<*ccX#r^xNFr&Pkf9wq*{f@?J_d1k{R@sg>4`>(n zeaw{=`VYp+-!!15hOS@C+T36+h25M*|2nSvGIx#lnc4 zX`i#-k-?JH!F%BbhV48Y{vk!GG4o49aFix337*ha*XY8IZ*OlW^TbfmPk_fQzQ=(z zG+~b3ZmImF@=k=mx|*9i_zu?V(kY$ft$sq)W7Ja)z=hZ`Vl~WJj2!qau2n&WwGs?k zX3bPe=hc-h7fwcC`QE07($jCb^$8jF{5xcO&^=t&-{kHePoJzE9P*y- zU)^UR|FVVNJHQvLa~YalAzCx;(M?TkI<12s(pZB{La^pc5E$IHe(GKLPE#2{oBn;F z4(gEMp{IpEJIDA;J%*(CFQn$>5;Iv|b| z_CM5(Bs^w>J?8xVFZFz`kwxE;K9os(A??pkrl0<3trZ{{i+UceAK_VTdFgqj;GYFk zddn^>J{oh$>qTeTPgK^A7e_~@T|q)Y;CZ$qkISxC1Y+P1&wAAlcZ*yGD0)}A?(ZC* zy8XC>(cL$Q^`oVb5}$CDJ>W7V_j(fHwAT=iX?!H0QBI>OY#~%u>}dsg7PEb~q=>I` zRx)}|_jc`z*SiXZq+2LPzuC4#b5JSXMF+h9B z`UN%eWN(6aY`at(l%{(_SKA{3Mw*^R@!AIb$2sJ&q38C;BN6Oe>j@bzCy8o~wobbf zgZt`}>2Z;DVU*Ap&+}^ogQeQEoISY)JzzD-bPhj;T^d+MT4dfDhNS4l3E)+xepdyG z=@C_?>0Tf(Hk~oUUS5x4%9q6v5z{J{k1T1F1injcR~4RL`8s+UJxXln(Ft8_k87OmqVg z36KF9JS8V%R|m~ftNjB%@b%T)onWHk>+g=Tni{6H*-p|EyOfMDI=>h=~XR8c|A`PPhjeEt)%v#>4lIlDe znwJC_O%@2E)`9f3vtlk4u^ZN$Al!Wq`avPR_a={`fsR7tf-yq0(KaU&_XMw3{Yot0 za5=Vc=da_gk%xTkTSe^l?ZO;bUJeTWj{Rp(GGcv!eT;_M1BYeHaLYb_^5{4g?Oo-DvVt7`DSnhS3@;4QiOAHp! zCKpneB|duS)MRsc3rwcw#F&jk{YNv{C?&y~;ctV?g@w~PclS(org%nq+%K;^`XEaR zQ?~RrxKzo+@UNAzyTY+dLLi=$e}8UoubLLBaEr>iB+Y}XpFV7~vaHUqFl~6gAJRUi z_pa;^0yHYAIAymTox&+58dulWx|h~A4Kn)3P}~5N#t_yTdi&Rr_4H0F;YYfFQlxFd zMcNvt1dfgPV5}-6JTCMVxGnHK^zeJdzVXV*6>8qT+0B}IJOKAB2v4G3Yikr)Sy@FP zcVcsy3+}Sjoi6h$4mz^6As1ICKD9>bRV|XZLKPX%%95mu1GiA;mVpEekp>P!NQOLP z|C(WSP8FyAhrAx{xiuvl9{(H%9+5~JkTRJabE@N1m?ZxR;r`L2FHDAa=JMomm%$?Ts|9UccE)zP)_SCB9)^6m%U#Y4BOeV_@&NG1tm^ zy*uza2?D?$^au%&#XnhEVlDf|t+DRnJ%v&bZEt$Kt(|BbqSW4w%~6Sjpt4DXNE@gg zjcB`^3wTSrexHr)-Y$9cM3q8}C$F1Qy_re`lnS-KbP%_;zPgY$CD8gpU#UKa{=4nr z&+Y+|SEqVbR-+mqX||+q8GKp=C1HvL6z`Rx4vy1>1bA0FjQi+TRy5S}{CGNXeXSr9 z{UG2zuH*aqAIGPQ`}-@T*pp6SoL#f8!uGlH@y=PG*;>IW1fn#=moAT%O$xy1fOTRp zKdR`!RehTsPX9*5$Sy_Rgf)-Ecz~k@yR(UDflT&$p&|G1;E?s`?c@V+`_d}9RHf?` z7m#i+@=ap(|8(eoVh9%+TMt`Luk0L2sJVy^$vHdQC$t@A9@kMw)#z6WCi z6>U%uYdMrO+^OVC>Tlr#w2j5OI}u-O7AQiU9}_D=P=d#Ez;5WAowFgod}vGSJ&4gP zv+P*sXSA{J9eP$nx09M#%t(Lhi=9X(ay zJskkV$_R4H1vM?1{6>|qRaYx*vx@8pf4rf(VGhi_zPE-8Bwr2SUw?}MdXK5wN;S+F&td@RxCyIJgrQOyBn{n*rj?W2S()6ApSu$vv zN2#>QgqVy0ZfZVNW_nvarz*KXMULH%lCd~Kd=mz_rAB+ZCf*HwEk;86h9Go$M(zPx z+ZD={vCKWnU>`ua zo<(McPHrwq7} zY!Bcu=3`0@M!6tVI!%3M`m2Cb;%d&8Q;rH|iez^*ajh$qrAYCg(^JHW5Y~!;t?@Jw z4Fc^d=6>~thMJAcISngM2=e&P>p5Glo@{OT*ZRQLqJguPD8GI}Wk{A;4RhMQan}B! z_vPAU-oT|xlkBnY(WTkXjXHtXtcS1-qokOXo2HxNKZgew4h`uYn)zltW#V7I7-UnW z^2bn`bsHaiD^mAm^a3+>im%K<5!{N`M%9KWB52u(KMMa<-a$(2d(vjvhm$^vx`Em*#J1QO_L3mhtbfE8+fnYF#*;D3bGOzwDr8{PzsM3MfaMK z=DWz4P%cL#SPL@R#tO9LQxo#T^3by`h$^=-!rdI|L-rS{LoNZx8F2?MisDFH5Ln_1HaH>1CY)hiD@go z0YIDUBP_>zx%;i=Xma<_ztfCm0dJ%}Ret9XnWCprtG|G+ebP{~^!i-bN%)KDexE9H z4e7)byLvRMP&n%kb=3TLu%3iIX*{YJ1ai-~jT(=XC!FNWuz2`LRa&l64ht+(0{_|-H znbaZYTTw9Z1LsKgJ}xuBYbrYtBi(Pk`RG!TraR#JarY8NMXKNJANulZK9M=AgBK82 z@LeI>%B$1TR7!snhB9E8b>^_;B&W~@N)0@YD9<$1f)9q&v~4*NuL_e&DNm_uh5^%*nNJ!O9JF(t0qN_P@{Xt}Q=^v_rpxi3dE-Hl^$ti0wWleU9a9VgK#i0}2HSj$@vsy3Miru5K+lJ4Z@ zqib;qaYeA=Mt&%MN;_xB-qe1LY=5)EzNftS+lRMgpbvX1;iX-w1d?IQ0n-lVA(kQ-vpAzIpHJ&6X=`^49 z_I8DL`{ofWvo}c>rg;vElOZ^o3e>48wPHfxeQZV&yN}l%VFJun*w*~6_-tAX>b+v?R7{KksMa-l`*;`Y)QHlyH-RinlAf1AKtwv(^q z0vSc19MYoWBo{dHUNABU3*_(NRhu=T(uyVaA3};gXWkfO>M9=Qj;tYl7w8HFXY#<{ zmU+=AU^W#4j!^p7_h1KN223zp3J#nyYdCfkbwP+#;Z&R!NeJS;5GS4nwIn6@68bay|!IicO>zZ3@=#BtMKs{_~QrHlZA_Ql*{H} z=^e#N3(+BBy(gX8%%CU^TZpoSx?09r_R#~IB|F!T-+Ob{{c}|TKT;;rZF%aM_A`o(KwuG^7Kcc2v}ptIXdz{OmJHuvR9$=3>&ao z06FWccOX$mkYp=tG4t}vJsYtzS;OVr;iiv}qG@76x?XMix^&G2OPsb4U@G5B;+E+p z)P!Ij1Uw88ub-Z(kDgv+I$H$NDY|&dPEKf&MGNs@p+oT9%+3i0Q?oT)qUqkkihj+gf8(zmzx#YA@kt7Ro)`Yvv*fn6_+== zKi)8+>~)A6z+}A@MZ}#m2sFZ98Tc~Ga-mSbgYne>Far)z(d^&yXCtaTBRd_SC zU-u0LpG_HcltsZi$F>*ez8}8q4mU^A&*V#pvr&BYls1$qYXuDR8(Ln)z(TKdo|3Dp ztCMAKKQt`?Q=zCL1Nfndi#5%#yoTg$d4PdWt=i+>CNL_738Ac_#S?o%3d;j1IJ_W- z2;%X(T$(o;7CCwuDG-Ea7esZ6id$MdGib~pohl!hjANiLZPo}LWYwZ>k76wNG7 zCh+Qk5d?6f`6?Cd-fIzU*zqJ2t?}w6{8gvfv2a7x}<)gN~@?>(8cZCP7?eQJ|{h2&j0pFFMtQ|_fGFHlSHmx<<_9II@4Z2lZ$L^85n{TVjTvD_FlvTpw zal*`wbe`@V{)$95g(d=6Q7Er^EF{yuswEnzr_Sg_3wB+JQ>}d=k9x@W{jX{->1;10 z77!VVO^x5lLOXJ4;)Cf}_w#l%zA;ymOOIw3^mDWd@Kv0zRd>NQ(})Nf`I0(WmMe>Y z{+4Zw9~#3CLme{NH>I7%bBAAfHRmyP|NY9(Z9NkE`}g!*2G{{5H}+=#GzEjTzHR!Z zmrbHoLUGtGJ@&CL=EI7wDY;O*06KeFdtibm-x?enZ@7p0FypsHEUlQXnEmIfT)u+k z*AgAvnoJGf!%+l6$UPc>5h^a$WRvM<3o&D$#pP59J`kc4%Am0;%rAEu zhY0XP=QS-gv}2>xHd;T3a?(N)yy+8ZU!YVp;s6%f6%8;2HjUwoQ|>z;GWh=839|g! z+WzCgu(VA@z+O-F?aai@;q)Q=VwmhiOIDQG1v-mI{K*Ch#o`yjO~%hE)^B)q)Y1*{5hnc@V!;Ugz_>-bIQso)vqQc!ryNB7gI+u{4prvU!gPmkn!)<6-D@i;{ z5dB8?u-`(QE-n#I=cNGO=W^AY4j|`fVm^SW9V3otbI&&!Lf>FHoFa1dhLk|TNa?M4 zcJdrZ@g1W+meyIHr+j)z3`gGlL;*eIE5Vgn_N>9Q3lAWMCsm{UujGu7}sC_2` z*|&FGF;c|A_4(_SyE2OoRG7gbM!e;M*y1GA6K{YBF^T~(07jo{AkdDCxJgpWZblhY ziPErNxvP7M}s%K8c zlF;{zdH3i#m*uCe2}k$@uYnkq7pYce2%WJV_e&{N4)$U7{%!$AI1EkO#t0})O-mO4 zv#RPPjXn#Q0u1m<-lgBd)FT`k2$^10uhkky&EFp7f%qab2_`L`Zp^|PtxoLacY10S z@ux+Z(ycEDdR+FNO~#5#FRXK&sQSjS^W!8dKEFeEIfTIPxyNN*?+herD5cfp^1jxx zM<`bGA*tmX-d08`LQ0c3^GS7uNudevLI}J8ACoyS)fJErwi9&YKlYDW3q7$|Fd5IT zec#Dla#d5zBJHRg>yX$Jnt_buwrpU*_8FP)t%Bl4uge5GNzO@XULTE!B#>{bc&ImG z;za!xMn@|-=k<@n)3xz6eEL+OrT0-NB3{IA;%Y)XhDJNmgyU1KsA(RcoBt;(`GL!H^xMjR5`a&Pz(mV)JGUO z+wTDtgZc55)?INhf$8{znK`KZ8UX3L^74!{J-naj?VUkArq)B0hE+Fa6STjbZ9zzF zG64T4N@R)a{gy>^h!p-2S>WOJg~K{J|KAnLZBTIktBO`vnD0wPak&f1&T_|PP+5PG zOke}I#*DClDB~AsixFyq7|NaVh-T|Y%vWorM_`4!c*-mC$$pGV2lgNX4IK~@`1 zfg=iH74#R=+HTbwi!w|~5ePOm1TbIfH6xaYNs4Q`x zI@hqWRGpK(#%lbdbmZUx^0q}1$7`DBPC8nUxH%wAmHK`7$*8k!9Ifq)E^(?E7vO&( zAkvCHDxO3lRT3Y#VAc}+R7Oq9T;&L3XqM5y{3d)dFy5aQs6_S>X^@y983cj8z$DhS z^{%c|{4(38qw+Qfo(a%=z4VRdgW;E0^7#a>kF{apiT0!sG9F;*Vr<4O6tRU!nHd%G zSe+b-y5b!);yG+&9Nc2cCAiak--%jJ!;Pl0ep&ra+lk4H!`_)~llpZMJmg^2aufNU z*G&_GmJyLhl@0kM4$X@(SQZV~RtC@_JwUAFwx)jg(ekt=Y5b`HB8){!6{Xh@Rb|vD z;8g28?&0-M(}+|9_@i&{!Ap8Kr#y|kCO?t;(~s{8t{kISz{ORLK#XMJl0iGkT7uj# z%x7HBBB&kT;WT_P<=jvnQedvhkEKfO$BaVTDb@SDR_lHe{y)hcYqX}G?xT~_FC%Jg zeVMYVstTKeRyH>t?KGbs5jNptIf6|P0}xww4lMmsj=gfRjf*LNzcilScKBC9g92Ox z9l+b8rIcQU9!Eb6@`=-e18Q;Y*I8|h?HvA~K9Zyi&apj)?L>77>op@{qDIk)TuC8O z+a;Q>EpV_Hz1!0zio-UQBKyk7|9me?nyP118QNYgK&&I zi5g&K02(ono*0cNg`PMZ`@&^gS+~Bn5(?Sj-K_m!G>L!IKiS~d+US$Tx}2-&qkTHy zLdZ96*Ep7RvDIolyz{^ppW?RK%9u zW#V&882GrV&c8Ap$I3b|{1>tKKd6W4%eMPl2|5Wv94{-4UZS06o{>eg4Qyxw(1`{G zJxTipI?}&7VXjB!98UP$-UYTan&c70a$F*UH9C}6klVyKvT-=xC^ZBz;rEw+$YH!f z)|`)t0D1K!!eUD8jxHH)ha*WBu8!1qGG>U2@e3WuAe^#P=G7W zp@_DBTkuNNj57~A-7iP2hL0j_CzCVM&OC2T4lB4xe^e;&3?*#xmV<&Fs}^N{k(MwZ z65itttT{>Tx09UOt`S+jTKQC>Du&T8kQZ+V{!xCXvQk-jG+XuK$S|d(8HXN+oh6pL zcDM$}9<8m|eMF?@thuiNn&3gtl-6^#bbN66S!?kX%4aJiY@m-M{Y&&q^DlcrPKS0- z&(8NG$>Sp#KpOo&92_9*B3UEHnJmiD+3fD~k(qbDb8Rk9Ou6vOYaca6+hpZPX240A zOdJj|0~IlnkXk$EPQk({2il^`BzdB^_a8}D8P!(Tbb}Nt777$7PH2JRPKy)VB{&p{ zyBBv_T#FQUcS&%!QlPj7r?|Vrm*@TdWUXYa+UeJ2L^%fH9Z%Cwkv%S)yOFsS#8VPe@@-06t+$nyYm7J?E+Z5hqtxR@z*Lfv$aj+Py^ z!^SJy9;c@W)j&7!D{CQ*cy^t(~(FpPwIjDS?=Y~6c*R8h)!)NyQ|X-Xs+#;pt# zoZ`pB)hWfrg3RpI+xj&2KEL$s;pa!6){`fh&#g1=hU>lGFJ`o~1T(WAB|kC^ZR ze;T?SB<(yiIM2DB3lHg}=%Y85SS+vm%;WX*XhQyV45Zge%z4KBYW!sI&CTeVm)Slv zj#X6>Uq_t{Q=Vloe6U;&E%}+rO|o%yqaFBjV{^`<1KX43-3^0+XJ$(kvC`bepZAtg zq8Pux^}9CF-T5wW_Q%~07AwCrK3aF^klu@m!*_j(>~<5NRkw+6ICBY(Ii=j=1Z}=- zg|cmFWfzT!44qy-YYkt_;X5y0PX7G|ltr{&IR-kZE>7#{^p3e;XSPZgyk8a^X-PSz zw2F82evkso4A2355+^$l%hi(*cw8lyQRlim9aqM-vW*Hl%4}{i8*~Sq1_Ywx_Ntc{ zWknVxx)`SIp$tuN7Yb{*@aqM)Mh1MvJ)m7z1q0UHb;DQH3g08Mmsj%aXA{a zB{^gPVbqw^v`a#?v+3QGh~7egYfYkWWpb0tNQjK+mky$gaugdN3#180w*}CW{0#$u zA(CqK9JDe;iGYS#+91=_n5u-hNmhzOr>|s36*PhT$9ky$tt^XO=Q}FxNQ?o!U38#qZw)aLV_CVewt;~`^=zqT z12G5*43--qtwb}HJQx)?oO-@RNzKkBXbb=dE%ILGoi50?-fww5ZJDKyJqY5LYTDQz zLcGM@cHWM)qW%s-jx|&hdwF%Kb@S5g06Ql}Y6?x2L>+p$5|OMZwADs0qkT7thBmhx1 z1@)o0{|XFY(on@xy1#hAdIC~Cp=Lie2z~xNY7~m+^l)=N`1V$dYW}SymB0FPzQ|4N z$yL3`%JVkWPJ2*Lly*8|jnB+|yGn9!I2Cz$JA{8OYGsL+vpavpfhdYp`Ht+^)cf*N zWVKKJ(jm`1?JFlf2k7~-bE$GNV(kziVU&S6tJx{L^R|l%jE);P+IYP(mhA3V|IY!| z((e%CboIt|+vMA~0o-tu$ZtJY*n>SNI->{p&o6XRYKb*2%_=kiRg& zL@nekQGYytM;SR1MK(^n;$2MT@#ynR)UYti+sd*QXdPvCI1uiMFq z-JeGPmKc%#;3H+HtH#2S4Boq&9i*g^qQ2M`X^zcx2jYvf0NQZb|IAL1H z5hKZbrPD`d(f{x231^m#t0)L1WDUqKK-?*VW^gQkI}#l(>C#83oX)G8_Xz z012Ze1p@L2lyet7J$~_Uy;o#(lch=9Vlq>#H8NpkM$mzD*+LKp%2=79jSuj2D_!f< z#sgzMs*-a+82YIu{A(opT4jzB$BANCVtWznxYdoytnQ8k(^@Q?_b9fJ&&op>JGiXH zo3VdcvLJEN{a3NDPX)cTT?lfR(O=7%;mHa{Szv;SEVO%xM|N&qjFH=&8^|slRGicZ z4d>~@{_q0{4@2j-0k!I9A(K@H<{JY|Q9-0IR8tS+K-M@Sx_^LaTq_g<1M<;bv7Ti* zafr)cM!Z(Z#|GSd&wUY!51?fH@U{C>M2Mp6&KdXr+=Ns8hW}9;ybX^zl=)F*q`ij4?F0IalluG3KMWySMg6foc(+aOf)MI>P|&n^ z0X4uKyoMB>8235Ms51lFy;CsQC-gNW={@o~MrxeYHhuAul?_fnnV_RPS7d+;q7XfS z7D2-SfH9P;RQnWVWQX2V@PzMz#6kkmvGF)}l~9qGvS{5U)Nq?6nvzn{=Z)!RfIt<#KXE1n0dVEMWZ@Bf?USB7SL^8JRFX3?cqn zCKu6ZMw=&h0|>&V29M);X6-mC3!qq5#{bT&d!+;vvl$%$Yk+By0i?$)JBAQFo8$L+ zWduN#ZgmeAq6rXVA%MI@z7$IEvAS7DuTTjAcuXRv*I&(rnk`pn9vxJ7KAy$;3#LRY z6Fit*J-hw&oRO82B>~3~im~uW9uZlzzt8h}CvOmgfJ+&71QiDl5H5KN&!u?Sb!m0g zXvD(8!k4X5$0ked4`XE5=aIZw&B*lcOc1W2O(zH9!A(ShqQpr%!dy_8h~QMQB5*gv z-%f5GX48r-$|uz_$%dW7n+jxP&F%#wr>7?^}oXno@##~-|LBFP(YZ?K+6o zLfh{BhTmPMNhK3gUKlzCokSPa^PQWY#Sqn-Zi>-_u=`x?j73k z&DQpI(QH{6Z5_+zuAg`O&V^!|b?DfD>fOfF_1??veqAnJvm$Y@z2|l8e!aiS-{%b< zk(kXhj|ymnj2xyYSlbRH8Dc8flP_44h-}olY-ng$w!iM!)Y<8f#J{!C>AO5T=BIiw zb|YAAkoCF{JLRcUxLTy8?SA5vdHLd?dVh6kIi=I`#K5TCkZr4+cKo>|M8v9ud(*|8qYt!*h5cg$OU4GR_52G6^l z3xE=6LR(8qOUKG|M0)z!l-XYK#F0nqz>%i|tiyjIvvd6AD0}ItJ3Fg$qeY~*u5$&O zA#mbM7=LwOf$!8Iht0m2ZVYI)5f80Wq>vV8ru z_s(0kgIi}~y&v`2Y+g$0qufQ-8_q_xr~I1FU%OgKe5o+7g;5y@cZ$O3 zp6|5$d3>8>vwbT3=VMzqsLe*u_Fx=&ov-{24~M^0pZp1^FZ~z$VxBU__jG}vTw~Wp zsl#LK3|BDQFG;i=TE5lGEof4;?F!#Y`Q1OaOP7`^S^Q#SPT2EN7~GQRzM&n^1O`3*GOZ5$3Pk4BmP^?X7|AYcD9mJPLX>C7OK zJ<_Q)9k7Wg=%fF-$v;!2Bg^S%KbCRIsV?22E)50Ga4 z{TrVUKpEJG3Qf(PRymVh9b|f^6#e|5C79rJKm-CXX`1O zb3@JY4f2-b(~XPB*eb4!%7U<~GI#fK`QrzZ{(gwxm7Of7!~MFsdv%Mh!x)1nrd~4U z3yibq=$psg#^}RIC(7D0>+*@B8-D9DiVOhN+bA|lI6oT*m_pidAT~l$2~1}(bw3;W z0G~Y_&h~9z$Y7}t2iYZ@&rmv;T~f6njpKH$e{#}YlAK~sS)^ZC?s%;Ce<3yI_rIBO z@SAd5=E_LbncFg9G-$+ZoNZe_3G3`2JK(kxj=g|ieeQfw0{T2`RihofyY|4kX~Du1T_Wlnk@LJ1AM##(c$UoKl~Eb+22{Wr9hm0uMVFrL_BWlvV5P? z9bERgLVJz(A3C^QLa%?IZ{IeKK3u3tS(6q&p;P%HqX^;N-24tyKR@EJuN6aCE0IU; z^3)P;>b@6%haUbt9bJDt*EbocM*bV2MV&6LZ@GUc`F(r|x&yy<`X zp?&ipy|uToag-BG%G!|V=zGvb+~>>H0CccQZ7W|oIe`dV>Ku7M7r<*f4`%Ay_&sxx5HPxdWYE9AaD9y+=N;TlYbg-pB+A=xa~KdpSzf2{Bex4 zTs!)C(fP6aLS3VmYVplVvv{9{#h0;*rEe@$ zZYplYtw)X??sG^a!2IK>Xrd34aWv~75*31Obe#1-k1hZ;@jA{by8Gabm}44m@T(r{ zCjK5DBwY{ZK}KNU2O7VfZmo0-eiV?*2fZ$&u#Za3X_XV@-Q%J`xTuWn@2t8V142}> z%xEl6bt9F7QWKS0uB`k042q?sbl68_DneMR!t4Cqg6k|zGl{DWnRRW_jd>`(m}kng zc>i6Xg3d%-2kWv^9^W+b$?xxM{?g+4fXSc)M6DD${egbWYEKv|F8Ey!M*?*X&1OBC z791&_A z`*0(IdPg?Zu%8L>5KJa;Z@Z_RIgOITsHmJc$vUPZCR-n)hB_vJJ(OjJV*Ne#$Dm>r zrwh&Pgo4)&vIMn@j6R2;3Cl(3XL!)WOSCh_+-m*fnWh1JZG zp;8M*Y!F7u#Gqd8YRSl|IS+Ncn~&b{t4v8Lhi*LDFQ<%~typAe>H;Qo1s!A6Wb$UM z6`5rL4I}udJ?q4yj#%nw_{I~~+Aa>p3zY+c)yN+Qk;!R3uNhU!!48qJQ6|Sd2cdcD z6&gC_P3e-_0Cp~?x9nJFgyuY7Pc2u-2=K-EC-U%%Rno#<({iQp3oWf1mjp4XhJzdf z>6m5Eo<;WhJ<5&58A{h91~34LlmTy$oO%Swjl~K#5$e9T!ZK?GP;G56K25If5VD3x zY!|}*#Wa5C+N;2TKD|WzIBa@7)r^e=EFXd|AmpAn+{xy!u_B<{jpYD7i-E3!st=mr zF0QOq{Q`_x_@BGxMI!4{Cjb;ohV3M;YJ1jQCOw&>vRx<#w+4pWmPARBq8 zmC(`yzpN(;6m}a0P-oLr<8V{cn1=lr+Jq%OEjulosL>@CS?FaXM7ZlD5&#*)MP+d_ ziyGsIP;*G8^+LYm32Y`}pc}0dbf0E`%u3Fy6fkRb#{+bOM^!Y-Juo1 zlK;ClQ{*la zc5azMitBTqgt@$G6~$G?=tGIcfJmY4nxaBWHDk9Ar_X%Dx^9B_?I1RBtjLqQiu1v^ zuKX;>%Ai|{9Jk{*sR6woH435N(FpY{wo;6`fjkWHObL0+x)>AXy;t}^{2oaif&?D) z^wvOtS2AV&kG8wV6xzC__F5~4{=Amt1yC75n7qO_Sw!~U2H~2m-3(k z=)o35PH!AwQT9xu1>3*0r^t%6Gv!=iBVc@Rx!X#v>Fmq{k}z0=`(_BgorYD>3V;sfK&Qb&`6S$_>1c9sk)RLsZ;F=rV! zJ-Qf2nONp@>3Y1WO)zkkD%GnEjN=ECA4d{8PGo zJz5G)X(1)r#)JC`kTcuen|`cY$;t!nDc}#B=4O_~EG(O3*p58ed2-HByQk`SgdMbO zavy*D48)dD=3i5X&oY|{X9UXG&OMpeXZ>I`&Cdg?dVLiqB%*MYXo@Hi1tQN`st%f+ z?=|(xzV~dmUpGs0`8)OOce4$NVAl}rw|zd@|O%^ zH=9~L4F>Yn08LCkrAVFRCOe?Sc}QKW(CgWwbO)2A&BUFTtMr*xt%JWmIKRpA6u8J_ zzgUxG58Z9!J==OecR%+Zq;%eGG<8Dmw;W^CnRNd)BdEgyJF{tJ_YDuHCr^e3w(b`# zqf@TrTICSBOruKv?*(PV7fWs~Iz>*a2p)Xz1=Wk264&zrg4x&oCoMBtQM}`sdS*M* zl+?I-u-oOB&%`F>AUEDvOB1`RskODUSrN#Bm9-HZTb*pir5w7voUU0Gp{>(W_0g?| zvC22QZnVCPm#?pN>e7nxzSY5>BWAOX`J90=J_^IwenWwNJ*+PmM^&oS zb#j14#`)cXWW-{BLSBiqsh8_O+uEN`QsQNm*~FDZa&LY#fxd%49CA4G`spd} zU6h2t@^kbw&T=aQn|?EuX`{3(KF!U(Tljflx>noAP)z_x&*r(r1A~qlfQr|n>r@{R z^wZkBA&p=?q!p5l+n;t2RP4xwId=++Qnk^GxNc4&D_tY}$ubI<-XmG=n!%a{@48uR z3IBJ>Kq!%E@rEW`U1MyYThDf>3`-pU!fA220J6rYi&0@lyQns3koi`5vQU3BOr1e5 zP>&@MLnc?(TY5;e+$>|jq`=e-b3!u(Y^CcYM?JS~l+q&B<4xp|TI4O+tU~R-) zUpLcXU)@N25^Zcb+~-ee9-{_M@c7&-Dn6aB;5E)2JliBez7EBE4;+W7mnD{z&XNHw zo1w&v3kj0b?b@UV#&ZJ_(Yms9P*SCMN9Dz|d^aeM?$1+4|1;I9nPex6(8pk0&cV5A6j7&g!wfH8w4LH|E*-25M@q!@%!U1uHsc(vPV&faiJDSW1IC?JPK zO*`@w%dY(|xj}WByj-EOi9k)^-D^m(N;Fd%wf>9fpmoX}xQgVL^?L3tzqjFiBLS0P{=OX?AE_h@WS*-D3JmjhCPeQ@SGLXM=WJ=!s2en0vP+W#UrMFkSH&lr^ zWK!0JV_0rv@cf9+Yl+5M$;0Jh?5?>G?^r(Ol|ot`JD%~>w) zP+_mKsvUm$l(|6LR_@f<$+pQ+p*KE`tlQVg(jXgPBhgWbtoYkz8M@VV^HHS#H=`_r8V6P3lL~r-YmYw{AWd1^qO9S{Kn2lZxNU zc}TZ+7g123+fVTN&&?3IHy#K2lXfi@pC3cg6))ON69V-m2U(EBPr#5qhx(5C%Ey<`j zOWH(qm^PuZvEg#&I_0QdVfv5HBPo<0tWcQ66vTIr*H=cm(o_kSda?09{dv?9sKrfx zU4H_EDl|SO*~|75_y4?nrmpbhrcaO)|21ccqBM!z5>Y-bnAe;NB_=S}tV~j2XQqKV zN;a5vF(xppXRzB8CbofMrNV!`$r7(?o-xxACp4DBFz7W=MKo$~$o(oHs=Y(6K(IKj z+r;O^AXMs+)7`5_*l(pio5ci$n)mCu1rXFSD3M-F=5Ou0qW%0H8mU8$-)MQ!^4Wjj zXsdtgD0wqFxuxJ$v1L6VZ`KiydI=bK4LE&|G!CKt`3e`$qn8o;?S~2hD%a`Mi`5(SEHO zNJq9GFhxvj#))9fQku_HNXs%XCZka!KxNT5jBfV?Ha;WVQShGJXzS+oyx94CHLIa- zv+sr4`R=$)`~b!$_KzT5}*jZlpFhn<>>I?L2d2?75X z$Fr5`?CL?jTP5(?=V+p024afhV-M|=I#sdRSHj&+7q0=p8r^(_ukx0a))LE~^Gf+p z@e^gljG4DGde_ns{-#!R3%~iNEAtn(-cNWoe2n@1roQqhtz9@u;l{D{dQAq&taGkg zd#Ux+*KDf>`0p)60gh%WK^{BfH}0z$REDOB&BKj4*9K=xvlpQRRmPazqAF2jaZbMJ zLhe~=>h9dyqvjDN?VZk^h6`*A!iPUBTFY;?FAn^?kODxI{d9nKKaFI*LZ6?eb64Q~ z^qs;#-|Za#91%^=e0&{bpgZyK#JYMic1{*bzb`-5h(Zr4A(Wa9QD?QCFeCjlJt)rX z`KfAAc(k{}FqFqDiL)*`jLA?yuP&+)gMWonuq7)$f^Bb2K%6#a1$S8L;i7(N{N&ep zKYl}GJy^Wu<4}3n@5)8aF&A|m9NB~1j$5r4M0c&NW7eStktlO1HL&Q8`zZq@&Xbq$ zF)JHe=kwi{vi)JUPC-_8+OIHlQ3C&z&BVK^mwx3z-H%^8vl04x^U~>m5*@o37o!r6 zZ8ToTI>RQ%{IC6m{GT6FzC6$T3ssA-elHNs7XVT$9O0)=1adI*t`4*Le`@ibIrmy{ zbM8{l*gT&Tc^TF^xO!SXC|&t`!AuLxt?gwrk5@~GgK={mJaim)KF|4|nIupQ8o-ap zei&<$l|ibr9Qzo{JaxX&h9b&k#)Mzq3hcDDw$f~ywDwQ^J{oR!>DRBf4y_Z6$tQxi z25EF=uf?A*I~pGBh`ij*z08T+pMID^hzza@J>J)P8DI%7(!0djuZ#W9qz_A( zgeUx;Rv%-(E4;I&+s-BDj%?lI&g^7mTYX+xeY|+NJZXOj?(8=n7a5@v*`XKqf2qDa zF25cY5%Im4IoNV`4l5;p>x5nSKuh_>_xhCY`T8OIWxcn⁡@yZ~HTEf5*#X>@%K- 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 + console.log( + "\n\x1b[31mKhรดng thแปƒ giแบฃi mรฃ, ฤ‘แบฃm bแบฃo bแบกn lแบฅy dแปฏ liแป‡u tแปซ ฤ‘รบng tร i khoแบฃn vร  trรฌnh duyแป‡t cแปงa tร i khoแบฃn ฤ‘รฃ ฤ‘ฤƒng nhแบญp\x1b[0m\n", + ); +const exit = () => { + console.log(); + process.exit(); +}; + +main(); + +async function main() { + console.log(); + if (secretKey && !gotFresh) { + const shouldGetFreshKey = await promptForFreshKey(); + if (shouldGetFreshKey) { + secretKey = await getFreshKey(); + } + } + + prompt(); +} + +function prompt() { + console.log(); + rl.question( + "Bแบกn muแป‘n giแบฃi mรฃ hรณa loแบกi nร o? \x1b[32m[1] HTTP(s)\x1b[0m, \x1b[38;5;165m[2] Websocket\x1b[0m, \x1b[38;5;15m[3] thoรกt?\x1b[0m: ", + (answer) => { + if (answer == "1") { + console.clear(); + console.log("\x1b[32m[ HTTP(s) ] Bแบฏt ฤ‘แบงu khแปŸi ฤ‘แป™ng!\x1b[0m"); + decodeHTTP(); + } else if (answer == "2") { + console.clear(); + console.log("\x1b[38;5;165m[ WEBSOCKET ] Bแบฏt ฤ‘แบงu khแปŸi ฤ‘แป™ng!\x1b[0m"); + decodeWebsocket(); + } else if (answer == "3") exit(); + else { + prompt(); + } + }, + ); +} + +function decodeHTTP() { + rl.question("\n\x1b[32m[ HTTP(s) ] Dรกn ฤ‘oแบกn dแปฏ liแป‡u bแป‹ mรฃ hรณa hoแบทc gรต \x1b[37mexit\x1b[0m \x1b[32mฤ‘แปƒ thoรกt:\x1b[0m\n\n", (encoded) => { + if (encoded.length == 0) return decodeHTTP(); + if (encoded == "exit") return prompt(); + + if (encoded.startsWith('"') && encoded.endsWith('"')) encoded = JSON.parse(encoded); + + try { + const decoded = decodeAES(secretKey, encoded); + if (decoded == null) { + cannotDecodeAlert(); + return decodeHTTP(); + } + + const parsed = JSON.stringify(JSON.parse(decoded), null, 2); + + console.log(); + console.log("\x1b[32m=========== HTTP(s) ===========\x1b[0m"); + console.log(parsed); + console.log("\x1b[32m===============================\x1b[0m"); + console.log(); + + return decodeHTTP(); + } catch { + cannotDecodeAlert(); + return decodeHTTP(); + } + }); +} + +async function decodeWebsocket() { + if (!cipher_key) { + console.log( + "MแปŸ devtools trรชn trรฌnh duyแป‡t tแบกi https://chat.zalo.me/, vร o tab Network -> (WS/Socket)\nMแปŸ tin nhแบฏn ฤ‘แบงu tiรชn, ฤ‘แป•i sang ฤ‘แป‹nh dแบกng UTF-8", + ); + cipher_key = await promptForCipherKey(); + } + + rl.question( + "\n\x1b[38;5;165m[ WEBSOCKET ] Dรกn ฤ‘oแบกn dแปฏ liแป‡u bแป‹ mรฃ hรณa hoแบทc gรต \x1b[37mexit\x1b[0m \x1b[38;5;165mฤ‘แปƒ thoรกt:\x1b[0m\n\n", + async (encoded) => { + if (encoded.length == 0) return decodeWebsocket(); + if (encoded == "exit") return prompt(); + + try { + const decoded = await decodeEventData(JSON.parse(encoded), cipher_key); + if (decoded == null) { + cannotDecodeAlert(); + return decodeWebsocket(); + } + + const parsed = JSON.stringify(decoded, null, 2); + + console.log(); + console.log("\x1b[38;5;165m=========== WEBSOCKET ===========\x1b[0m"); + console.log(parsed); + console.log("\x1b[38;5;165m=================================\x1b[0m"); + console.log(); + + return decodeWebsocket(); + } catch { + cannotDecodeAlert(); + return decodeWebsocket(); + } + }, + ); +} + +function promptForFreshKey() { + return new Promise((resolve) => { + rl.question("\x1b[38;5;204m[ !? ] Tรฌm thแบฅy khรณa bรญ mแบญt, bแบกn cรณ muแป‘n tรกi sแปญ dแปฅng? (y/n):\x1b[0m ", (answer) => { + if (answer.toLowerCase() == "y") resolve(false); + else if (answer.toLowerCase() == "n") resolve(true); + else resolve(promptForFreshKey()); + }); + }); +} + +async function getSecretKey(): Promise { + if (!fs.existsSync(SECRET_PATH)) { + return await getFreshKey(); + } else { + const localKey = fs.readFileSync(SECRET_PATH, "utf-8").trim(); + + if (!localKey) return await getFreshKey(); + return localKey; + } +} + +async function getFreshKey() { + gotFresh = true; + const zalo = new Zalo({ + selfListen: true, + logging: true, + }); + + console.log(); + const api = await zalo.login(credentials); + fs.writeFileSync(SECRET_PATH, api.getContext().secretKey, "utf-8"); + + return api.getContext().secretKey; +} + +async function promptForCipherKey() { + return new Promise((resolve) => { + rl.question("\n\x1b[38;5;81mSao chรฉp khรณa vร  dรกn vร o ฤ‘รขy:\x1b[0m ", (answer) => { + if (answer.length == 0) resolve(promptForCipherKey()); + + resolve(answer); + }); + }); +} diff --git a/_libs/zca-js/test/test.ts b/_libs/zca-js/test/test.ts new file mode 100644 index 0000000..a73b890 --- /dev/null +++ b/_libs/zca-js/test/test.ts @@ -0,0 +1,100 @@ +import path from "node:path"; +import fs from "node:fs"; + +import { Reactions, Zalo } from "../src/index.js"; +import { ThreadType } from "../src/models/index.js"; +const zalo = new Zalo({ + selfListen: true, + logging: true, +}); + +const api = await zalo.login(JSON.parse(fs.readFileSync(path.resolve("./test/credentials.json"), "utf-8"))); + +const { listener } = api; + +listener.on("connected", () => { + console.log("Connected"); +}); + +listener.on("closed", (code, reason) => { + console.log("Closed:", code, reason); +}); + +listener.on("error", (error) => { + console.error("Error:", error); +}); + +listener.on("message", (message) => { + console.log("Message:", message.threadId, message.data.content); + switch (message.type) { + case ThreadType.User: + api.addReaction(Reactions.HAHA, message).then(console.log); + if (!message.data.content || typeof message.data.content != "string") return; + if (!message.isSelf) { + switch (message.data.content) { + case "reply": { + api.sendMessage( + { + msg: "reply", + quote: message.data, + }, + message.threadId, + message.type, + ).then(console.log); + break; + } + case "ping": { + api.sendMessage("pong", message.threadId).then(console.log); + break; + } + default: { + const args = message.data.content.split(/\s+/); + if (args[0] == "sticker" && args[1]) { + api.getStickers(args[1]).then(async (stickerIds) => { + const random = stickerIds[Math.floor(Math.random() * stickerIds.length)]; + const sticker = await api.getStickersDetail(random); + console.log("Sending sticker:", sticker[0]); + + if (random) api.sendSticker(sticker[0], message.threadId).then(console.log); + else api.sendMessage("No sticker found", message.threadId).then(console.log); + }); + } + break; + } + } + } else { + const args = message.data.content.split(/\s+/); + if (args[0] == "find" && args[1]) { + api.findUser(args[1]).then(console.log); + } else if (args[0] == "get") { + api.sendMessage( + { + msg: "hi", + attachments: [path.resolve("./test/a.png")], + }, + message.threadId, + message.type, + ).then(console.log); + } + } + break; + + case ThreadType.Group: + if (!message.isSelf) { + switch (message.data.content) { + case "ping": { + api.sendMessage("pong", message.threadId, message.type).then(console.log); + break; + } + default: + break; + } + } + break; + + default: + break; + } +}); + +listener.start(); diff --git a/_libs/zca-js/tsconfig.json b/_libs/zca-js/tsconfig.json new file mode 100644 index 0000000..399af5d --- /dev/null +++ b/_libs/zca-js/tsconfig.json @@ -0,0 +1,104 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "ES2017" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "NodeNext" /* Specify what module code is generated. */, + "rootDir": "./src" /* Specify the root folder within your source files. */, + "moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "ts-node": { + "esm": true + }, + "exclude": ["examples", "test", "dist"] +} diff --git a/data/monitor.db b/data/monitor.db new file mode 100644 index 0000000000000000000000000000000000000000..578853cfd5b282722656eeacd6cfe022bd326758 GIT binary patch literal 45056 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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "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.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/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, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.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, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz", + "integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.6.tgz", + "integrity": "sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz", + "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz", + "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz", + "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz", + "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz", + "integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz", + "integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz", + "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz", + "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz", + "integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "postcss": "^8.5.10", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.40.tgz", + "integrity": "sha512-xxx6M2IpSTnnKcR0cMvIiohkiCx20/oRPtWGbenFygKCGl3zqUzdNjQ/1V4solq1LU+dgv0nQzeGOuqkqZGg0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-cron": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/node-cron/-/node-cron-3.0.11.tgz", + "integrity": "sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/nodemailer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-8.0.0.tgz", + "integrity": "sha512-fyf8jWULsCo0d0BuoQ75i6IeoHs47qcqxWc7yUdUcV0pOZGjUTTOvwdG1PRXUDqN/8A64yQdQdnA2pZgcdi+cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pdfkit": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.17.6.tgz", + "integrity": "sha512-tIwzxk2uWKp0Cq9JIluQXJid77lYhF52EsIOwhsMF4iWLA6YneoBR1xVKYYdAysHuepUB0OX4tdwMiUDdGKmig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "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-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "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, + "license": "MIT", + "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.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "license": "MIT", + "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.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "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, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", + "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "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, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/better-sqlite3": { + "version": "12.9.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.9.0.tgz", + "integrity": "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "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" + } + ], + "license": "CC-BY-4.0" + }, + "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, + "license": "MIT", + "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/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "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.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "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/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.353", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.21.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.2.tgz", + "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.6.tgz", + "integrity": "sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "16.2.6", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "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, + "license": "BSD-2-Clause", + "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, + "license": "BSD-2-Clause", + "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, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "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": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "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, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "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, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "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.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.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, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "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, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-md5": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz", + "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "license": "MIT", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "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, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.14.0.tgz", + "integrity": "sha512-+1mdWcfSJVUsaTIjN9zoezmUhfXo5l0vP7ekBMPo3jcS/aIkxHnXqAPsByszMZx/Y8oQBRJxJx5xg+RH3urzxA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "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, + "license": "MIT" + }, + "node_modules/next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", + "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==", + "license": "MIT", + "dependencies": { + "@next/env": "16.2.6", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.6", + "@next/swc-darwin-x64": "16.2.6", + "@next/swc-linux-arm64-gnu": "16.2.6", + "@next/swc-linux-arm64-musl": "16.2.6", + "@next/swc-linux-x64-gnu": "16.2.6", + "@next/swc-linux-x64-musl": "16.2.6", + "@next/swc-win32-arm64-msvc": "16.2.6", + "@next/swc-win32-x64-msvc": "16.2.6", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-cron": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", + "integrity": "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemailer": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.7.tgz", + "integrity": "sha512-pkjE4mkBzQjdJT4/UmlKl3pX0rC9fZmjh7c6C9o7lv66Ac6w9WCnzPzhbPNxwZAzlF4mdq4CSWB5+FbK6FWCow==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "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, + "license": "MIT", + "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.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "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==", + "license": "ISC", + "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, + "license": "MIT", + "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/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT" + }, + "node_modules/pdfkit": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.18.0.tgz", + "integrity": "sha512-NvUwSDZ0eYEzqAiWwVQkRkjYUkZ48kcsHuCO31ykqPPIVkwoSDjDGiwIgHHNtsiwls3z3P/zy4q00hl2chg2Ug==", + "license": "MIT", + "dependencies": { + "@noble/ciphers": "^1.0.0", + "@noble/hashes": "^1.6.0", + "fontkit": "^2.0.4", + "js-md5": "^0.8.3", + "linebreak": "^1.1.0", + "png-js": "^1.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/png-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.1.0.tgz", + "integrity": "sha512-PM/uYGzGdNSzqeOgly68+6wKQDL1SY0a/N+OEa/+br6LnHWOAJB0Npiamnodfq3jd2LS/i2fMeOKSAILjA+m5Q==", + "dependencies": { + "browserify-zlib": "^0.2.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.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, + "license": "MIT", + "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" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "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, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "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" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "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, + "license": "ISC", + "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, + "license": "MIT", + "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, + "license": "MIT", + "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/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT", + "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-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", + "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.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, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "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, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "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, + "license": "MIT", + "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.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "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, + "license": "MIT", + "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==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": 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, + "license": "ISC" + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zca-js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/zca-js/-/zca-js-2.1.2.tgz", + "integrity": "sha512-82+zCqoIXnXEF6C9YuN3Kf7WKlyyujY/6Ejl2n8PkwazYkBK0k7kiPd8S7nHvC5Wl7vjwGRhDYeAM8zTHyoRxQ==", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "form-data": "^4.0.4", + "json-bigint": "^1.0.0", + "pako": "^2.1.0", + "semver": "^7.6.3", + "spark-md5": "^3.0.2", + "tough-cookie": "^5.0.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/zca-js/node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, + "node_modules/zca-js/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cb0e84a --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "tmp_next", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "better-sqlite3": "^12.9.0", + "clsx": "^2.1.1", + "lucide-react": "^1.14.0", + "next": "16.2.6", + "node-cron": "^4.2.1", + "nodemailer": "^8.0.7", + "pdfkit": "^0.18.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "tailwind-merge": "^3.5.0", + "zca-js": "^2.1.2", + "zod": "^4.4.3" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/better-sqlite3": "^7.6.13", + "@types/node": "^20", + "@types/node-cron": "^3.0.11", + "@types/nodemailer": "^8.0.0", + "@types/pdfkit": "^0.17.6", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..aa39580 --- /dev/null +++ b/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Zalo Monitor Run Script + +echo "๐Ÿš€ Starting Zalo Monitor & Reporter..." + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + echo "๐Ÿ“ฆ Dependencies not found. Installing..." + npm install +fi + +# Check if data directory exists +if [ ! -d "data" ]; then + echo "๐Ÿ“ Creating data directory..." + mkdir -p data +fi + +# Run the development server +echo "๐ŸŒ Launching dashboard at http://localhost:3000" +npm run dev diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts new file mode 100644 index 0000000..8fcc1dd --- /dev/null +++ b/src/app/api/login/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from "next/server"; +import { loginWithQR } from "@/lib/zalo"; + +export async function POST() { + return new Promise((resolve) => { + loginWithQR((qr) => { + resolve(NextResponse.json({ qr })); + }).catch(err => { + console.error(err); + resolve(NextResponse.json({ error: "Failed to initialize login" }, { status: 500 })); + }); + }); +} diff --git a/src/app/api/logs/route.ts b/src/app/api/logs/route.ts new file mode 100644 index 0000000..0674c5e --- /dev/null +++ b/src/app/api/logs/route.ts @@ -0,0 +1,7 @@ +import { NextResponse } from "next/server"; +import db from "@/lib/db"; + +export async function GET() { + const logs = db.prepare("SELECT * FROM messages ORDER BY timestamp DESC LIMIT 100").all(); + return NextResponse.json({ logs }); +} diff --git a/src/app/api/settings/route.ts b/src/app/api/settings/route.ts new file mode 100644 index 0000000..ca553e4 --- /dev/null +++ b/src/app/api/settings/route.ts @@ -0,0 +1,20 @@ +import { NextResponse } from "next/server"; +import db, { getSettings, saveSetting } from "@/lib/db"; + +export async function GET() { + return NextResponse.json({ + smtp_host: getSettings("smtp_host") || "", + smtp_port: getSettings("smtp_port") || "465", + smtp_user: getSettings("smtp_user") || "", + smtp_pass: getSettings("smtp_pass") || "", + report_email: getSettings("report_email") || "" + }); +} + +export async function POST(req: Request) { + const data = await req.json(); + for (const [key, value] of Object.entries(data)) { + saveSetting(key, String(value)); + } + return NextResponse.json({ success: true }); +} diff --git a/src/app/api/status/route.ts b/src/app/api/status/route.ts new file mode 100644 index 0000000..46e2138 --- /dev/null +++ b/src/app/api/status/route.ts @@ -0,0 +1,6 @@ +import { NextResponse } from "next/server"; +import { isConnected } from "@/lib/zalo"; + +export async function GET() { + return NextResponse.json({ connected: isConnected() }); +} diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..1813872 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,36 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Zalo Monitor & Reporter", + description: "Real-time monitoring and daily reporting for Zalo messages", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..adf5f2f --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,401 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Shield, MessageSquare, Settings, Mail, Bell, Play, Square, RefreshCcw, FileText } from "lucide-react"; +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export default function Dashboard() { + const [activeTab, setActiveTab] = useState("monitor"); + const [status, setStatus] = useState({ connected: false, loading: true }); + const [qrCode, setQrCode] = useState(null); + const [logs, setLogs] = useState([]); + const [isLoggingIn, setIsLoggingIn] = useState(false); + + useEffect(() => { + fetchStatus(); + fetchLogs(); + const interval = setInterval(fetchLogs, 5000); + return () => clearInterval(interval); + }, []); + + const fetchStatus = async () => { + try { + const res = await fetch("/api/status"); + const text = await res.text(); + if (!res.ok) throw new Error(`Status error: ${res.status}`); + try { + const data = JSON.parse(text); + setStatus({ connected: data.connected, loading: false }); + } catch (e) { + console.error("Status JSON Parse Error:", text); + throw new Error("Invalid status response"); + } + } catch (e) { + console.error("Status Check Error:", e); + setStatus({ connected: false, loading: false }); + } + }; + + const fetchLogs = async () => { + try { + const res = await fetch("/api/logs"); + const text = await res.text(); + if (!res.ok) throw new Error(`Logs error: ${res.status}`); + try { + const data = JSON.parse(text); + setLogs(data.logs || []); + } catch (e) { + console.error("Logs JSON Parse Error:", text); + } + } catch (e) { + console.error("Logs Fetch Error:", e); + } + }; + + const handleLogin = async () => { + if (isLoggingIn) return; + setIsLoggingIn(true); + setQrCode(null); + try { + const res = await fetch("/api/login", { method: "POST" }); + const text = await res.text(); + + if (!res.ok) { + console.error("Server Error Body:", text); + throw new Error(`Server returned ${res.status}: ${res.statusText}`); + } + + try { + const data = JSON.parse(text); + if (data.qr) { + setQrCode(data.qr); + } + } catch (parseError) { + console.error("JSON Parse Error. Body was:", text); + throw new Error("Invalid response format from server"); + } + } catch (e) { + console.error("Login Error:", e); + alert(e instanceof Error ? e.message : "Failed to initialize login. Please try again."); + } finally { + setIsLoggingIn(false); + } + }; + + return ( +

+ {/* Background Glow */} +
+
+
+
+ +
+ {/* Sidebar */} + + + {/* Main Content */} +
+
+ {activeTab === "monitor" && ( +
+
+
+

System Monitor

+

Real-time surveillance and status control.

+
+ {!status.connected && ( + + )} +
+ +
+ } /> + } /> + } /> +
+ + {!status.connected && qrCode && ( +
+

Scan QR Code to Login

+
+ Zalo QR +
+

+ Open Zalo on your phone, go to Menu {'>'} QR Code, and scan this code to link your account. +

+
+ )} + +
+
+

Recent Messages

+ +
+
+ {logs.slice(0, 5).map((log, i) => ( +
+
+ {log.senderName?.[0] || "?"} +
+
+
+ {log.senderName} + {new Date(log.timestamp).toLocaleTimeString()} +
+

{log.content}

+
+
+ ))} + {logs.length === 0 && ( +
+ No activity recorded yet. +
+ )} +
+
+
+ )} + + {activeTab === "logs" && } + {activeTab === "settings" && } +
+
+
+
+ ); +} + +function NavItem({ icon, label, active, onClick }: any) { + return ( + + ); +} + +function StatCard({ label, value, icon }: any) { + return ( +
+
+
{icon}
+
+
{value}
+
{label}
+
+ ); +} + +function ActivityLogs({ logs }: any) { + return ( +
+

Activity Logs

+
+ + + + + + + + + + + {logs.map((log: any, i: number) => ( + + + + + + + ))} + +
TimestampSenderTypeContent
+ {new Date(log.timestamp).toLocaleString()} + {log.senderName} + + {log.threadType} + + + {log.content} +
+
+
+ ); +} + +function SettingsPage() { + const [settings, setSettings] = useState({ + smtp_host: "", + smtp_port: "465", + smtp_user: "", + smtp_pass: "", + report_email: "" + }); + + useEffect(() => { + fetch("/api/settings").then(res => res.json()).then(data => setSettings(data)); + }, []); + + const handleSave = async (e: React.FormEvent) => { + e.preventDefault(); + await fetch("/api/settings", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(settings) + }); + alert("Settings saved!"); + }; + + return ( +
+

Configuration

+
+
+

+ + Email Settings +

+
+ setSettings({...settings, report_email: v})} + placeholder="your@email.com" + /> + setSettings({...settings, smtp_host: v})} + placeholder="smtp.gmail.com" + /> + setSettings({...settings, smtp_port: v})} + placeholder="465" + /> +
+
+ +
+

+ + Authentication +

+
+ setSettings({...settings, smtp_user: v})} + placeholder="your@gmail.com" + /> + setSettings({...settings, smtp_pass: v})} + type="password" + placeholder="App Password" + /> +
+
+ +
+
+
+
+ ); +} + +function Input({ label, value, onChange, placeholder, type = "text" }: any) { + return ( +
+ + onChange(e.target.value)} + placeholder={placeholder} + className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-slate-200 placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500/40 transition-all" + /> +
+ ); +} diff --git a/src/instrumentation.ts b/src/instrumentation.ts new file mode 100644 index 0000000..1ef40b2 --- /dev/null +++ b/src/instrumentation.ts @@ -0,0 +1,13 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === "node") { + const { initCron } = await import("./lib/cron"); + const { getZaloApi } = await import("./lib/zalo"); + + initCron(); + + // Attempt to login if credentials exist + getZaloApi().catch(err => { + console.error("Initial Zalo login failed:", err); + }); + } +} diff --git a/src/lib/cron.ts b/src/lib/cron.ts new file mode 100644 index 0000000..37a62e8 --- /dev/null +++ b/src/lib/cron.ts @@ -0,0 +1,12 @@ +import cron from "node-cron"; +import { processDailyReport } from "./reporting"; + +export function initCron() { + // Run every day at 00:01 AM + cron.schedule("1 0 * * *", () => { + console.log("Running daily report job..."); + processDailyReport(); + }); + + console.log("Cron jobs initialized"); +} diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..98cc69a --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,106 @@ +import Database from "better-sqlite3"; +import path from "node:path"; +import fs from "node:fs"; + +const DB_PATH = path.join(process.cwd(), "data/monitor.db"); + +// Ensure data directory exists +if (!fs.existsSync(path.join(process.cwd(), "data"))) { + fs.mkdirSync(path.join(process.cwd(), "data")); +} + +const db = new Database(DB_PATH); + +// Initialize schema +db.exec(` + CREATE TABLE IF NOT EXISTS messages ( + id TEXT PRIMARY KEY, + threadId TEXT NOT NULL, + senderId TEXT NOT NULL, + senderName TEXT, + content TEXT, + timestamp INTEGER NOT NULL, + threadType TEXT NOT NULL, + isSelf INTEGER DEFAULT 0 + ); + + CREATE TABLE IF NOT EXISTS threads ( + id TEXT PRIMARY KEY, + name TEXT, + type TEXT NOT NULL + ); + + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT + ); + + CREATE TABLE IF NOT EXISTS credentials ( + id INTEGER PRIMARY KEY CHECK (id = 1), + cookie TEXT, + imei TEXT, + userAgent TEXT, + updatedAt INTEGER + ); +`); + +export default db; + +export function saveMessage(message: any) { + const stmt = db.prepare(` + INSERT OR REPLACE INTO messages (id, threadId, senderId, senderName, content, timestamp, threadType, isSelf) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `); + + let content = "[Unknown Content]"; + try { + content = typeof message.data.content === "string" + ? message.data.content + : JSON.stringify(message.data.content); + } catch (e) { + content = "[Complex Object Content]"; + } + + stmt.run( + message.data.msgId || `tmp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, + message.threadId, + message.data.uidFrom, + message.data.senderName || message.data.uidFrom, + content, + message.data.timestamp || Date.now(), + message.type, + message.isSelf ? 1 : 0 + ); +} + +export function getSettings(key: string): string | null { + const stmt = db.prepare("SELECT value FROM settings WHERE key = ?"); + const row = stmt.get(key) as { value: string } | undefined; + return row ? row.value : null; +} + +export function saveSetting(key: string, value: string) { + const stmt = db.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)"); + stmt.run(key, value); +} + +export function saveCredentials(cookie: any, imei: string, userAgent: string) { + const stmt = db.prepare(` + INSERT OR REPLACE INTO credentials (id, cookie, imei, userAgent, updatedAt) + VALUES (1, ?, ?, ?, ?) + `); + stmt.run(JSON.stringify(cookie), imei, userAgent, Date.now()); +} + +export function getCredentials() { + const stmt = db.prepare("SELECT * FROM credentials WHERE id = 1"); + const row = stmt.get() as any; + if (row) { + return { + cookie: JSON.parse(row.cookie), + imei: row.imei, + userAgent: row.userAgent + }; + } + return null; +} diff --git a/src/lib/reporting.ts b/src/lib/reporting.ts new file mode 100644 index 0000000..99a7f64 --- /dev/null +++ b/src/lib/reporting.ts @@ -0,0 +1,119 @@ +import PDFDocument from "pdfkit"; +import nodemailer from "nodemailer"; +import db, { getSettings } from "./db"; +import fs from "node:fs"; +import path from "node:path"; + +export async function generateDailyReport(date: Date) { + const startOfDay = new Date(date); + startOfDay.setHours(0, 0, 0, 0); + const endOfDay = new Date(date); + endOfDay.setHours(23, 59, 59, 999); + + const messages = db.prepare(` + SELECT * FROM messages + WHERE timestamp >= ? AND timestamp <= ? + ORDER BY timestamp ASC + `).all(startOfDay.getTime(), endOfDay.getTime()) as any[]; + + if (messages.length === 0) { + console.log("No messages to report for", date.toDateString()); + return null; + } + + const doc = new PDFDocument(); + const fileName = `report-${date.toISOString().split("T")[0]}.pdf`; + const filePath = path.join(process.cwd(), "data", fileName); + const stream = fs.createWriteStream(filePath); + + doc.pipe(stream); + + // Title + doc.fontSize(20).text(`Zalo Daily Report - ${date.toDateString()}`, { align: "center" }); + doc.moveDown(); + + // Group by thread + const threads: Record = {}; + messages.forEach(msg => { + if (!threads[msg.threadId]) threads[msg.threadId] = []; + threads[msg.threadId].push(msg); + }); + + for (const [threadId, msgs] of Object.entries(threads)) { + const threadName = msgs[0].threadType === "Group" ? `Group: ${threadId}` : `User: ${msgs[0].senderName}`; + doc.fontSize(16).text(threadName, { underline: true }); + doc.moveDown(0.5); + + msgs.forEach(msg => { + const time = new Date(msg.timestamp).toLocaleTimeString(); + doc.fontSize(10).text(`[${time}] ${msg.senderName}:`, { continued: true }); + doc.fontSize(11).text(` ${msg.content}`); + }); + doc.moveDown(); + } + + doc.end(); + + return new Promise((resolve) => { + stream.on("finish", () => resolve(filePath)); + }); +} + +export async function sendEmail(filePath: string, recipient: string) { + const smtpHost = getSettings("smtp_host") || "smtp.gmail.com"; + const smtpPort = parseInt(getSettings("smtp_port") || "465"); + const smtpUser = getSettings("smtp_user"); + const smtpPass = getSettings("smtp_pass"); + + if (!smtpUser || !smtpPass) { + throw new Error("SMTP credentials not configured"); + } + + const transporter = nodemailer.createTransport({ + host: smtpHost, + port: smtpPort, + secure: smtpPort === 465, + auth: { + user: smtpUser, + pass: smtpPass, + }, + }); + + const info = await transporter.sendMail({ + from: `"Zalo Monitor" <${smtpUser}>`, + to: recipient, + subject: `Zalo Daily Report - ${new Date().toDateString()}`, + text: "Please find the attached daily report for your Zalo messages.", + attachments: [ + { + filename: path.basename(filePath), + path: filePath, + }, + ], + }); + + console.log("Email sent: %s", info.messageId); + return info; +} + +export async function processDailyReport() { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + + const recipient = getSettings("report_email"); + if (!recipient) { + console.error("Report email not configured"); + return; + } + + try { + const filePath = await generateDailyReport(yesterday); + if (filePath) { + await sendEmail(filePath, recipient); + // Optional: delete file after sending + // fs.unlinkSync(filePath); + } + } catch (error) { + console.error("Failed to process daily report:", error); + } +} diff --git a/src/lib/zalo.ts b/src/lib/zalo.ts new file mode 100644 index 0000000..68cf175 --- /dev/null +++ b/src/lib/zalo.ts @@ -0,0 +1,110 @@ +import { Zalo, ThreadType, LoginQRCallbackEventType } from "zca-js"; +import db, { saveMessage, saveCredentials, getCredentials } from "./db"; + +let zaloInstance: Zalo | null = null; +let apiInstance: any = null; +let isInitializing = false; + +function getZaloInstance() { + if (!zaloInstance) { + zaloInstance = new Zalo(); + } + return zaloInstance; +} + +export async function getZaloApi() { + if (apiInstance) return apiInstance; + + const creds = getCredentials(); + if (creds) { + try { + console.log("Attempting to login with saved credentials..."); + const zalo = getZaloInstance(); + const api = await zalo.login(creds); + apiInstance = api; + setupListener(api); + return api; + } catch (error) { + console.error("Failed to login with saved credentials:", error); + } + } + return null; +} + +export async function loginWithQR(onQrGenerated: (qrData: string) => void) { + if (isInitializing) { + console.log("Already initializing Zalo login..."); + return; + } + + isInitializing = true; + console.log("Initializing Zalo QR login..."); + const zalo = getZaloInstance(); + + try { + console.log("Calling zalo.loginQR..."); + const api = await zalo.loginQR({}, (qrData: any) => { + console.log("Zalo login event type:", qrData.type); + if (qrData.type === LoginQRCallbackEventType.QRCodeGenerated) { + console.log("QR Code generated successfully"); + let image = qrData.data.image; + if (!image.startsWith("data:image")) { + image = `data:image/png;base64,${image}`; + } + onQrGenerated(image); + } + }); + + console.log("Zalo login scan completed successfully"); + const context = api.getContext(); + saveCredentials( + context.cookie.toJSON()?.cookies, + context.imei, + context.userAgent + ); + + apiInstance = api; + setupListener(api); + return api; + } catch (error) { + console.error("Zalo loginQR process failed:", error); + throw error; + } finally { + isInitializing = false; + } +} + +function setupListener(api: any) { + api.listener.on("message", (message: any) => { + let contentStr = "[Unknown Content]"; + try { + contentStr = typeof message.data.content === "string" + ? message.data.content + : JSON.stringify(message.data.content); + } catch (e) { + contentStr = "[Complex Object Content]"; + } + + console.log(`Received message from ${message.data.senderName || message.data.uidFrom}:`, contentStr); + saveMessage(message); + }); + + api.listener.onConnected(() => { + console.log("Zalo listener connected"); + }); + + api.listener.onClosed(() => { + console.log("Zalo listener closed"); + apiInstance = null; + }); + + api.listener.onError((error: any) => { + console.error("Zalo listener error:", error); + }); + + api.listener.start(); +} + +export function isConnected() { + return !!apiInstance; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +}