diff --git a/examples/wordpress-rest-api/.npmrc b/examples/wordpress-rest-api/.npmrc new file mode 120000 index 0000000000000000000000000000000000000000..cba44bb384cf56f20d6083a86eab1061f70e214d --- /dev/null +++ b/examples/wordpress-rest-api/.npmrc @@ -0,0 +1 @@ +../../.npmrc \ No newline at end of file diff --git a/examples/wordpress-rest-api/example-config.ts b/examples/wordpress-rest-api/example-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..d9a7fe0bcd2028b51eeb4c6cc7181189a1ea02a2 --- /dev/null +++ b/examples/wordpress-rest-api/example-config.ts @@ -0,0 +1,21 @@ +export interface WordPressConfig { + postLimit: number; + observer: string; + sort: "trending" | "hot" | "created" | "promoted" | "payout" | "payout_comments" | "muted"; + startAuthor: string; + startPermlink: string; + postTag: string; + defaultPort: number; + host: string; +} + +export const wordPressExampleConfig: WordPressConfig = { + postLimit: 10, + observer: "hive.blog", + sort: "created", // "trending" / "hot" / "created" / "promoted" / "payout" / "payout_comments" / "muted" + startAuthor: "", + startPermlink: "", + postTag: "hive-148441", + defaultPort: 4000, + host: "http://localhost", +} \ No newline at end of file diff --git a/examples/wordpress-rest-api/hash-utils.ts b/examples/wordpress-rest-api/hash-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..bacb48f755f3cb833b3aa566f01b7c431d2b3368 --- /dev/null +++ b/examples/wordpress-rest-api/hash-utils.ts @@ -0,0 +1,8 @@ +export const simpleHash = (str): number => { + let hash = 0; + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + } + return (hash >>> 0); + }; \ No newline at end of file diff --git a/examples/wordpress-rest-api/hive.ts b/examples/wordpress-rest-api/hive.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d967d1dbcd39c11abe97fa086f87c8e3a6cee5f --- /dev/null +++ b/examples/wordpress-rest-api/hive.ts @@ -0,0 +1,130 @@ +import { + TWaxApiRequest +} from '@hiveio/wax'; + + +export interface IGetPostHeader { + author: string; + permlink: string; + category: string; + depth: number; +} + +export interface JsonMetadata { + image: string; + links?: string[]; + flow?: { + pictures: { + caption: string; + id: number; + mime: string; + name: string; + tags: string[]; + url: string; + }[]; + tags: string[]; + }; + images: string[]; + author: string | undefined; + tags?: string[]; + description?: string | null; + app?: string; + canonical_url?: string; + format?: string; + original_author?: string; + original_permlink?: string; + summary?: string; +} + +export interface EntryVote { + voter: string; + rshares: number; +} + +export interface EntryBeneficiaryRoute { + account: string; + weight: number; +} + +export interface EntryStat { + flag_weight: number; + gray: boolean; + hide: boolean; + total_votes: number; + is_pinned?: boolean; +} + + +export interface Entry { + active_votes: EntryVote[]; + author: string; + author_payout_value: string; + author_reputation: number; + author_role?: string; + author_title?: string; + beneficiaries: EntryBeneficiaryRoute[]; + blacklists: string[]; + body: string; + category: string; + children: number; + community?: string; + community_title?: string; + created: string; + total_votes?: number; + curator_payout_value: string; + depth: number; + is_paidout: boolean; + json_metadata: JsonMetadata; + max_accepted_payout: string; + net_rshares: number; + parent_author?: string; + parent_permlink?: string; + payout: number; + payout_at: string; + pending_payout_value: string; + percent_hbd: number; + permlink: string; + post_id: number; + id?: number; + promoted: string; + reblogged_by?: string[]; + replies: Array; + stats?: EntryStat; + title: string; + updated: string; + url: string; + original_entry?: Entry; +} + +export type ExtendedNodeApi = { + bridge: { + get_post_header: TWaxApiRequest<{ author: string; permlink: string }, IGetPostHeader>; + get_post: TWaxApiRequest<{ author: string; permlink: string; observer: string }, Entry | null>; + get_discussion: TWaxApiRequest< + { author: string; permlink: string; observer?: string }, + Record | null + >; + get_ranked_posts: TWaxApiRequest< + { + sort: string; + tag: string; + start_author: string; + start_permlink: string; + limit: number; + observer: string; + }, + Entry[] | null + >; + get_account_posts: TWaxApiRequest< + { + sort: string; + account: string; + start_author: string; + start_permlink: string; + limit: number; + observer: string; + }, + Entry[] | null + >; + }; +}; \ No newline at end of file diff --git a/examples/wordpress-rest-api/hiveToWpMap.ts b/examples/wordpress-rest-api/hiveToWpMap.ts new file mode 100644 index 0000000000000000000000000000000000000000..202a2047f4e643d2c9bab5ff3e2996eec12af299 --- /dev/null +++ b/examples/wordpress-rest-api/hiveToWpMap.ts @@ -0,0 +1,132 @@ +import { IPost, IReply } from "../../packages/blog-logic/interfaces"; +import { wordPressExampleConfig } from "./example-config"; +import { simpleHash } from "./hash-utils"; +import { Entry } from "./hive"; +import { WPComment, WPPost, WPTag, WPTerm } from "./wp-reference"; +import { DefaultRenderer } from "@hiveio/content-renderer"; + +const renderer = new DefaultRenderer({ + baseUrl: "https://hive.blog/", + breaks: true, + skipSanitization: false, + allowInsecureScriptTags: false, + addNofollowToLinks: true, + doNotShowImages: false, + assetsWidth: 640, + assetsHeight: 480, + imageProxyFn: (url: string) => url, + usertagUrlFn: (account: string) => "https://hive.blog/@" + account, + hashtagUrlFn: (hashtag: string) => "https://hive.blog/trending/" + hashtag, + isLinkSafeFn: (url: string) => true, + addExternalCssClassToMatchingLinksFn: (url: string) => true, + ipfsPrefix: "https://ipfs.io/ipfs/" // IPFS gateway to display ipfs images +}); + +const mapWpTerm = (termName: string, type: "tag" | "category"): WPTerm => { + const termId = simpleHash(termName); + const taxonomy: string = type === "tag" ? "post_tag" : "category"; + const wpTerm: WPTerm = { + id: termId, + link: `${wordPressExampleConfig.host}/${type}/${termName}/`, + name: termName, + slug: termName.toLocaleLowerCase(), + taxonomy, + }; + return wpTerm; +} + + +export const mapIPostToWpPost = async (hivePost: IPost, wpId: number, accountId: number): Promise => { + const slug = hivePost.getSlug(); + const tags = hivePost?.tags || []; + const wpTermTags = tags.map((tag) => mapWpTerm(tag, "tag")); + const community = hivePost.communityTitle; + const wpTermCategory = community ? [mapWpTerm(community, "category")] : []; + const renderedBody = renderer.render(await hivePost.getContent()); + const titleImage = hivePost.getTitleImage(); + const wpExcerpt = renderer.render(`${titleImage} \n \n ${renderedBody.replace(/<[^>]+>/g, '').substring(0, 100)}...`); + const wpPost: WPPost = { + id:wpId, + slug, + date: new Date(hivePost.publishedAt).toISOString(), + date_gmt: new Date(hivePost.publishedAt).toISOString(), + modified: new Date(hivePost.updatedAt).toISOString(), + modified_gmt: new Date(hivePost.updatedAt).toISOString(), + status: "publish", + type: "post", + link: `${wordPressExampleConfig.host}/${slug}/`, + title: { rendered: hivePost.title }, + content: { rendered: renderedBody, protected: false }, + excerpt: { rendered: wpExcerpt, protected: false }, + author: accountId, + featured_media: 0, + comment_status: "open", + ping_status: "open", + sticky: false, + template: "", + format: "standard", + meta: {}, + categories: [community ? simpleHash(community) : 0], + tags: tags.map((tags) => simpleHash(tags)), + guid: { rendered: `${wordPressExampleConfig.host}/?p=${wpId}` }, + class_list: [`category-${community}`], + _embedded: { + replies: [], + author: [{ + id: accountId, + name: hivePost.author, + url: `https://hive.blog/@${hivePost.author}`, + description: "", + link: `https://hive.blog/@${hivePost.author}`, + slug: hivePost.author, + avatar_urls: { + 24: `https://images.hive.blog/u/${hivePost.author}/avatar`, + 48: `https://images.hive.blog/u/${hivePost.author}/avatar`, + 96: `https://images.hive.blog/u/${hivePost.author}/avatar` + } + }], + "wp:term": [...wpTermTags.map((wpTerm) => [wpTerm]), ...wpTermCategory.map((wpTerm) => [wpTerm])] + } + }; + return wpPost +} + +export const mapIReplyToWPComment = async (hiveComment: IReply, commentId: number, mainPostId: number, authorId: number): Promise => { + const parentId = simpleHash(`${hiveComment.parent.author}_${hiveComment.parent.permlink}`); + const renderedBody = renderer.render(await hiveComment.getContent()); + const wpComment: WPComment = { + id: commentId, + post: mainPostId, + parent: parentId === mainPostId ? 0 : parentId, // There is no id for parent post + author: authorId, + author_name: hiveComment.author, + author_url: `https://hive.blog/@${hiveComment.author}`, + date: new Date(hiveComment.publishedAt).toISOString(), + date_gmt: new Date(hiveComment.publishedAt).toISOString(), + content: { rendered: renderedBody }, + link: `http://host/${hiveComment.parent.author}_${hiveComment.parent.permlink}/#comment-${commentId}`, + status: "approved", + type: "comment", + meta: [], + author_avatar_urls: { + 24: `https://images.hive.blog/u/${hiveComment.author}/avatar`, + 48: `https://images.hive.blog/u/${hiveComment.author}/avatar`, + 96: `https://images.hive.blog/u/${hiveComment.author}/avatar` + } + } + return wpComment; +} + +// For later use +export const mapHiveTagsToWpTags = (tagSlug: string): WPTag => { + return { + id: 1, + count: 1, + description: "", + link: `${wordPressExampleConfig.host}/tag/${tagSlug}/`, + name: tagSlug, + slug: tagSlug, + taxonomy: "post_tag", + meta: [] + } +} diff --git a/examples/wordpress-rest-api/package.json b/examples/wordpress-rest-api/package.json new file mode 100644 index 0000000000000000000000000000000000000000..c66e2cc3ebefa126f88b93c15ab1eecdffb38678 --- /dev/null +++ b/examples/wordpress-rest-api/package.json @@ -0,0 +1,21 @@ +{ + "name": "wordpress-rest-api", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "private": false, + "contributors": [], + "scripts": { + "dev": "tsx ./wordpress-rest-api.ts" + }, + "dependencies": { + "@hiveio/content-renderer": "^2.3.1", + "@hiveio/wax": "1.27.6-rc10-stable.250804212246", + "@hiveio/workerbee": "file:../..", + "cors": "^2.8.5", + "express": "^5.1.0" + }, + "devDependencies": { + "tsx": "^4.20.5" + } +} diff --git a/examples/wordpress-rest-api/pnpm-lock.yaml b/examples/wordpress-rest-api/pnpm-lock.yaml new file mode 100644 index 0000000000000000000000000000000000000000..68934c2be3e9ce37fe85fac97f7265d52db756d9 --- /dev/null +++ b/examples/wordpress-rest-api/pnpm-lock.yaml @@ -0,0 +1,1255 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@hiveio/content-renderer': + specifier: ^2.3.1 + version: 2.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@hiveio/wax': + specifier: 1.27.6-rc10-stable.250804212246 + version: 1.27.6-rc10-stable.250804212246 + '@hiveio/workerbee': + specifier: file:../.. + version: file:../.. + cors: + specifier: ^2.8.5 + version: 2.8.5 + express: + specifier: ^5.1.0 + version: 5.1.0 + devDependencies: + tsx: + specifier: ^4.20.5 + version: 4.20.5 + +packages: + + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@hiveio/content-renderer@2.3.1': + resolution: {integrity: sha512-YE8Lj4LycKxig+2z8VQXNiedQq0lx0pQk9q6USsbHZIFaZePWvkZAJAFy2q9qmLFH5Ca5Nu/TGTiZFQbmbLOVQ==, tarball: https://registry.npmjs.org/@hiveio/content-renderer/-/content-renderer-2.3.1.tgz} + engines: {node: '>=20'} + + '@hiveio/wax@1.27.6-rc10-stable.250804212246': + resolution: {integrity: sha1-qZYJ4ot0Lgy3C0fbeS1WFoxmFEY=, tarball: https://gitlab.syncad.com/api/v4/projects/419/packages/npm/@hiveio/wax/-/@hiveio/wax-1.27.6-rc10-stable.250804212246.tgz} + engines: {node: ^20.11 || >= 21.2} + + '@hiveio/workerbee@file:../..': + resolution: {directory: ../.., type: directory} + engines: {node: ^20.11 || >= 21.2} + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@xmldom/xmldom@0.8.10': + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + autolinker@3.16.2: + resolution: {integrity: sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + engines: {node: '>=18'} + hasBin: true + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + ow@0.13.2: + resolution: {integrity: sha512-9wvr+q+ZTDRvXDjL6eDOdFe5WUl/wa5sntf9kAolxqSpkBqaIObwLgFCGXSJASFw+YciXnOVtDWpxXa9cqV94A==} + engines: {node: '>=6'} + + ow@0.28.2: + resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} + engines: {node: '>=12'} + + parse-srcset@1.0.2: + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.1: + resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + engines: {node: '>= 0.10'} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-twitter-embed@4.0.4: + resolution: {integrity: sha512-2JIL7qF+U62zRzpsh6SZDXNI3hRNVYf5vOZ1WRcMvwKouw+xC00PuFaD0aEp2wlyGaZ+f4x2VvX+uDadFQ3HVA==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + remarkable@2.0.1: + resolution: {integrity: sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==} + engines: {node: '>= 6.0.0'} + hasBin: true + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sanitize-html@2.13.0: + resolution: {integrity: sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + scriptjs@2.5.9: + resolution: {integrity: sha512-qGVDoreyYiP1pkQnbnFAUIS5AjenNwwQBdl7zeos9etl+hYKWahjRTfzAZZYBv5xNHx7vNKCmaLDQZ6Fr2AEXg==} + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + ts-custom-error@3.3.1: + resolution: {integrity: sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==} + engines: {node: '>=14.0.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.5: + resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-fest@0.5.2: + resolution: {integrity: sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==} + engines: {node: '>=6'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typescript-chained-error@1.6.0: + resolution: {integrity: sha512-DUH9Cf8AggyReFexCoVYofKqkLp95sRNZRMa7kQL4xpZd+eB6bmgD0YwD37aDwjupFjfCyYjkC0JGT/zc9JG8w==} + engines: {node: '>=12'} + + universe-log@5.2.0: + resolution: {integrity: sha512-8jSMiXIcm0Ea/N3zCj2Kl+BIihCV7ydvshAgGaDDsLgOSSchiDORUMmSYKMH3FXeKaUr6dKd7e1eScBUpe2+3Q==} + engines: {node: '>=8'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + vali-date@1.0.0: + resolution: {integrity: sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==} + engines: {node: '>=0.10.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + +snapshots: + + '@esbuild/aix-ppc64@0.25.9': + optional: true + + '@esbuild/android-arm64@0.25.9': + optional: true + + '@esbuild/android-arm@0.25.9': + optional: true + + '@esbuild/android-x64@0.25.9': + optional: true + + '@esbuild/darwin-arm64@0.25.9': + optional: true + + '@esbuild/darwin-x64@0.25.9': + optional: true + + '@esbuild/freebsd-arm64@0.25.9': + optional: true + + '@esbuild/freebsd-x64@0.25.9': + optional: true + + '@esbuild/linux-arm64@0.25.9': + optional: true + + '@esbuild/linux-arm@0.25.9': + optional: true + + '@esbuild/linux-ia32@0.25.9': + optional: true + + '@esbuild/linux-loong64@0.25.9': + optional: true + + '@esbuild/linux-mips64el@0.25.9': + optional: true + + '@esbuild/linux-ppc64@0.25.9': + optional: true + + '@esbuild/linux-riscv64@0.25.9': + optional: true + + '@esbuild/linux-s390x@0.25.9': + optional: true + + '@esbuild/linux-x64@0.25.9': + optional: true + + '@esbuild/netbsd-arm64@0.25.9': + optional: true + + '@esbuild/netbsd-x64@0.25.9': + optional: true + + '@esbuild/openbsd-arm64@0.25.9': + optional: true + + '@esbuild/openbsd-x64@0.25.9': + optional: true + + '@esbuild/openharmony-arm64@0.25.9': + optional: true + + '@esbuild/sunos-x64@0.25.9': + optional: true + + '@esbuild/win32-arm64@0.25.9': + optional: true + + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.25.9': + optional: true + + '@hiveio/content-renderer@2.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@xmldom/xmldom': 0.8.10 + ow: 0.28.2 + react-twitter-embed: 4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + remarkable: 2.0.1 + sanitize-html: 2.13.0 + typescript-chained-error: 1.6.0 + universe-log: 5.2.0 + transitivePeerDependencies: + - react + - react-dom + + '@hiveio/wax@1.27.6-rc10-stable.250804212246': + dependencies: + events: 3.3.0 + + '@hiveio/workerbee@file:../..': + dependencies: + '@hiveio/wax': 1.27.6-rc10-stable.250804212246 + + '@sindresorhus/is@4.6.0': {} + + '@xmldom/xmldom@0.8.10': {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + autolinker@3.16.2: + dependencies: + tslib: 2.8.1 + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.1 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + clean-stack@2.2.0: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deepmerge@4.3.1: {} + + depd@2.0.0: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-prop@6.0.1: + dependencies: + is-obj: 2.0.0 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + entities@4.5.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild@0.25.9: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + etag@1.8.1: {} + + events@3.3.0: {} + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + 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 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-obj@2.0.0: {} + + is-plain-object@5.0.0: {} + + is-promise@4.0.0: {} + + js-tokens@4.0.0: {} + + lodash.isequal@4.5.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + negotiator@1.0.0: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + ow@0.13.2: + dependencies: + type-fest: 0.5.2 + + ow@0.28.2: + dependencies: + '@sindresorhus/is': 4.6.0 + callsites: 3.1.0 + dot-prop: 6.0.1 + lodash.isequal: 4.5.0 + vali-date: 1.0.0 + + parse-srcset@1.0.2: {} + + parseurl@1.3.3: {} + + path-to-regexp@8.3.0: {} + + picocolors@1.1.1: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.1: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-twitter-embed@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + scriptjs: 2.5.9 + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + remarkable@2.0.1: + dependencies: + argparse: 1.0.10 + autolinker: 3.16.2 + + resolve-pkg-maps@1.0.0: {} + + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + sanitize-html@2.13.0: + dependencies: + deepmerge: 4.3.1 + escape-string-regexp: 4.0.0 + htmlparser2: 8.0.2 + is-plain-object: 5.0.0 + parse-srcset: 1.0.2 + postcss: 8.5.6 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + scriptjs@2.5.9: {} + + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + source-map-js@1.2.1: {} + + sprintf-js@1.0.3: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + toidentifier@1.0.1: {} + + ts-custom-error@3.3.1: {} + + tslib@2.8.1: {} + + tsx@4.20.5: + dependencies: + esbuild: 0.25.9 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + type-fest@0.5.2: {} + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + typescript-chained-error@1.6.0: + dependencies: + clean-stack: 2.2.0 + deepmerge: 4.3.1 + ts-custom-error: 3.3.1 + + universe-log@5.2.0: + dependencies: + ow: 0.13.2 + typescript-chained-error: 1.6.0 + + unpipe@1.0.0: {} + + vali-date@1.0.0: {} + + vary@1.1.2: {} + + wrappy@1.0.2: {} diff --git a/examples/wordpress-rest-api/readme.md b/examples/wordpress-rest-api/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..0d25ed5d662ab5ccbe8fae92a5bf8b70503a6d25 --- /dev/null +++ b/examples/wordpress-rest-api/readme.md @@ -0,0 +1,17 @@ +# Hive to WordPress example + +This project maps data from Hive and returns it as proper WordPress API. It will be later use by custom front end (like Frontity) to display posts and comments. + +### Instalation + +* Get project and get submodules. +* Go into `example/wordpress-rest-api`. +* `pnpm install --ignore-workspace`. +* Change `example-config.ts` file to get the data you want to see on main page. +* `pnpm run dev` for deployment. Rest API should be ready to work with your front end. + +### About + +This example is still a work in progress. It was made to be used with Frontity especially, but the API can connect to other part of WordPress infrastructure. + +Tags, categories and authors are off so far. The things you can do with this API is to display posts list, single posts and comments for given post. \ No newline at end of file diff --git a/examples/wordpress-rest-api/wordpress-rest-api.ts b/examples/wordpress-rest-api/wordpress-rest-api.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc207cca8d775308e5126a3ab8e5664eb4aa6c0c --- /dev/null +++ b/examples/wordpress-rest-api/wordpress-rest-api.ts @@ -0,0 +1,150 @@ +import express, { Request, Response } from "express"; +import cors from "cors"; +import { createHiveChain } from "@hiveio/wax"; +import { ExtendedNodeApi } from "./hive"; +import { mapIPostToWpPost, mapIReplyToWPComment } from "./hiveToWpMap"; +import { WPComment, WPPost } from "./wp-reference"; +import { simpleHash } from "./hash-utils"; +import { wordPressExampleConfig } from "./example-config"; +import { IPost, IReply } from "../../packages/blog-logic/interfaces"; +import { BloggingPlaform } from "../../packages/blog-logic/BloggingPlatform"; +import { DataProvider } from "../../packages/blog-logic/DataProvider"; +import { getWax } from "../../packages/blog-logic/wax"; + +const hiveChain = await createHiveChain(); + +const app = express(); +const PORT = wordPressExampleConfig.defaultPort; + +// Middleware to parse JSON +app.use(express.json()); +app.use(cors()); +const apiRouter = express.Router(); + +const idToStringMap = new Map(); + +const chain = await getWax() +const dataProvider = new DataProvider(chain); +dataProvider.bloggingPlatform.configureViewContext({name: wordPressExampleConfig.observer}); + +const getAuthorPermlinkFromSlug = (slug: string): {author: string, permlink: string} => { + const splitedSlug = slug.split("_"); + const author = splitedSlug[0]; + splitedSlug.shift(); + const permlink = splitedSlug.join("_"); + return { + author, + permlink + } +} + +const mapAndAddtoMapPosts = async (posts: IPost[]): Promise => { + const mappedPosts: WPPost[] = []; + await Promise.all(posts.map(async (post) => { + const postId = simpleHash(`${post.author}_${post.permlink}`); + const authorId = simpleHash(post.author); + idToStringMap.set(postId, `${post.author}_${post.permlink}`).set(authorId, post.author); + posts.push(post); + mappedPosts.push(await mapIPostToWpPost(post, postId, authorId)); + })); + return mappedPosts; +} + + +const mapReplies = async (replies: IReply[], postId: number) : Promise => { + const wpComments: WPComment[] = []; + await Promise.all( replies.map( async (reply) => { + if (reply.author && reply.permlink) { + const wpAuthorPermlink = reply.getSlug(); + const wpComment = await mapIReplyToWPComment(reply, simpleHash(wpAuthorPermlink), postId, simpleHash(reply.author)); + wpComments.push(wpComment) + } + })); + return await wpComments; +} + + +apiRouter.get("/posts", async (req: Request, res: Response) => { + // Default WP call for devtools + if (req.query.slug === "com.chrome.devtools.json") res.json([]); + // Single post + else if (!!req.query.slug) { + const {author, permlink} = getAuthorPermlinkFromSlug(req.query.slug as string); + const authorPermlinkHash = simpleHash(req.query.slug); + const authorHash = simpleHash(author); + idToStringMap.set(authorPermlinkHash, req.query.slug as string).set(authorHash, author); + const post = await dataProvider.bloggingPlatform.getPost({author: author, permlink}); + if (post) { + res.json(await mapIPostToWpPost(post, authorPermlinkHash, authorHash)); + } else { + res.status(404).json({ error: "Post not found" }); + } + // Posts list + } else { + const newPosts = await dataProvider.bloggingPlatform.enumPosts({ + limit: wordPressExampleConfig.postLimit, + sort: wordPressExampleConfig.sort, + startAuthor: wordPressExampleConfig.startAuthor, + startPermlink: wordPressExampleConfig.startPermlink, + tag: wordPressExampleConfig.postTag + }, { + page: 1, + pageSize: 10 + }) as IPost[]; + res.json(await mapAndAddtoMapPosts(newPosts)); + } + +}); + +apiRouter.get("/comments", async (req: Request, res: Response) => { + const postId = Number(req.query.post); + const postParent = idToStringMap.get(postId); + if (postParent) { + const {author, permlink} = getAuthorPermlinkFromSlug(postParent); + // Delete array of posts, get replies here. + const post = await dataProvider.bloggingPlatform.getPost({author, permlink}) + if (post) { + const replies = await post.enumReplies({}, {page: 1, pageSize: 1000}) as IReply[]; + if (replies) { + res.json(await mapReplies(replies, postId)) + } + } + } else { + res.json([]); + } +}); + +apiRouter.get("/test-votes", async (req: Request, res: Response) => { + const voteTestId = {author: "theycallmedan", permlink: "i-have-returned"}; + const post = await dataProvider.bloggingPlatform.getPost(voteTestId); + const votes = await post.enumVotes({limit: 1000, votesSort: "by_comment_voter"}, {page: 1, pageSize: 1000}); + res.json(votes); +}) + +apiRouter.get("/tags", (req: Request, res: Response) => { + res.json([]); +}); + +apiRouter.get("/categories", (req: Request, res: Response) => { + res.json([]); +}); + +apiRouter.get("/users", (req: Request, res: Response) => { + res.json([]); +}); + +apiRouter.get("/media", (req: Request, res: Response) => { + res.json([]); +}); + +apiRouter.get("/pages", (req: Request, res: Response) => { + res.json([]); +}); + +// Mount router with prefix +app.use("/wp-json/wp/v2", apiRouter); + +app.listen(PORT, () => { + // eslint-disable-next-line no-console + console.log(`🚀 Server running at http://localhost:${PORT}`); +}); diff --git a/examples/wordpress-rest-api/wp-reference.ts b/examples/wordpress-rest-api/wp-reference.ts new file mode 100644 index 0000000000000000000000000000000000000000..af779eb5236411f3e336082381b883412bfc2b8b --- /dev/null +++ b/examples/wordpress-rest-api/wp-reference.ts @@ -0,0 +1,97 @@ +export interface Rendered { + rendered: string; +} + +export interface RenderedProtected extends Rendered { + protected: boolean; +} + +export interface WPPost { + id: number; + date: string; // Date + date_gmt: string; // Date + guid: Rendered; + modified: string; // Date + modified_gmt: string; // Date + slug: string; + status: "publish" | "future" | "draft" | "pending" | "private"; + type: "post"; + link: string; + title: Rendered; + content: RenderedProtected; + excerpt: RenderedProtected; + author: number; + featured_media: number; + comment_status: "open" | "closed"; + ping_status: "open" | "closed"; + sticky: boolean; + template: string; + format: "standard" | "aside" | "chat" | "gallery" | "link" | "image" | "quote" | "status" | "video" | "audio"; + meta: Record; + categories: number[]; + tags: number[]; + class_list: string[]; + _embedded?: { + replies: WPComment[][]; + author: Array<{ + id: number; + name: string; + url: string; + description: string; + link: string; + slug: string; + avatar_urls: { + 24: string; + 48: string; + 96: string; + }; + }>; + "wp:term": WPTerm[][]; + }; +} +export interface WPComment { + id: number; + post: number; + parent: number; + author: number; + author_name: string; + author_url: string; + date: string; // Date + date_gmt: string; // Date + content: { + rendered: string + }; + link: string; + status: "approved"; + type: "comment"; + author_avatar_urls: { + 24: string; + 48: string; + 96: string + }; + meta: []; +} + +export interface Content { + rendered: string +} + +export interface WPTag { + id: number, + count: number, + description: string, + link: string, + name: string, + slug: string, + taxonomy: string, + meta: [] + +} + +export interface WPTerm { + id: number; + link: string; + name: string; + slug: string; + taxonomy: string; +} \ No newline at end of file diff --git a/packages/blog-logic/.npmrc b/packages/blog-logic/.npmrc new file mode 120000 index 0000000000000000000000000000000000000000..cba44bb384cf56f20d6083a86eab1061f70e214d --- /dev/null +++ b/packages/blog-logic/.npmrc @@ -0,0 +1 @@ +../../.npmrc \ No newline at end of file diff --git a/packages/blog-logic/Account.ts b/packages/blog-logic/Account.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3b1e3c7df394ca74b67e0a11dfdad541cbf0a27 --- /dev/null +++ b/packages/blog-logic/Account.ts @@ -0,0 +1,32 @@ +import { WorkerBeeError } from "../../src/errors"; +import { DataProvider } from "./DataProvider"; +import { IAccount } from "./interfaces"; + +export class Account implements IAccount { + public readonly name: string; + public readonly creationDate: Date; + public readonly postCount: number; + public readonly lastActivity: Date; + public readonly registeredDate: Date; + public readonly description: string; + public readonly avatar: string; + + public constructor(accountName: string, dataProvider: DataProvider) { + const accountData = dataProvider.getAccount(accountName); + if(!accountData) throw new WorkerBeeError("No account"); + this.name = accountData.name; + this.avatar = JSON.parse(accountData.posting_json_metadata)?.profile.profile_image || ""; + this.creationDate = new Date(`${accountData.created}Z`); + this.postCount = 0; // In this API not available. + this.lastActivity = new Date(); // In this API not available. + this.registeredDate = new Date(`${accountData.created}Z`); + this.description = JSON.parse(accountData.posting_json_metadata)?.about || ""; + } + + /** + * Get standard WordPress slug for account. + */ + public getSlug(): string { + return this.name; + } +} diff --git a/packages/blog-logic/BloggingPlatform.ts b/packages/blog-logic/BloggingPlatform.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a2cb20fe55ccfee925b7e0fa1662c021951cd8c --- /dev/null +++ b/packages/blog-logic/BloggingPlatform.ts @@ -0,0 +1,87 @@ +import { Account } from "./Account"; +import { Community } from "./Community"; +import { DataProvider } from "./DataProvider"; +import { IAccount, IAccountIdentity, + IBloggingPlatform, + ICommunity, + ICommunityFilters, + IPagination, + IPost, + IPostCommentIdentity, + IPostFilters +} from "./interfaces"; +import { Post } from "./Post"; + +export class BloggingPlaform implements IBloggingPlatform { + private dataProvider: DataProvider; + public viewerContext: IAccountIdentity; + + public overwrittenGetTitleImage?: () => string; + + public constructor(dataProvider: DataProvider) { + this.viewerContext = {name: "hive.blog"}; // Set default + this.dataProvider = dataProvider; + } + + /** + * Change the observer for blog. + * @param accontName account name or community. + */ + public configureViewContext(accontName: IAccountIdentity): void { + this.viewerContext = accontName; + } + + + /** + * Get single post idetified by author/permlink. + * @param postId + * @returns post object + */ + public async getPost(postId: IPostCommentIdentity): Promise { + await this.dataProvider.fetchPost(postId); + return new Post(postId, this.dataProvider); + } + + /** + * Enumarate all communities for given filters + * @param filter + * @param pagination + * @returns iterable of community objects + */ + public async enumCommunities(filter: ICommunityFilters, pagination: IPagination): Promise> { + const communitiesIds = await this.dataProvider.enumCommunities(filter, pagination); + return communitiesIds.map((communityId) => new Community(communityId, this.dataProvider)); + } + + /** + * Get posts for selected filters. + * @param filter + * @param pagination + * @returns iterable of posts + */ + public async enumPosts(filter: IPostFilters, pagination: IPagination): Promise> { + const postsIds = await this.dataProvider.enumPosts(filter, pagination); + return postsIds.map((post) => new Post({author: post.author, permlink: post.permlink}, this.dataProvider)) + } + + /** + * Get account object for given account name. + * @param accontName + * @returns account object + */ + public async getAccount(accontName: string): Promise { + await this.dataProvider.fetchAccount(accontName); + return new Account(accontName, this.dataProvider); + } + + // Section for overwritting methods + + /** + * It allows other project to overwrite title image method with their custom implementation. + * @param callbackMethod custom method for getting title image. + */ + public overwriteGetTitleImage(callbackMethod: () => string) { + this.overwriteGetTitleImage = callbackMethod; + } +} + diff --git a/packages/blog-logic/Comment.ts b/packages/blog-logic/Comment.ts new file mode 100644 index 0000000000000000000000000000000000000000..ffafe95d9c7f81ee332f8e3cf146c0018a4354cb --- /dev/null +++ b/packages/blog-logic/Comment.ts @@ -0,0 +1,79 @@ +import { DataProvider } from "./DataProvider"; +import { IComment, IPagination, IPostCommentIdentity, IVote, IVotesFilters } from "./interfaces"; +import { Vote } from "./Vote"; + +export class Comment implements IComment { + + public author: string; + public permlink: string; + public publishedAt: Date; + public updatedAt: Date; + + + protected content?: string; + protected votes?: Iterable; + protected dataProvider: DataProvider; + + // Refactor blogginPlatform and data into dataProvider with promises. + public constructor(authorPermlink: IPostCommentIdentity, dataProvider: DataProvider) { + this.author = authorPermlink.author; + this.permlink = authorPermlink.permlink; + this.dataProvider = dataProvider; + const post = dataProvider.getComment(authorPermlink); + this.publishedAt = new Date(post?.created || ""); + this.updatedAt = new Date(post?.updated || ""); + this.content = post?.body; + + } + + protected getCommentId(): IPostCommentIdentity { + return {author: this.author, permlink: this.permlink}; + } + + /** + * Create standard slug that can be used in Wordpress. It's generated by adding "_" between author and permlink. + */ + public getSlug(): string { + return `${this.author}_${this.permlink}`; + } + + /** + * Get list of all mentioned accounts as strings. + */ + /* eslint-disable-next-line require-await */ + public async enumMentionedAccounts(): Promise> { + const regex = /@[a-z]+[a-z0-9.-]+[a-z0-9]+\b/g; // Alphanumeric with . and -, but not on the end. + return this.content?.match(regex) ?? []; + } + + /** + * Get full body of comment or post. + */ + /* eslint-disable-next-line require-await */ + public async getContent(): Promise { + return this.content || "" + } + + /** + * Return all votes for given comment. + * @param filter Standard filters for date and order. + * @param pagination + * @returns Iterable of Votes. + */ + public async enumVotes(filter: IVotesFilters, pagination: IPagination): Promise> { + // Get rid of condenser API + const voters = await this.dataProvider.enumVotes(this.getCommentId(), filter, pagination); + return voters.map((voter) => new Vote(this.getCommentId(), voter, this.dataProvider)); + } + + /** + * Check if this post was voted by selecred user. + * @param userName + */ + public async wasVotedByUser(userName: string): Promise { + let voters = this.dataProvider.getVoters(this.getCommentId()); + if (!voters) voters = await this.dataProvider.enumVotes(this.getCommentId(), {limit: 1000, votesSort: "by_comment_voter"}, {page: 1, pageSize: 10000}); + return voters.some((voter) => voter === userName) + } + +} diff --git a/packages/blog-logic/Community.ts b/packages/blog-logic/Community.ts new file mode 100644 index 0000000000000000000000000000000000000000..84f32b1b92471df482ea6d065142e6f1e40a0377 --- /dev/null +++ b/packages/blog-logic/Community.ts @@ -0,0 +1,37 @@ +import { WorkerBeeError } from "../../src/errors"; +import { DataProvider } from "./DataProvider"; +import { ICommunity } from "./interfaces"; + +export class Community implements ICommunity { + public readonly name: string; + public readonly title: string; + public readonly about: string; + public readonly admins: string[]; + public readonly avatarUrl: string; + public readonly creationDate: Date; + public readonly subscribersCount: number; + public readonly authorsCount: number; + public readonly pendingCount: number; + + public constructor(communityName: string, dataProvider: DataProvider) { + const communityData = dataProvider.getCommunity(communityName); + if (!communityData) throw new WorkerBeeError("No community"); + this.name = communityData.name; + this.title = communityData.title; + this.about = communityData.about; + this.admins = communityData.admins || []; + this.avatarUrl = communityData.avatar_url; + this.creationDate = new Date(`${communityData.created_at}Z`); + this.subscribersCount = communityData.subscribers; + this.authorsCount = communityData.num_authors; + this.pendingCount = communityData.num_pending; + + } + + /** + * Get standard WordPress slug. It treats community as category. + */ + public getSlug(): string { + return this.title.toLowerCase().replace(/\s+/g, "-"); + } +} diff --git a/packages/blog-logic/DataProvider.ts b/packages/blog-logic/DataProvider.ts new file mode 100644 index 0000000000000000000000000000000000000000..57c6a7532e641fa303bcbf0d0278c79bd89e9c2d --- /dev/null +++ b/packages/blog-logic/DataProvider.ts @@ -0,0 +1,150 @@ +import {HafbeTypesAccount} from "@hiveio/wax-api-hafbe" +import {Community as CommunityData, PostBridgeApi, ActiveVotesDatabaseApi} from "@hiveio/wax-api-jsonrpc"; +import { WorkerBeeError } from "../../src/errors"; +import { BloggingPlaform } from "./BloggingPlatform"; +import { ICommonFilters, ICommunityFilters, IPagination, IPostCommentIdentity, IPostFilters, IVotesFilters } from "./interfaces"; +import { paginateData } from "./utils"; +import { WaxExtendedChain } from "./wax"; + +/** + * Main class to call all of Blog Logic. The class is responsible for making instances of Blog Logic's objects and + * getting and caching all the necessary data for them. + */ +export class DataProvider { + public chain: WaxExtendedChain; + public bloggingPlatform: BloggingPlaform; + + private comments: Map = new Map(); + private repliesByPostId: Map = new Map(); + private accounts: Map = new Map(); + private communities: Map = new Map(); + private votesByCommentsAndVoter: Map> = new Map(); + + + public constructor(chain: WaxExtendedChain) { + this.chain = chain; + this.bloggingPlatform = new BloggingPlaform(this); + } + + /** + * For keeping universal author-permlink strings as a map key. The string is done the same way as in WP API. + */ + private convertCommentIdToHash(commentId: IPostCommentIdentity): string { + return `${commentId.author}_${commentId.permlink}` + } + + public getComment(postId: IPostCommentIdentity): PostBridgeApi | null { + return this.comments.get(this.convertCommentIdToHash(postId)) || null; + } + + public async fetchPost(postId: IPostCommentIdentity): Promise { + const fetchedPostData = await this.chain.api.bridge.get_post({ + author: postId.author, + permlink: postId.permlink, + observer: this.bloggingPlatform.viewerContext.name, + }); + if (!fetchedPostData) + throw new Error("Post not found"); + this.comments.set(this.convertCommentIdToHash(postId), fetchedPostData); + } + + public async enumPosts(filter: IPostFilters, pagination: IPagination): Promise { + const posts = await this.chain.api.bridge.get_ranked_posts({ + sort: filter.sort, + observer: this.bloggingPlatform.viewerContext.name, + tag: filter.tag + }); + if (!posts) + throw new WorkerBeeError("Posts not found"); + const paginatedPosts = paginateData(posts, pagination); + paginatedPosts.forEach((post) => { + const postId = {author: post.author, permlink: post.permlink} + this.comments.set(this.convertCommentIdToHash(postId), post); + }) + return paginatedPosts.map((post) => ({author: post.author, permlink: post.permlink})); + } + + public getRepliesIdsByPost(postId: IPostCommentIdentity): IPostCommentIdentity[] | null { + return this.repliesByPostId.get(this.convertCommentIdToHash(postId)) || null; + } + + public async enumReplies(postId: IPostCommentIdentity, filter: ICommonFilters, pagination: IPagination): Promise { + const replies = await this.chain!.api.bridge.get_discussion({ + author: postId.author, + permlink: postId.permlink, + observer: this.bloggingPlatform.viewerContext.name, + }); + if (!replies) throw WorkerBeeError; + const filteredReplies = Object.values(replies).filter((rawReply) => !!rawReply.parent_author); + const repliesIds: IPostCommentIdentity[] = []; + filteredReplies.forEach((reply) => { + const replyId = { + author: reply.author, + permlink: reply.permlink + } + repliesIds.push(replyId); + this.comments.set(this.convertCommentIdToHash(replyId), reply); + }) + this.repliesByPostId.set(this.convertCommentIdToHash(postId), repliesIds); + + return paginateData(filteredReplies, pagination).map((reply) => ({author: reply.author, permlink: reply.permlink})); + } + + public getAccount(accountName: string): HafbeTypesAccount | null { + return this.accounts.get(accountName) || null; + } + + public async fetchAccount(accountName: string): Promise { + const account = await this.chain.restApi.hafbeApi.accounts.accountName({accountName: accountName}); + if (!account) + throw new Error("Account not found"); + this.accounts.set(accountName, account); + } + + public getCommunity(communityName: string): CommunityData | null { + return this.communities.get(communityName) || null; + } + public async enumCommunities(filter: ICommunityFilters, pagination: IPagination): Promise { + const communities = await this.chain.api.bridge.list_communities({ + observer: this.bloggingPlatform.viewerContext.name, + sort: filter.sort, + query: filter.query, + }); + const communitiesNames: string[] = []; + if (communities) + communities.forEach((community) => { + this.communities.set(community.name, community); + communitiesNames.push(community.name); + }) + return paginateData(communitiesNames, pagination); + } + + public getVote(commentId: IPostCommentIdentity, voter: string): ActiveVotesDatabaseApi | null { + return this.votesByCommentsAndVoter.get(this.convertCommentIdToHash(commentId))?.get(voter) || null; + } + + public getVoters(commentId: IPostCommentIdentity): string[] | null { + const votesMap = this.votesByCommentsAndVoter.get(this.convertCommentIdToHash(commentId)); + const votes = Array.from(votesMap?.keys() || []); + return votes || null; + } + + public async enumVotes(commentId: IPostCommentIdentity, filter: IVotesFilters, pagination: IPagination): Promise { + const votesData = ( + await this.chain!.api.database_api.list_votes({ + limit: filter.limit, + order: filter.votesSort, + start: [commentId.author, commentId.permlink, ""], + }) + ).votes; + const votersForComment: string[] = []; + const votesByVoters: Map = new Map(); + votesData.forEach((voteData) => { + votersForComment.push(voteData.voter); + votesByVoters.set(voteData.voter, voteData); + }) + this.votesByCommentsAndVoter.set(this.convertCommentIdToHash(commentId), votesByVoters); + return paginateData(votersForComment, pagination); + } + +} diff --git a/packages/blog-logic/Post.ts b/packages/blog-logic/Post.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc9b4635b3ed81604e382335266b91e58faab1bf --- /dev/null +++ b/packages/blog-logic/Post.ts @@ -0,0 +1,61 @@ +import { Comment } from "./Comment"; +import { DataProvider } from "./DataProvider"; +import { ICommonFilters, ICommunityIdentity, IPagination, IPost, IPostCommentIdentity, IReply } from "./interfaces"; +import { Reply } from "./Reply"; + +export class Post extends Comment implements IPost { + + public title: string; + public tags: string[]; + public community?: ICommunityIdentity; + public summary: string; + public communityTitle?: string; + + private postImage?: string; + + public constructor(authorPermlink: IPostCommentIdentity, dataProvider: DataProvider) { + super(authorPermlink, dataProvider); + const post = dataProvider.getComment(authorPermlink); + this.title = post?.title || ""; + this.tags = post?.json_metadata?.tags || []; + this.summary = post?.json_metadata?.description || ""; + this.community = post?.community ? {name: post.community} : undefined; + this.communityTitle = post?.community_title + this.postImage = post?.json_metadata.image?.[0]; + + } + + + /** + * Get title image from post content. + * @returns Link to title image + */ + public getTitleImage(): string { + if (this.dataProvider.bloggingPlatform.overwrittenGetTitleImage) return this.dataProvider.bloggingPlatform.overwrittenGetTitleImage() + return this.postImage || "" + } + + /** + * Enum replies for given post. + * @param filter + * @param pagination + * @returns iterable of replies objects + */ + public async enumReplies(filter: ICommonFilters, pagination: IPagination): Promise> { + const postId = {author: this.author, permlink: this.permlink}; + const repliesIds = await this.dataProvider.enumReplies(postId, filter, pagination) || []; + return repliesIds.map((replyId) => new Reply(replyId, this.dataProvider, postId)); + } + + /** + * Get number of comments (replies) for given post. + */ + public async getCommentsCount(): Promise { + const postId = {author: this.author, permlink: this.permlink}; + let repliesIds = this.dataProvider.getRepliesIdsByPost(postId); + if (!repliesIds) + repliesIds = await this.dataProvider.enumReplies(postId, {}, {page: 1, pageSize: 10000}); + return repliesIds.length; + } + +} diff --git a/packages/blog-logic/Reply.ts b/packages/blog-logic/Reply.ts new file mode 100644 index 0000000000000000000000000000000000000000..335565db59160f26238f4a634dfbc75c02ebba51 --- /dev/null +++ b/packages/blog-logic/Reply.ts @@ -0,0 +1,20 @@ +import { Comment } from "./Comment"; +import { DataProvider } from "./DataProvider"; +import { IPostCommentIdentity, IReply } from "./interfaces"; + +export class Reply extends Comment implements IReply { + public parent: IPostCommentIdentity; + public topPost: IPostCommentIdentity; + + public constructor( + authorPermlink: IPostCommentIdentity, + dataProvider: DataProvider, + topPost: IPostCommentIdentity + ) { + super(authorPermlink, dataProvider); + const reply = dataProvider.getComment(authorPermlink); + + this.parent = {author: reply?.parent_author || "", permlink: reply?.parent_permlink || ""}; + this.topPost = topPost; + } +} diff --git a/packages/blog-logic/Vote.ts b/packages/blog-logic/Vote.ts new file mode 100644 index 0000000000000000000000000000000000000000..74a8ae13bbe1fd5afcaa5b3e142be18550591b81 --- /dev/null +++ b/packages/blog-logic/Vote.ts @@ -0,0 +1,19 @@ +import { WorkerBeeError } from "../../src/errors"; +import { DataProvider } from "./DataProvider"; +import { IPostCommentIdentity, IVote } from "./interfaces"; + +export class Vote implements IVote { + public parentComment: IPostCommentIdentity; + public upvote: boolean; + public voter: string; + public weight: number; + + public constructor(parentId: IPostCommentIdentity, voter: string, dataProvider: DataProvider) { + const voteData = dataProvider.getVote(parentId, voter); + if(!voteData) throw new WorkerBeeError("No account"); + this.upvote = Number(voteData.weight) > 0 + this.voter = voteData.voter; + this.weight = Number(voteData.weight); + this.parentComment = {author: voteData.author, permlink: voteData.permlink}; + } +} diff --git a/packages/blog-logic/interfaces.ts b/packages/blog-logic/interfaces.ts new file mode 100644 index 0000000000000000000000000000000000000000..488502054a41eec5d502c8d06da67bd100098272 --- /dev/null +++ b/packages/blog-logic/interfaces.ts @@ -0,0 +1,164 @@ +// WORK IN PROGRESS +import type { TAccountName, IOnlineSignatureProvider } from "@hiveio/wax"; +import { type Observer } from "../../src/types/subscribable"; + +export interface IPagination { + page: number; + pageSize: number; +} + +export interface ICommonFilters { + readonly startTime?: Date; + readonly endTime?: Date; + order?: "asc" | "desc"; +} + +export interface IVotesFilters extends ICommonFilters { + readonly limit: number; + readonly votesSort: "by_comment_voter" | "by_voter_comment"; +} +export interface IPostFilters extends ICommonFilters { + readonly sort: "trending" | "hot" | "created" | "promoted" | "payout" | "payout_comments" | "muted"; + readonly tag: string; +} + +export interface ICommunityFilters extends ICommonFilters { + readonly sort: string; + readonly query: string +} + +export interface IAccountIdentity { + readonly name: string; +} + +export interface ICommunityIdentity { + readonly name: string; +} + +/** + * Represents a set of data uniquely identifying a post or reply object. + */ +export interface IPostCommentIdentity { + readonly author: string; + readonly permlink: string; +} + +export interface IVote { + readonly weight: number; + readonly upvote: boolean; + readonly voter: string; + readonly parentComment: IPostCommentIdentity +} + +export interface ICommunity extends ICommunityIdentity { + readonly title: string; + readonly about: string; + readonly admins: string[]; + readonly avatarUrl: string; + readonly creationDate: Date; + readonly subscribersCount: number; + readonly authorsCount: number; + readonly pendingCount: number; + getSlug(): string; +} +export interface IAccount extends IAccountIdentity { + readonly creationDate: Date; + readonly lastActivity: Date; + readonly postCount: number; + readonly registeredDate: Date; + readonly description: string; + readonly avatar: string; + getSlug(): string; +} + +/** + * Common representation of a post and reply objects + */ +export interface IComment extends IPostCommentIdentity { + readonly publishedAt: Date; + readonly updatedAt: Date; + + + enumMentionedAccounts(): Promise>; + enumVotes(filter: IVotesFilters, pagination: IPagination): Promise>; + getContent(): Promise; + wasVotedByUser(userName: string): Promise; + + /** + * Allows to generate a slug for the comment, which can be used in URLs or as a unique identifier. + */ + getSlug(): string; +}; + +/** + * Represents a reply to a post or another reply object. + */ +export interface IReply extends IComment { + parent: IPostCommentIdentity; + topPost: IPostCommentIdentity; +} + +export interface ISession { + +} + +/** + * Represents a post (article) published on the platform. + */ +export interface IPost extends IComment { + title: string; + summary: string; + tags: string[]; + community?: ICommunityIdentity; + communityTitle?: string; + + getCommentsCount(): Promise; + enumReplies(filter: ICommonFilters, pagination: IPagination): Promise>; + getTitleImage(): string; +} + +export interface ILoginSession { + readonly authenticatedAccount: TAccountName; + readonly sessionId: string; + + logout(): Promise +} + +export interface IAuthenticationProvider { + login(account: TAccountName, signatureProvider: IOnlineSignatureProvider, directLogin: boolean, sessionTimeout: number): Promise; +} + +export interface IActiveBloggingPlatform { + readonly session: ILoginSession; + // Add callbacks + + post(body: string, tags: string[], title?: string, observer?: Partial>): Promise; + comment(postOrComment: IPostCommentIdentity, body: string, tags: string[], title?: string, observer?: Partial>): Promise; + vote(postOrComment: IPostCommentIdentity, upvote: boolean, weight: number, observer?: Partial>): Promise; + reblog(postOrComment: IPostCommentIdentity): Promise; + deletePost(postOrComment: IPostCommentIdentity): Promise; + editPost(postOrComment: IPostCommentIdentity, body: string, tags: string[], title?: string, observer?: Partial>): Promise; + deleteComment(postOrComment: IPostCommentIdentity): Promise; + editComment(postOrComment: IPostCommentIdentity, body: string, tags: string[], title?: string, observer?: Partial>): Promise; + followBlog(authorOrCommunity: IAccountIdentity | ICommunityIdentity): Promise; + getAccount(accountName: string): Promise; +} + +export interface IBloggingPlatform { + viewerContext: IAccountIdentity; + getPost(postId: IPostCommentIdentity): Promise; + enumPosts(filter: IPostFilters, pagination: IPagination): Promise>; + configureViewContext(accontName: IAccountIdentity): void; + enumCommunities(filter: ICommunityFilters, pagination: IPagination): Promise>; + getAccount(accontName: string): Promise; + + // To do: add getAccount method later + + overwrittenGetTitleImage?: () => string; + overwriteGetTitleImage(callback: () => string): void; + + // authorize(provider: IAuthenticationProvider): Promise; +} + +// UI integration with mock data + diff --git a/packages/blog-logic/optimistic-actions-interfaces.ts b/packages/blog-logic/optimistic-actions-interfaces.ts new file mode 100644 index 0000000000000000000000000000000000000000..2493c5ca27e6f3feefeb6c19bbb79d92d48d5a94 --- /dev/null +++ b/packages/blog-logic/optimistic-actions-interfaces.ts @@ -0,0 +1,54 @@ +import {TAccountName} from "@hiveio/wax"; + +export enum TActionState { + /// Indicates that the action is started and pending + PENDING, + /// Indicates that the action was processed (i.e. by L1 chain layer), but potentially not yet completed + PROCESSED, + /// Indicates that the action was processed and completed (i.e. by L2 chain layer) + COMPLETED, + /// Indicates that the action was rejected L1 chain layer + REJECTED, + /// Indicates that the action didn't change expected state in specified time (i.e. L2 chain layer didn't complete it) + TIMEOUT +}; + +export interface IApplicationMutableProperty { + value(): T; + + /** + * Indicates whether the action is still pending or has been completed. + * @returns {TActionState} The current state of the action, COMPLETED value means action is settled on the backend side. + */ + isSettled(): TActionState; +}; + +export interface IAccountListEntry { + readonly account: IApplicationMutableProperty; +}; + +export interface IFollowListEntry extends IAccountListEntry { + readonly isFollowedBlog: IApplicationMutableProperty; + readonly isMuted: IApplicationMutableProperty; +}; + +export interface IApplicationMutableList { + isSettled(): TActionState; + /** + * Returns the number of entries in the list. Not settled, means that some upcoming change is processing. + */ + count(): IApplicationMutableProperty; + entries(): Iterable; +}; + +/** + * Represents the application state of the follow list specific to given account + */ +export interface IFollowListState extends IApplicationMutableList { +}; + +export interface IBlacklistedUserListState extends IApplicationMutableList { +}; + +export interface IMutedUserListState extends IApplicationMutableList { +}; diff --git a/packages/blog-logic/optimistic_actions.ts b/packages/blog-logic/optimistic_actions.ts new file mode 100644 index 0000000000000000000000000000000000000000..3465f9880e12f4a07969d4d7e84ce33e76455c8f --- /dev/null +++ b/packages/blog-logic/optimistic_actions.ts @@ -0,0 +1,136 @@ +import WorkerBee from "@hiveio/workerbee"; +import type { IWorkerBee, Observer } from "@hiveio/workerbee"; +import {ITransaction, IOnlineSignatureProvider, IHiveChainInterface, FollowOperation, TAccountName} from "@hiveio/wax"; +import BeekeeperProvider from "@hiveio/wax-signers-beekeeper"; +import Beekeeper from "@hiveio/beekeeper"; +import { + TActionState, + IFollowListState +} from "./optimistic-actions-interfaces"; +class ChainDeferredActions { + public constructor(private readonly bot: IWorkerBee) { + } + + public async followBlog(signatureProvider: IOnlineSignatureProvider, workingAccount: TAccountName, blog: TAccountName, observer: Partial>): Promise { + const tx = await this.bot.chain.createTransaction(); + tx.pushOperation(new FollowOperation().followBlog(workingAccount, blog)); + await tx.sign(signatureProvider); + + let checksCount = 2; + + /// todo missing implementation + const followList: IFollowListState = {} as IFollowListState; + + const internalObserver: Partial> = { + next: (state: TActionState) => { + /// todo update followList state object + observer.next?.(followList); + }, + error: (err: Error) => { + observer.error?.(err); + } + }; + + + await this.deferredActionStateIndicator(tx, internalObserver, async (): Promise => { + /// todo: call the follow_api here to check if the follow was successful + /// now emulate some delay in L2 processing layer + return --checksCount === 0; + }) + } + + private async deferredActionStateIndicator(tx: ITransaction, observer: Partial>, l2Acceptor?: () => Promise): Promise { + observer.next?.(TActionState.PENDING); + + let blockMargin = 3; + + return new Promise((resolve, reject) => { + const onL1Accept = () => { + observer.next?.(TActionState.PROCESSED); + + if( l2Acceptor === undefined) { + observer.next?.(TActionState.COMPLETED); + resolve(); + return; + } + + /// can be replaced with timeout, but it does not matter + const listener = this.bot.observe.onBlock().subscribe({ + next(blockData) { + l2Acceptor() + .then((result) => { + if (result) { + listener.unsubscribe(); + observer.next?.(TActionState.COMPLETED); + resolve(); + } else { + if(--blockMargin == 0) { + listener.unsubscribe(); + observer.next?.(TActionState.TIMEOUT); + reject(new Error(`Block: ${blockData.block.number} L2 layer didn't complete action in expected time`)); + } + } + }) + .catch((err) => { + observer.error?.(err); + reject(err); + }); + }, + error(val) { + listener.unsubscribe(); + reject(val); + } + }); + + }; + + this.bot.broadcast(tx) + .then(onL1Accept) + .catch((err) => { + observer.next?.(TActionState.REJECTED); + reject(err); + }); + }); + }; + + +}; + +const log = (message: string): void => { + const date = new Date(); + const formattedDate = date.toISOString(); + console.log(`[${formattedDate}] ${message}`); +}; + +const beekeepperInstance = await Beekeeper(); + +const walletName = 'myWallet'; +const walletPassword = 'myPassword'; + +const workingAccount = 'small.minion'; +const mysecretkey = '5J...'; // Replace with your actual secret key +const blogAccount = 'medium.minion'; + +const session = beekeepperInstance.createSession('xxx'); +const unlockedWallet = session.hasWallet('myWallet') ? session.openWallet(walletName).unlock(walletPassword) : (await session.createWallet(walletName, walletPassword, false)).wallet; + +await unlockedWallet.importKey(mysecretkey); + +const bot = new WorkerBee(); +await bot.start(); + +const signatureProvider: IOnlineSignatureProvider = await BeekeeperProvider.for(unlockedWallet, workingAccount, 'posting', bot.chain); + +const optimisticUI = new ChainDeferredActions(bot); + +const follow = await optimisticUI.followBlog(signatureProvider, workingAccount, blogAccount, { + next: (data: IFollowListState) => { + log(`Follow action state: ${TActionState[data.isSettled()]}`); + }, + error: (err: Error) => { + log(`Follow action error: ${err.message}`); + } +}); + +bot.stop(); +bot.delete(); diff --git a/packages/blog-logic/package.json b/packages/blog-logic/package.json new file mode 100644 index 0000000000000000000000000000000000000000..31a272ed53bf118e9a57c653f06bbf569a28bf56 --- /dev/null +++ b/packages/blog-logic/package.json @@ -0,0 +1,16 @@ +{ + "name": "hive-blog-logic", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "private": false, + "contributors": [], + "dependencies": { + "@hiveio/wax": "1.27.6-rc10-stable.250804212246", + "@hiveio/wax-api-hafbe": "1.27.12", + "@hiveio/wax-api-jsonrpc": "^1.27.12" + }, + "devDependencies": { + "tsx": "^4.20.5" + } +} diff --git a/packages/blog-logic/readme.md b/packages/blog-logic/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..9ca0111e6b626046dcb7897c24071c77ae3c2702 --- /dev/null +++ b/packages/blog-logic/readme.md @@ -0,0 +1,29 @@ +# Blog Logic + +Blog Logic is a library that makes getting, keeping and preparing Hive's data for blogging much simpler process. You can use logical, carefully prepared interfaces to handle the blog data you need, like posts, replies, accounts and other. + +### Technical requirement + +Because in the future development we plan using the Workerbee for getting data, it's part of Workerbee's repo. Other things necessary for functionality of this library is Wax for data fetching. + +### Current data entities + +* Comments - parent class for both Posts and Replies. +* Posts - an equivalent of real live blog post, not a reply for any existing one. +* Reply - in nested structure. It can be a direct reply to other reply. We keep identification of top post for any Reply. +* Vote - representation of single vote for given comment. +* Community - for Hive's community with its details. +* Account - details about an user or the author of given post. + +### Data Provider + +The class to feed all the other classes with data is Data Provider. Its implementation is necessary for proper working of the rest of classes. It is responsible for fetching and caching all the data required by other entities. Then they can ask Data Provider for data and map then into their own interfaces. + +The implementation of Data Provider is quite elastic. In the future we're going to use Workerbee there and some system to cache data in a better way. + +### How to use + +1. Import files or interfaces you want to use. You can just start with current version of Data Provider. +2. Create new object of Data Provider class, putting Wax's chain into contructor. +3. Use Data Provider's Blogging Platform class to get any data you want, preprepared for the nice, logical interface. You can call their methods and they'll help you with managing blog data. + diff --git a/packages/blog-logic/utils.ts b/packages/blog-logic/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7e6b2a2804fd2a3db886df435877442ed34edac --- /dev/null +++ b/packages/blog-logic/utils.ts @@ -0,0 +1,7 @@ +import { IPagination } from "./interfaces"; + +export const paginateData = (data: T[], pagination: IPagination): T[] => { + const {page, pageSize} = pagination + const startIndex = (page - 1) * pageSize; + return data.slice(startIndex, startIndex + pageSize); +} diff --git a/packages/blog-logic/wax.ts b/packages/blog-logic/wax.ts new file mode 100644 index 0000000000000000000000000000000000000000..7f81b41a4ce0ab23c3b32f3852f623411d20da75 --- /dev/null +++ b/packages/blog-logic/wax.ts @@ -0,0 +1,18 @@ +import { + createHiveChain, + TWaxExtended, + TWaxRestExtended +} from "@hiveio/wax"; +import HafbeExtendedData from "@hiveio/wax-api-hafbe"; +import WaxExtendedData from "@hiveio/wax-api-jsonrpc"; + +export type WaxExtendedChain = TWaxExtended>; + +let chain: Promise; + +export const getWax = () => { + if (!chain) + return chain = createHiveChain().then(chain => chain.extend(WaxExtendedData).extendRest(HafbeExtendedData)); + + return chain; +}; diff --git a/packages/blog-logic/wordpress-reference.ts b/packages/blog-logic/wordpress-reference.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f3666bfb806fbff0bba72fe88ac86fa1aad74e9 --- /dev/null +++ b/packages/blog-logic/wordpress-reference.ts @@ -0,0 +1,227 @@ +// https://developer.wordpress.org/rest-api/reference/ + +// REST API docs for wordpress + +export interface WPPost { + id: number; + date: Date; + date_gmt: Date; + guid: Rendered; + modified: Date; + modified_gmt: Date; + slug: string; + status: "publish" | "future" | "draft" | "pending" | "private"; + type: string; + link: string; + title: Rendered; + content: RenderedProtected; + excerpt: RenderedProtected; + author: number; + featured_media: number; + comment_status: "open" | "closed"; + ping_status: "open" | "closed"; + sticky: boolean; + template: string; + format: "standard" | "aside" | "chat" | "gallery" | "link" | "image" | "quote" | "status" | "video" | "audio"; + meta: Record; + categories: number[]; + tags: number[]; + + // Optional: Embedded or custom fields + _links?: WPLinks; + _embedded?: any; // Add specific types if using _embed +} + +export interface Rendered { + rendered: string; +} + +export interface RenderedProtected extends Rendered { + protected: boolean; +} + +export interface WPLinks { + self: WPLink[]; + collection: WPLink[]; + about: WPLink[]; + author: WPLink[]; + replies?: WPLink[]; + version_history?: WPLink[]; + "wp:featuredmedia"?: WPLink[]; + "wp:attachment"?: WPLink[]; + "wp:term"?: WPLink[]; + curies?: WPCurie[]; +} + +export interface WPLink { + href: string; +} + +export interface WPCurie { + name: string; + href: string; + templated: boolean; +} + +export interface WPComment { + id: number; + post: number; // Post ID this comment is attached to + parent: number; // Parent comment ID (if it's a reply) + author: number; // User ID (0 if anonymous) + author_name: string; + author_email: string; + author_url: string; + date: Date; + date_gmt: Date; + content: Rendered; + link: string; + status: "publish" | "future" | "draft" | "pending" | "private"; + type: string; // Usually '' (empty string for normal comment) + author_ip: string; + author_user_agent: string; + meta: Record; + + _links?: WPLinks; +} + +export interface WPGetPostsParams { + context?: "view" | "embed" | "edit"; + page?: number; + per_page?: number; + search?: string; + after?: Date; // ISO 8601 date + modified_after?: Date; // ISO 8601 date + before?: Date; // ISO 8601 date + modified_before?: Date; // ISO 8601 date + author?: number | number[]; + author_exclude?: number | number[]; + exclude?: number | number[]; + include?: number | number[]; + offset?: number; + order?: "asc" | "desc"; + orderby?: "author" | "date" | "id" | "include" | "modified" | "parent" | "relevance" | "slug" | "include_slugs" | "title"; + search_columns?: string[]; + slug?: string | string[]; + status?: string | string[]; + _fields?: string[]; +} + +export interface WPUser { + id: number; + username?: string; + name?: string; + first_name?: string; + last_name?: string; + email?: string + url?: string; + description?: string; + link: string; + locale?: string; + nickname?: string; + slug?: string; + registered_date: string; + roles?: string[]; + capabilities?: Record; + extra_capabilities?: Record; + avatar_urls: WPLink; + meta?: Record; +} + +export interface WPGetCommentsParams { + /** + * Scope under which the request is made; determines which fields appear in the response. + * One of: 'view', 'embed', 'edit' + * Default: 'view' + */ + context?: "view" | "embed" | "edit"; + + /** Current page of pagination. Default: 1 */ + page?: number; + + /** Maximum number of items per page. Default: 10 */ + per_page?: number; + + /** Limit results to comments matching this search string */ + search?: string; + + /** Limit response to comments published after this ISO-8601 date-time */ + after?: string; + + /** Limit response to comments published before this ISO-8601 date-time */ + before?: string; + + /** Limit result set to comments assigned to specific user IDs (requires authorization) */ + author?: number[]; + + /** Exclude comments assigned to specific user IDs (requires authorization) */ + author_exclude?: number[]; + + /** Limit result set to comments from a specific author email (requires authorization) */ + author_email?: string; + + /** Ensure result set excludes specific comment IDs */ + exclude?: number[]; + + /** Limit result set to specific comment IDs */ + include?: number[]; + + /** Offset the result set by a specific number of items */ + offset?: number; + + /** Order by ascending or descending. Default: 'desc' */ + order?: "asc" | "desc"; + + /** + * Attribute to sort by. + * Default: 'date_gmt' + * One of: 'date', 'date_gmt', 'id', 'include', 'post', 'parent', 'type' + */ + orderby?: "date" | "date_gmt" | "id" | "include" | "post" | "parent" | "type"; + + /** Limit result set to comments with specific parent IDs */ + parent?: number[]; + + /** Exclude comments with specific parent IDs */ + parent_exclude?: number[]; + + /** Limit result set to comments assigned to specific post IDs */ + post?: number[]; + + /** + * Limit result set to comments with a specific status (requires authorization). + * Example statuses include: 'approve' + * Default: 'approve' + */ + status?: string; + + /** + * Limit result set to comments of a specific type (requires authorization). + * Example: 'comment' + * Default: 'comment' + */ + type?: string; + + /** Password for password-protected posts */ + password?: string; +} + +export interface WPCreatePostPayload { + date?: string; + date_gmt?: string; + slug?: string; + status?: "publish" | "future" | "draft" | "pending" | "private"; + password?: string; + title?: { rendered?: string; raw?: string }; + content?: { rendered?: string; raw?: string }; + author?: number; + excerpt?: { rendered?: string; raw?: string }; + featured_media?: number; + comment_status?: "open" | "closed"; + ping_status?: "open" | "closed"; + format?: "standard" | "aside" | "chat" | "gallery" | "link" | "image" | "quote" | "status" | "video" | "audio"; + sticky?: boolean; + template?: string; + categories?: number[]; + tags?: number[]; + meta?: Record; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23d2b21d7221c4cf681cc8cf9e7c64a274712016..cb1c9d004ceb12e780f07c8ebbdf868353a9c80c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,7 +62,7 @@ catalogs: version: 2.8.1 tsx: specifier: 4.19.3 - version: 4.19.2 + version: 4.19.3 typescript: specifier: 5.7.3 version: 5.7.3 @@ -176,7 +176,7 @@ importers: version: 2.8.1 tsx: specifier: catalog:typescript-toolset - version: 4.19.2 + version: 4.19.3 typedoc: specifier: catalog:typedoc-toolset version: 0.27.3(typescript@5.7.3) @@ -190,6 +190,22 @@ importers: specifier: catalog:typescript-toolset version: 5.7.3 + packages/blog-logic: + dependencies: + '@hiveio/wax': + specifier: 1.27.6-rc10-stable.250804212246 + version: 1.27.6-rc10-stable.250804212246 + '@hiveio/wax-api-hafbe': + specifier: 1.27.12 + version: 1.27.12 + '@hiveio/wax-api-jsonrpc': + specifier: ^1.27.12 + version: 1.27.12 + devDependencies: + tsx: + specifier: ^4.20.5 + version: 4.20.6 + packages: '@aashutoshrathi/word-wrap@1.2.6': @@ -204,146 +220,158 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -405,6 +433,12 @@ packages: resolution: {integrity: sha1-0v2gSYFhN5MLQzBHIYikUyksu/k=, tarball: https://gitlab.syncad.com/api/v4/projects/198/packages/npm/@hiveio/beekeeper/-/@hiveio/beekeeper-1.27.11.tgz} engines: {node: ^20.11 || >= 21.2} + '@hiveio/wax-api-hafbe@1.27.12': + resolution: {integrity: sha1-2cLBu1vRPRoeIeyLKizlkuWcZnI=, tarball: https://gitlab.syncad.com/api/v4/projects/358/packages/npm/@hiveio/wax-api-hafbe/-/@hiveio/wax-api-hafbe-1.27.12.tgz} + + '@hiveio/wax-api-jsonrpc@1.27.12': + resolution: {integrity: sha1-2zVZzJHNCBIfPZI3qC1xa7gj0v0=, tarball: https://gitlab.syncad.com/api/v4/projects/198/packages/npm/@hiveio/wax-api-jsonrpc/-/@hiveio/wax-api-jsonrpc-1.27.12.tgz} + '@hiveio/wax@1.27.6-rc10-stable.250804212246': resolution: {integrity: sha1-qZYJ4ot0Lgy3C0fbeS1WFoxmFEY=, tarball: https://gitlab.syncad.com/api/v4/projects/419/packages/npm/@hiveio/wax/-/@hiveio/wax-1.27.6-rc10-stable.250804212246.tgz} engines: {node: ^20.11 || >= 21.2} @@ -1283,8 +1317,8 @@ packages: es6-promise@4.2.8: resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} hasBin: true @@ -1792,9 +1826,6 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - is-core-module@2.15.1: resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} @@ -2460,10 +2491,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.11.2: - resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} - engines: {node: '>=0.6'} - qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -2854,8 +2881,13 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.19.2: - resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} + tsx@4.19.3: + resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} engines: {node: '>=18.0.0'} hasBin: true @@ -3070,76 +3102,82 @@ snapshots: '@babel/helper-validator-identifier@7.25.9': optional: true - '@esbuild/aix-ppc64@0.23.1': + '@esbuild/aix-ppc64@0.25.9': + optional: true + + '@esbuild/android-arm64@0.25.9': optional: true - '@esbuild/android-arm64@0.23.1': + '@esbuild/android-arm@0.25.9': optional: true - '@esbuild/android-arm@0.23.1': + '@esbuild/android-x64@0.25.9': optional: true - '@esbuild/android-x64@0.23.1': + '@esbuild/darwin-arm64@0.25.9': optional: true - '@esbuild/darwin-arm64@0.23.1': + '@esbuild/darwin-x64@0.25.9': optional: true - '@esbuild/darwin-x64@0.23.1': + '@esbuild/freebsd-arm64@0.25.9': optional: true - '@esbuild/freebsd-arm64@0.23.1': + '@esbuild/freebsd-x64@0.25.9': optional: true - '@esbuild/freebsd-x64@0.23.1': + '@esbuild/linux-arm64@0.25.9': optional: true - '@esbuild/linux-arm64@0.23.1': + '@esbuild/linux-arm@0.25.9': optional: true - '@esbuild/linux-arm@0.23.1': + '@esbuild/linux-ia32@0.25.9': optional: true - '@esbuild/linux-ia32@0.23.1': + '@esbuild/linux-loong64@0.25.9': optional: true - '@esbuild/linux-loong64@0.23.1': + '@esbuild/linux-mips64el@0.25.9': optional: true - '@esbuild/linux-mips64el@0.23.1': + '@esbuild/linux-ppc64@0.25.9': optional: true - '@esbuild/linux-ppc64@0.23.1': + '@esbuild/linux-riscv64@0.25.9': optional: true - '@esbuild/linux-riscv64@0.23.1': + '@esbuild/linux-s390x@0.25.9': optional: true - '@esbuild/linux-s390x@0.23.1': + '@esbuild/linux-x64@0.25.9': optional: true - '@esbuild/linux-x64@0.23.1': + '@esbuild/netbsd-arm64@0.25.9': optional: true - '@esbuild/netbsd-x64@0.23.1': + '@esbuild/netbsd-x64@0.25.9': optional: true - '@esbuild/openbsd-arm64@0.23.1': + '@esbuild/openbsd-arm64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.23.1': + '@esbuild/openbsd-x64@0.25.9': optional: true - '@esbuild/sunos-x64@0.23.1': + '@esbuild/openharmony-arm64@0.25.9': optional: true - '@esbuild/win32-arm64@0.23.1': + '@esbuild/sunos-x64@0.25.9': optional: true - '@esbuild/win32-ia32@0.23.1': + '@esbuild/win32-arm64@0.25.9': optional: true - '@esbuild/win32-x64@0.23.1': + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.25.9': optional: true '@eslint-community/eslint-utils@4.4.0(eslint@9.20.1(jiti@2.4.2))': @@ -3202,6 +3240,10 @@ snapshots: '@hiveio/beekeeper@1.27.11': {} + '@hiveio/wax-api-hafbe@1.27.12': {} + + '@hiveio/wax-api-jsonrpc@1.27.12': {} + '@hiveio/wax@1.27.6-rc10-stable.250804212246': dependencies: events: 3.3.0 @@ -3687,7 +3729,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.4 + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -4281,32 +4323,34 @@ snapshots: es6-promise@4.2.8: {} - esbuild@0.23.1: + esbuild@0.25.9: optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 escalade@3.2.0: {} @@ -4558,7 +4602,7 @@ snapshots: foreground-child@3.3.0: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 forwarded@0.2.0: {} @@ -4801,7 +4845,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -4835,7 +4879,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -4927,10 +4971,6 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.13.1: - dependencies: - hasown: 2.0.0 - is-core-module@2.15.1: dependencies: hasown: 2.0.2 @@ -5377,7 +5417,7 @@ snapshots: normalize-package-data@5.0.0: dependencies: hosted-git-info: 6.1.1 - is-core-module: 2.13.1 + is-core-module: 2.15.1 semver: 7.6.3 validate-npm-package-license: 3.0.4 @@ -5653,10 +5693,6 @@ snapshots: punycode@2.3.1: {} - qs@6.11.2: - dependencies: - side-channel: 1.0.4 - qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -5970,7 +6006,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.4.1 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -6148,9 +6184,16 @@ snapshots: tslib@2.8.1: {} - tsx@4.19.2: + tsx@4.19.3: dependencies: - esbuild: 0.23.1 + esbuild: 0.25.9 + get-tsconfig: 4.10.0 + optionalDependencies: + fsevents: 2.3.3 + + tsx@4.20.6: + dependencies: + esbuild: 0.25.9 get-tsconfig: 4.10.0 optionalDependencies: fsevents: 2.3.3 @@ -6158,7 +6201,7 @@ snapshots: tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.4 + debug: 4.4.1 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color @@ -6264,7 +6307,7 @@ snapshots: union@0.5.0: dependencies: - qs: 6.11.2 + qs: 6.14.0 unique-filename@2.0.1: dependencies: diff --git a/src/chain-observers/filters/post-mention.ts b/src/chain-observers/filters/post-mention.ts index d389f6285511c9f794fdb6a884b7993c1bc65bc3..592c205c4c567472ec2c7f73c9081055e757266a 100644 --- a/src/chain-observers/filters/post-mention.ts +++ b/src/chain-observers/filters/post-mention.ts @@ -28,7 +28,7 @@ export class PostMentionFilter extends FilterBase { for(const { operation: { body } } of (operationsPerType.comment_operation ?? [])) { // Use regex to find all account mentions in the form of @username - const mentionRegex = /@([a-z0-9.-]+)/gi; + const mentionRegex = /@[a-z]+[a-z0-9.-]+[a-z0-9]+\b/g; let match: RegExpExecArray | null; while ((match = mentionRegex.exec(body)) !== null) { const mentionedAccount = match[1] as TAccountName; diff --git a/src/chain-observers/providers/mention-provider.ts b/src/chain-observers/providers/mention-provider.ts index 36910d6dcc35285c81765c1dd9263434b2d94568..c2059e0f82e430b66c67af7464342eedf1ef025b 100644 --- a/src/chain-observers/providers/mention-provider.ts +++ b/src/chain-observers/providers/mention-provider.ts @@ -47,7 +47,7 @@ export class MentionedAccountProvider = Ar postMetadataSet.add(postHash); - const mentionRegex = /@([a-z0-9.-]+)/gi; + const mentionRegex = /@[a-z]+[a-z0-9.-]+[a-z0-9]+\b/g; let match: RegExpExecArray | null; let foundMention = false; while ((match = mentionRegex.exec(operation.body)) !== null && !foundMention) {