tg buttons /viewContent, tags, validation address form and more /uploadContent

This commit is contained in:
Verticool 2025-02-01 19:17:40 +06:00
parent 372f84d1b1
commit 7705908484
16 changed files with 336 additions and 89 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
VITE_API_BASE_URL=https://my-public-node-1.projscale.dev/api/v1

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
node_modules
.DS_Store
dist
.env

65
package-lock.json generated
View File

@ -13,6 +13,7 @@
"@tonconnect/ui-react": "^2.0.2",
"@vkruglikov/react-telegram-web-app": "^2.1.9",
"axios": "^1.6.7",
"buffer": "^6.0.3",
"clsx": "^2.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -2054,6 +2055,26 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"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/big-integer": {
"version": "1.6.52",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
@ -2139,6 +2160,30 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"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.2.1"
}
},
"node_modules/call-bind": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
@ -3621,6 +3666,26 @@
"react-is": "^16.7.0"
}
},
"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.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",

View File

@ -15,6 +15,7 @@
"@tonconnect/ui-react": "^2.0.2",
"@vkruglikov/react-telegram-web-app": "^2.1.9",
"axios": "^1.6.7",
"buffer": "^6.0.3",
"clsx": "^2.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -1,4 +1,5 @@
import "~/app/styles/globals.css";
import '~/shared/libs/buffer';
import { useEffect } from "react";
import { useExpand, useWebApp } from "@vkruglikov/react-telegram-web-app";

View File

@ -26,18 +26,26 @@
/*Input Range*/
/* Custom styles for the range input */
input[type="range"] {
-webkit-appearance: none; /* Remove default appearance */
width: 100%; /* Full width */
height: 2px; /* Track height */
background: linear-gradient(
to right,
white 0%, /* Start color of passed track */
white var(--value-percentage, 0%), /* End color of passed track */
gray var(--value-percentage, 0%), /* Start color of remaining track */
gray 100% /* End color of remaining track */
);
border-radius: 9999px; /* Rounded-full track */
outline: none; /* Remove outline */
-webkit-appearance: none;
/* Remove default appearance */
width: 100%;
/* Full width */
height: 2px;
/* Track height */
background: linear-gradient(to right,
white 0%,
/* Start color of passed track */
white var(--value-percentage, 0%),
/* End color of passed track */
gray var(--value-percentage, 0%),
/* Start color of remaining track */
gray 100%
/* End color of remaining track */
);
border-radius: 9999px;
/* Rounded-full track */
outline: none;
/* Remove outline */
transition: background 0.3s;
}
@ -45,45 +53,64 @@
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 9px; /* Thumb width */
height: 9px; /* Thumb height */
background: #fff; /* Thumb color (blue-500) */
border-radius: 9999px; /* Rounded-full thumb */
cursor: pointer; /* Pointer cursor on hover */
width: 9px;
/* Thumb width */
height: 9px;
/* Thumb height */
background: #fff;
/* Thumb color (blue-500) */
border-radius: 9999px;
/* Rounded-full thumb */
cursor: pointer;
/* Pointer cursor on hover */
transition: background 0.3s;
}
/* For Firefox */
input[type="range"]::-moz-range-thumb {
appearance: none;
width: 9px; /* Thumb width */
height: 9px; /* Thumb height */
background: #fff; /* Thumb color (blue-500) */
border-radius: 9999px; /* Rounded-full thumb */
cursor: pointer; /* Pointer cursor on hover */
width: 9px;
/* Thumb width */
height: 9px;
/* Thumb height */
background: #fff;
/* Thumb color (blue-500) */
border-radius: 9999px;
/* Rounded-full thumb */
cursor: pointer;
/* Pointer cursor on hover */
transition: background 0.3s;
}
input[type="range"]::-moz-range-track {
-webkit-appearance: none; /* Remove default appearance */
width: 100%; /* Full width */
height: 2px; /* Track height */
background: linear-gradient(
to right,
white 0%, /* Start color of passed track */
white var(--value-percentage, 0%), /* End color of passed track */
gray var(--value-percentage, 0%), /* Start color of remaining track */
gray 100% /* End color of remaining track */
);
border-radius: 9999px; /* Rounded-full track */
outline: none; /* Remove outline */
-webkit-appearance: none;
/* Remove default appearance */
width: 100%;
/* Full width */
height: 2px;
/* Track height */
background: linear-gradient(to right,
white 0%,
/* Start color of passed track */
white var(--value-percentage, 0%),
/* End color of passed track */
gray var(--value-percentage, 0%),
/* Start color of remaining track */
gray 100%
/* End color of remaining track */
);
border-radius: 9999px;
/* Rounded-full track */
outline: none;
/* Remove outline */
transition: background 0.3s;
}
/* English comment: override react-tag-input classes */
.ReactTags__tagInputField {
@apply bg-transparent text-gray placeholder:text-gray outline-none w-full h-8;
@apply border border-white py-[20px] !important;
@apply bg-[#2B2B2B] outline-none w-full h-8 text-sm !important;
@apply border border-white px-[10px] py-[18px] !important;
@apply whitespace-pre !important;
/* English comment:
'bg-transparent' to blend with your dark background
'text-white' to have white text
@ -94,7 +121,7 @@
/* English comment: style for the tag itself when it's rendered */
.ReactTags__selected .ReactTags__tag {
@apply bg-[#363636] text-white text-sm inline-flex items-center px-2 py-1 rounded mr-1;
@apply bg-[#363636] text-white text-sm inline-flex items-center px-2 py-1 mb-2 rounded mr-1 !important;
/* English comment:
'bg-[#363636]' to have a dark gray background
'text-white' keeps the text white
@ -105,11 +132,11 @@
}
.ReactTags__selected .ReactTags__remove {
@apply ml-1 text-gray hover:text-white cursor-pointer;
@apply ml-1 text-gray hover:text-white cursor-pointer !important;
/* English comment:
'ml-1' a small margin to separate the 'x' or close symbol
'text-gray-400' by default, and change to white on hover
'cursor-pointer' so it looks clickable
*/
}
}
}

View File

@ -64,7 +64,7 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
content: fileUploadResult.content_id_v1,
image: coverUploadResult.content_id_v1,
price: String(rootStore.price * 10 ** 9),
hashtags: rootStore.hashtags,
royaltyParams: rootStore.royalty.map((member) => ({
...member,
value: member.value * 100,
@ -151,6 +151,24 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
</FormLabel>
)}
{rootStore.author && (
<FormLabel label={"Теги"}>
<div
className="flex flex-wrap gap-1">
{rootStore.hashtags.map((tag, index) => (
<div
key={index}
className={
"bg-[#363636] text-white text-sm inline-flex items-center px-2 py-1 rounded mr-1"
}
>
{tag}
</div>
))}
</div>
</FormLabel>
)}
<FormLabel label={"Цена"}>
<div
className={

View File

@ -9,10 +9,10 @@ import { Button } from "~/shared/ui/button";
import { useRootStore } from "~/shared/stores/root";
import { BackButton } from "~/shared/ui/back-button";
const MIN_PRICE = 0.07;
const MIN_RESALE_PRICE = 0.07;
const MIN_PRICE = 0.15;
const MIN_RESALE_PRICE = 0.15;
const RECOMMENDED_PRICE = 0.15;
// const RECOMMENDED_PRICE = 0.15;
// const RECOMMENDED_RESALE_PRICE = 0.15;
type PriceStepProps = {
@ -25,9 +25,9 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => {
const formSchema = useMemo(() => {
const parsePrice = (value: unknown) => {
if (typeof value === "string") {
// Replace commas with dots and parse the value
const parsedValue = parseFloat(value.replace(",", "."));
if (typeof value === "string" || typeof value === "number") {
const stringValue = value.toString().replace(",", ".");
const parsedValue = parseFloat(stringValue);
return isNaN(parsedValue) ? undefined : parsedValue;
}
return undefined;
@ -37,10 +37,10 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => {
return z.object({
price: z.preprocess(
parsePrice,
z.number().min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`)
z.number({required_error: 'Цена не соответствует требованиям'}).min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`)
),
resaleLicensePrice: z
.preprocess(parsePrice, z.number().min(MIN_RESALE_PRICE, `Цена копии должна быть минимум ${MIN_RESALE_PRICE} TON.`))
.preprocess(parsePrice, z.number({required_error: 'Цена не соответствует требованиям'}).min(MIN_RESALE_PRICE, `Цена копии должна быть минимум ${MIN_RESALE_PRICE} TON.`))
.optional(),
});
}
@ -48,7 +48,7 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => {
return z.object({
price: z.preprocess(
parsePrice,
z.number().min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`)
z.number({required_error: 'Цена не соответствует требованиям'}).min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`)
),
});
}, [rootStore.allowResale]);
@ -59,9 +59,9 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => {
resolver: zodResolver(formSchema),
mode: "onChange",
defaultValues: {
price: rootStore.price,
price: rootStore.price || MIN_PRICE,
//@ts-expect-error Fix typings
resaleLicensePrice: rootStore?.licenseResalePrice,
resaleLicensePrice: rootStore?.licenseResalePrice || MIN_RESALE_PRICE,
},
});
@ -98,13 +98,22 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => {
<FormLabel label={"Цена продажи TON"}>
<div className={"my-2 flex flex-col gap-1.5"}>
<p className={"text-xs"}>Минимальная стоимость {MIN_PRICE} TON.</p>
<p className={"text-xs"}>Рекомендуемая стоимость {RECOMMENDED_PRICE} TON.</p>
{/* <p className={"text-xs"}>Рекомендуемая стоимость {RECOMMENDED_PRICE} TON.</p> */}
</div>
<Input
error={form.formState.errors?.price}
placeholder={"[ Введите цену ]"}
{...form.register("price")}
/>
inputMode="decimal"
pattern="[0-9]*[.,]?[0-9]*"
{...form.register("price", {
onChange: (e) => {
const value = e.target.value;
if (!/^\d*[.,]?\d*$/.test(value)) {
e.target.value = value.replace(/[^\d.,]/g, '');
}
}
})}
/>
</FormLabel>
</div>
<Button

View File

@ -12,17 +12,30 @@ import { ConfirmModal } from "~/pages/root/steps/royalty-step/components/confirm
import { useRootStore } from "~/shared/stores/root";
import { BackButton } from "~/shared/ui/back-button";
import { useTonConnectUI } from "@tonconnect/ui-react";
import { Address } from "@ton/core";
import { FieldError } from "react-hook-form";
type RoyaltyStepProps = {
prevStep(): void;
nextStep(): void;
};
const isValidTonAddress = (address: string): boolean => {
try {
if (!address) return false;
Address.parse(address);
return true;
} catch {
return false;
}
};
export const RoyaltyStep = ({ nextStep, prevStep }: RoyaltyStepProps) => {
const [impactOccurred] = useHapticFeedback();
const [isDeleteAllOpen, setDeleteAllOpen] = useState(false);
const [isSpreadOpen, setSpreadOpen] = useState(false);
const [addressErrors, setAddressErrors] = useState<Record<number, FieldError | undefined>>({});
const { royalty, setRoyalty, isPercentHintOpen, setPercentHintOpen } =
useRootStore();
@ -42,11 +55,23 @@ export const RoyaltyStep = ({ nextStep, prevStep }: RoyaltyStepProps) => {
};
const handleWalletChange = (index: number, address: string) => {
const isValid = isValidTonAddress(address);
setAddressErrors({
...addressErrors,
[index]: !isValid
? {
type: 'validation',
message: 'Неверный адрес TON'
}
: undefined
});
const newRoyalty = royalty.map((member, i) =>
i === index ? { ...member, address } : member,
i === index ? { ...member, address } : member
);
setRoyalty(newRoyalty);
};
const handlePercentChange = (index: number, value: string) => {
const percentNumber = parseInt(value, 10) || 0;
@ -67,20 +92,28 @@ export const RoyaltyStep = ({ nextStep, prevStep }: RoyaltyStepProps) => {
const isValid = useMemo(() => {
return (
royalty.every((member) => member.address && member.value >= 0) &&
royalty.every((member) => isValidTonAddress(member.address) && member.value >= 0) &&
royalty.reduce((acc, curr) => acc + curr.value, 0) === 100
);
}, [royalty]);
const [tonConnectUI] = useTonConnectUI();
// Устанавливаем адрес из tonConnectUI.account при загрузке страницы
useEffect(() => {
console.log('tonconnectUI', tonConnectUI)
if (tonConnectUI.account) {
setRoyalty([{ address: tonConnectUI.account.address, value: 100 }]);
if (!tonConnectUI.account) return;
if (royalty.length === 0) {
// First initialization with 100%
setRoyalty([{
address: Address.parse(tonConnectUI.account.address).toString({
bounceable: true,
urlSafe: true,
testOnly: false,
}),
value: 100
}]);
}
}, [tonConnectUI.account, setRoyalty]);
}, [tonConnectUI.account, setRoyalty, royalty]);
return (
<section className={"mt-4 px-4 pb-8"}>
@ -128,7 +161,7 @@ export const RoyaltyStep = ({ nextStep, prevStep }: RoyaltyStepProps) => {
<section className={"flex flex-col gap-1.5"}>
{royalty.map((member, index) => (
<div key={index} className={"flex flex-col gap-[20px]"}>
<div className={"flex w-full items-center gap-1"}>
<div className={"flex w-full items-start gap-1"}>
<div className={"w-[83%]"}>
<FormLabel
labelClassName={"flex"}
@ -145,7 +178,8 @@ export const RoyaltyStep = ({ nextStep, prevStep }: RoyaltyStepProps) => {
value={member.address}
onChange={(e) => handleWalletChange(index, e.target.value)}
placeholder={"[ Введите адрес криптокошелька TON ]"}
/>
error={addressErrors[index]}
/>
</FormLabel>
</div>

View File

@ -2,7 +2,6 @@ import ReactPlayer from "react-player/lazy";
import { useTonConnectUI } from "@tonconnect/ui-react";
import { useWebApp } from "@vkruglikov/react-telegram-web-app";
import { Button } from "~/shared/ui/button";
import { usePurchaseContent, useViewContent } from "~/shared/services/content";
import { fromNanoTON } from "~/shared/utils";
import {useCallback, useEffect, useMemo} from "react";
@ -72,7 +71,43 @@ export const ViewContentPage = () => {
}, []);
useEffect(() => {
const mainButton = WebApp.MainButton;
const secondaryButton = WebApp.SecondaryButton;
try {
// Set main button color
mainButton.color = '#e40615';
mainButton.textColor = '#FFFFFF';
// Set secondary button color
secondaryButton.color = '#363636';
secondaryButton.textColor = '#FFFFFF';
if (!haveLicense) {
mainButton.text = `Купить за ${fromNanoTON(content?.data?.encrypted?.license?.resale?.price)} ТОН`;
mainButton.show();
mainButton.onClick(handleBuyContent);
} else {
mainButton.hide();
}
secondaryButton.text = 'Загрузить свой контент';
secondaryButton.show();
secondaryButton.onClick(() => {
WebApp.openTelegramLink('https://t.me/MY_UploaderRobot');
});
return () => {
mainButton.hide();
mainButton.offClick(handleBuyContent);
secondaryButton.hide();
secondaryButton.offClick();
};
} catch (error) {
console.error('Error setting up Telegram WebApp buttons:', error);
}
}, [content, haveLicense, WebApp, handleBuyContent]);
return (
<main className={"flex w-full flex-col gap-[50px] px-4"}>
{content?.data?.content_type.startsWith("audio") && content?.data?.display_options?.metadata?.image && (
@ -111,7 +146,7 @@ export const ViewContentPage = () => {
</p>
</section>
{!haveLicense && <Button
{/* {!haveLicense && <Button
onClick={handleBuyContent}
className={"mb-4 mt-[30px] h-[48px]"}
label={`Купить за ${fromNanoTON(content?.data?.encrypted?.license?.resale?.price)} ТОН`}
@ -125,7 +160,7 @@ export const ViewContentPage = () => {
}}
className={"mb-4 mt-[-20px] h-[48px] bg-darkred"}
label={`Загрузить свой контент`}
/>
/> */}
</main>
);
};

View File

@ -0,0 +1,2 @@
import { Buffer } from 'buffer';
globalThis.Buffer = Buffer;

View File

@ -8,6 +8,7 @@ type UseCreateNewContentPayload = {
content: string;
image: string;
description: string;
hashtags: string[];
price: string;
resaleLicensePrice: string; // nanoTON bignum (default = 0)
allowResale: boolean;

View File

@ -80,7 +80,7 @@ export const useRootStore = create<RootStore>((set) => ({
royalty: [{ address: "", value: 100 }],
setRoyalty: (royalty) => set({ royalty }),
price: 0,
price: 0.15,
setPrice: (price: number) => set({ price }),
allowResale: false,

View File

@ -1,7 +1,6 @@
import { useState } from "react";
import { WithContext as ReactTags } from "react-tag-input";
import { WithContext as ReactTags, SEPARATORS } from "react-tag-input";
import { useRootStore } from "~/shared/stores/root";
// English comment: no extra Tag interface, just cast to any
export const HashtagInput = () => {
const { hashtags, setHashtags } = useRootStore();
@ -9,9 +8,7 @@ export const HashtagInput = () => {
// English comment: local state as string[] for simplicity
const [tags, setTags] = useState<string[]>(hashtags);
const KeyCodes = { comma: 188, enter: 13 };
const delimiters = [KeyCodes.comma, KeyCodes.enter];
const separators = [SEPARATORS.ENTER, SEPARATORS.COMMA]
const handleDelete = (i: number) => {
const newTags = tags.filter((_, index) => index !== i);
setTags(newTags);
@ -20,20 +17,47 @@ export const HashtagInput = () => {
// English comment: pass "any" to the function
const handleAddition = (newTag: any) => {
// English comment: newTag might be { id, text, ... } from react-tag-input
const updatedTags = [...tags, newTag?.text || newTag?.id || ""];
// Clean up text from commas and trim whitespace
const text = newTag?.text || newTag?.id || "";
const cleanText = text.replace(/,/g, '').trim();
// Skip empty tags
if (!cleanText) return;
const updatedTags = [...tags, cleanText];
setTags(updatedTags);
setHashtags(updatedTags);
};
// Simulate Enter keypress for Android
const enterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
code: 'Enter',
bubbles: true,
cancelable: true,
which: 13,
keyCode: 13,
});
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Unidentified') {
const lastChar = e.currentTarget.value.slice(-1);
if (lastChar === ',') {
e.currentTarget.dispatchEvent(enterEvent);
}
}
};
return (
<ReactTags
tags={tags.map((t) => ({ id: t, text: t })) as any}
delimiters={delimiters as any}
separators={separators as any}
handleDelete={handleDelete as any}
handleAddition={handleAddition as any}
allowDragDrop={false}
placeholder="[ enter a hashtag ]"
placeholder="[ введите тэги через запятую ]"
inputProps={{
onKeyUp: handleKeyUp as any
}}
/>
);
};

View File

@ -4,4 +4,19 @@ import TSPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [react(), TSPaths()],
});
define: {
global: 'globalThis',
},
resolve: {
alias: {
buffer: 'buffer/',
}
},
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
}
}
}
});

View File

@ -225,10 +225,10 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@esbuild/darwin-arm64@0.19.12":
"@esbuild/win32-x64@0.19.12":
version "0.19.12"
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz"
integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz"
integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
@ -381,10 +381,10 @@
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz"
integrity sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==
"@rollup/rollup-darwin-arm64@4.12.0":
"@rollup/rollup-win32-x64-msvc@4.12.0":
version "4.12.0"
resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz"
integrity sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==
resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz"
integrity sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==
"@ton/core@^0.59.1":
version "0.59.1"
@ -860,6 +860,11 @@ balanced-match@^1.0.0:
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
big-integer@^1.6.16:
version "1.6.52"
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz"
@ -916,6 +921,14 @@ browserslist@^4.22.2, browserslist@^4.23.0, "browserslist@>= 4.21.0":
node-releases "^2.0.14"
update-browserslist-db "^1.0.13"
buffer@^6.0.3:
version "6.0.3"
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz"
@ -1569,11 +1582,6 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
@ -1764,6 +1772,11 @@ hoist-non-react-statics@^3.3.2:
dependencies:
react-is "^16.7.0"
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
ignore@^5.2.0, ignore@^5.2.4:
version "5.3.1"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz"