From 0c9b56b5934016220d63a3bc7caa2b466a44d070 Mon Sep 17 00:00:00 2001 From: ItsRauf <31735267+ItsRauf@users.noreply.github.com> Date: Sat, 27 Dec 2025 01:00:47 -0500 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=91=B7=20feat(ci):=20split=20github?= =?UTF-8?q?=20actions=20across=20multiple=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/pink-owls-float.md | 12 +++++ .github/workflows/{ci.yml => changesets.yml} | 51 +++----------------- .github/workflows/tag-releases.yml | 49 +++++++++++++++++++ .github/workflows/validate.yml | 41 ++++++++++++++++ 4 files changed, 110 insertions(+), 43 deletions(-) create mode 100644 .changeset/pink-owls-float.md rename .github/workflows/{ci.yml => changesets.yml} (52%) create mode 100644 .github/workflows/tag-releases.yml create mode 100644 .github/workflows/validate.yml diff --git a/.changeset/pink-owls-float.md b/.changeset/pink-owls-float.md new file mode 100644 index 0000000..02767eb --- /dev/null +++ b/.changeset/pink-owls-float.md @@ -0,0 +1,12 @@ +--- +"@embedly/platforms": patch +"@embedly/builder": patch +"@embedly/logging": patch +"@embedly/config": patch +"@embedly/parser": patch +"@embedly/types": patch +"@embedly/api": patch +"@embedly/bot": patch +--- + +feat(ci): split github actions across multiple files diff --git a/.github/workflows/ci.yml b/.github/workflows/changesets.yml similarity index 52% rename from .github/workflows/ci.yml rename to .github/workflows/changesets.yml index 0710099..a7ab6a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/changesets.yml @@ -1,4 +1,5 @@ -name: CI +name: Changesets + env: PNPM_VERSION: "10.15.1" NODE_VERSION: "24" @@ -6,64 +7,31 @@ env: on: push: branches: [main] - pull_request: - branches: [main] concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Cache turbo build setup - uses: actions/cache@v4 - with: - path: .turbo - key: ${{ runner.os }}-turbo-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-turbo- - - name: Setup pnpm - uses: pnpm/action-setup@v2 - with: - version: ${{ env.PNPM_VERSION }} - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - cache: 'pnpm' - - name: Install dependencies - run: pnpm install --frozen-lockfile - - name: Lint and format check - run: pnpm biome ci - - name: Build all packages - run: pnpm build release: - needs: build runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' + permissions: + contents: write + pull-requests: write steps: - name: Checkout uses: actions/checkout@v4 with: - token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 - - name: Setup pnpm - uses: pnpm/action-setup@v3 + uses: pnpm/action-setup@v2 with: version: ${{ env.PNPM_VERSION }} - - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Get Release Version id: versioning run: | @@ -71,13 +39,10 @@ jobs: echo "VERSION=$(jq -r '.releases.[0].newVersion' release.json)" >> $GITHUB_OUTPUT rm release.json fi - - - name: Create Release Pull Request or Publish - id: changesets + - name: Create Release Pull Request uses: changesets/action@v1 with: - publish: pnpm changeset publish - title: '🔖 chore(release): v${{ steps.versioning.outputs.VERSION }}' commit: '🔖 chore(release): v${{ steps.versioning.outputs.VERSION }}' + title: '🔖 chore(release): v${{ steps.versioning.outputs.VERSION }}' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tag-releases.yml b/.github/workflows/tag-releases.yml new file mode 100644 index 0000000..1c63939 --- /dev/null +++ b/.github/workflows/tag-releases.yml @@ -0,0 +1,49 @@ +name: Tag Release + +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + tag: + if: | + github.event.pull_request.merged == true && + startsWith(github.event.pull_request.title, '🔖 chore(release):') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: main + + - name: Get version from package.json + id: version + run: | + VERSION=$(jq -r '.version' apps/bot/package.json) + echo "VERSION=v$VERSION" >> $GITHUB_OUTPUT + echo "Creating tag: v$VERSION" + + - name: Check if tag exists + id: check_tag + run: | + if git rev-parse "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "exists=false" >> $GITHUB_OUTPUT + fi + + - name: Create and push tag + if: steps.check_tag.outputs.exists == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a ${{ steps.version.outputs.VERSION }} -m "Release ${{ steps.version.outputs.VERSION }}" + git push origin ${{ steps.version.outputs.VERSION }} + + - name: Tag already exists + if: steps.check_tag.outputs.exists == 'true' + run: echo "Tag ${{ steps.version.outputs.VERSION }} already exists, skipping" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..ecbabaa --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,41 @@ +name: Validate (Lint and Build) +env: + PNPM_VERSION: "10.15.1" + NODE_VERSION: "24" + +on: + push: + branches: ['**'] + pull_request: + branches: [main, devel] + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: ${{ env.PNPM_VERSION }} + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'pnpm' + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Lint and format check + run: pnpm biome ci + - name: Build all packages + run: pnpm build From 602e91fe4b1b34d7597d33aa6d4baee1db042143 Mon Sep 17 00:00:00 2001 From: ItsRauf <31735267+ItsRauf@users.noreply.github.com> Date: Tue, 13 Jan 2026 20:15:10 -0600 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=94=A7=20feat(workspace):=20add=20git?= =?UTF-8?q?hub=20templates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bug report and feature request templates --- .github/ISSUE_TEMPLATE/bug-report.yml | 58 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/feature-request.yml | 35 +++++++++++++ pnpm-lock.yaml | 28 +++++++++-- 4 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..74d1793 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,58 @@ +name: Bug Report +description: Report a bug with Embedly +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. + + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Describe what went wrong + placeholder: The bot didn't embed my Twitter link + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect to happen? + description: What should have happened instead? + placeholder: Expected a rich embed with the tweet content + validations: + required: true + + - type: input + id: link + attributes: + label: Link to the post + description: If applicable, include the link that failed + placeholder: https://twitter.com/user/status/123456789 + validations: + required: false + + - type: dropdown + id: platform + attributes: + label: Platform + description: Which platform is this bug related to? + options: + - Twitter/X + - Instagram + - TikTok + - Threads + - Other + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional context + description: Any other info that might be relevant + placeholder: This only happens with reply chains + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..84701ff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Questions + url: https://github.com/embed-team/embedly/discussions + about: For general questions, use Discussions instead diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..8ba4e1e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,35 @@ +name: Feature Request +description: Suggest a new feature for Embedly +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Got an idea? Let's hear it. + + - type: textarea + id: feature + attributes: + label: What feature do you want? + description: Describe the feature you'd like to see + placeholder: Add support for Bluesky posts + validations: + required: true + + - type: textarea + id: why + attributes: + label: Why? + description: Why would this be useful? + placeholder: Bluesky is growing and Discord embeds don't work for it + validations: + required: true + + - type: textarea + id: alternative + attributes: + label: Alternatives + description: Are there any workarounds or alternative solutions? + placeholder: Currently have to manually screenshot posts + validations: + required: false diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0661519..c31e8a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,7 +76,7 @@ importers: version: 1.2.0 elysia: specifier: ^1.4.19 - version: 1.4.19(@sinclair/typebox@0.34.38)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3) + version: 1.4.19(@sinclair/typebox@0.34.38)(@types/bun@1.3.5)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3) wrangler: specifier: ^4.56.0 version: 4.56.0(@cloudflare/workers-types@4.20251220.0) @@ -116,7 +116,7 @@ importers: version: 2.0.4 '@elysiajs/eden': specifier: ^1.4.5 - version: 1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.38)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3)) + version: 1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.38)(@types/bun@1.3.5)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3)) '@logtail/node': specifier: ^0.5.6 version: 0.5.6 @@ -1521,6 +1521,9 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/bun@1.3.5': + resolution: {integrity: sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -1702,6 +1705,9 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + bun-types@1.3.5: + resolution: {integrity: sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -3182,6 +3188,7 @@ packages: wrangler@4.56.0: resolution: {integrity: sha512-Nqi8duQeRbA+31QrD6QlWHW3IZVnuuRxMy7DEg46deUzywivmaRV/euBN5KKXDPtA24VyhYsK7I0tkb7P5DM2w==} engines: {node: '>=20.0.0'} + deprecated: Version 4.55.0 and 4.56.0 can incorrectly automatically delegate 'wrangler deploy' to 'opennextjs-cloudflare'. Use an older or newer version. hasBin: true peerDependencies: '@cloudflare/workers-types': ^4.20251217.0 @@ -3687,9 +3694,9 @@ snapshots: - bufferutil - utf-8-validate - '@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.38)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3))': + '@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.38)(@types/bun@1.3.5)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3))': dependencies: - elysia: 1.4.19(@sinclair/typebox@0.34.38)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3) + elysia: 1.4.19(@sinclair/typebox@0.34.38)(@types/bun@1.3.5)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3) '@emnapi/runtime@1.7.1': dependencies: @@ -4326,6 +4333,11 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 25.0.3 + '@types/bun@1.3.5': + dependencies: + bun-types: 1.3.5 + optional: true + '@types/connect@3.4.38': dependencies: '@types/node': 25.0.3 @@ -4502,6 +4514,11 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bun-types@1.3.5: + dependencies: + '@types/node': 25.0.3 + optional: true + callsites@3.1.0: {} camelcase@8.0.0: {} @@ -4731,7 +4748,7 @@ snapshots: dotenv@8.6.0: {} - elysia@1.4.19(@sinclair/typebox@0.34.38)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3): + elysia@1.4.19(@sinclair/typebox@0.34.38)(@types/bun@1.3.5)(exact-mirror@0.1.2(@sinclair/typebox@0.34.38))(file-type@21.0.0)(openapi-types@12.1.3)(typescript@5.9.3): dependencies: '@sinclair/typebox': 0.34.38 cookie: 1.1.1 @@ -4741,6 +4758,7 @@ snapshots: memoirist: 0.4.0 openapi-types: 12.1.3 optionalDependencies: + '@types/bun': 1.3.5 typescript: 5.9.3 emoji-regex@10.5.0: {} From d40a6892ac3ee724fd600957c0d09dc4a84c7538 Mon Sep 17 00:00:00 2001 From: ItsRauf <31735267+ItsRauf@users.noreply.github.com> Date: Tue, 13 Jan 2026 21:00:12 -0600 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat(platforms):=20swi?= =?UTF-8?q?tch=20rehype=20out=20for=20cheerio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(platforms/tiktok): fetch video cdn url for better playback reliability --- packages/platforms/package.json | 6 +- packages/platforms/src/CBC.ts | 30 +-- packages/platforms/src/TikTok.ts | 22 +- pnpm-lock.yaml | 349 ++++++++++++++----------------- 4 files changed, 171 insertions(+), 236 deletions(-) diff --git a/packages/platforms/package.json b/packages/platforms/package.json index a1e866f..8f5775b 100644 --- a/packages/platforms/package.json +++ b/packages/platforms/package.json @@ -30,9 +30,7 @@ "@embedly/parser": "workspace:*", "@embedly/types": "workspace:*", "@types/he": "^1.2.3", - "he": "^1.2.0", - "rehype-parse": "^9.0.1", - "unified": "^11.0.5", - "unist-util-visit": "^5.0.0" + "cheerio": "^1.1.2", + "he": "^1.2.0" } } diff --git a/packages/platforms/src/CBC.ts b/packages/platforms/src/CBC.ts index edb5586..5acbb68 100644 --- a/packages/platforms/src/CBC.ts +++ b/packages/platforms/src/CBC.ts @@ -9,10 +9,8 @@ import { type BaseEmbedData, EmbedlyPlatformType } from "@embedly/types"; +import * as cheerio from "cheerio"; import he from "he"; -import rehypeParse from "rehype-parse"; -import { unified } from "unified"; -import { visit } from "unist-util-visit"; import { EmbedlyPlatform } from "./Platform.ts"; export class CBC extends EmbedlyPlatform { @@ -41,23 +39,15 @@ export class CBC extends EmbedlyPlatform { if (!resp.ok) { throw { code: resp.status, message: resp.statusText }; } - const hast = unified() - .use(rehypeParse) - .parse(await resp.text()); - let data: any; - visit(hast, "element", (node) => { - if ( - node.tagName === "script" && - node.properties.id === "initialStateDom" - ) { - const text = node.children.find( - (c) => c.type === "text" - )!.value; - data = JSON.parse( - text.replace("window.__INITIAL_STATE__ = ", "").slice(0, -1) - ); - } - }); + const html = await resp.text(); + const $ = cheerio.load(html); + const script = $("script#initialStateDom"); + const data = JSON.parse( + script + .text() + .replace("window.__INITIAL_STATE__ = ", "") + .slice(0, -1) + ); return data.app.meta.jsonld; } diff --git a/packages/platforms/src/TikTok.ts b/packages/platforms/src/TikTok.ts index 131e9a7..df8d118 100644 --- a/packages/platforms/src/TikTok.ts +++ b/packages/platforms/src/TikTok.ts @@ -8,9 +8,7 @@ import { type BaseEmbedData, EmbedlyPlatformType } from "@embedly/types"; -import rehypeParse from "rehype-parse"; -import { unified } from "unified"; -import { visit } from "unist-util-visit"; +import * as cheerio from "cheerio"; import { EmbedlyPlatform } from "./Platform.ts"; export class TikTok extends EmbedlyPlatform { @@ -49,20 +47,10 @@ export class TikTok extends EmbedlyPlatform { if (!resp.ok) { throw { code: resp.status, message: resp.statusText }; } - const hast = unified() - .use(rehypeParse) - .parse(await resp.text()); - let data: any; - visit(hast, "element", (node) => { - if ( - node.tagName === "script" && - node.properties.id === "__UNIVERSAL_DATA_FOR_REHYDRATION__" - ) { - data = JSON.parse( - node.children.find((c) => c.type === "text")!.value - ); - } - }); + const html = await resp.text(); + const $ = cheerio.load(html); + const script = $("script#__UNIVERSAL_DATA_FOR_REHYDRATION__"); + const data = JSON.parse(script.text()); return data.__DEFAULT_SCOPE__["webapp.video-detail"].itemInfo .itemStruct; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c31e8a5..56f95ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -255,18 +255,12 @@ importers: '@types/he': specifier: ^1.2.3 version: 1.2.3 + cheerio: + specifier: ^1.1.2 + version: 1.1.2 he: specifier: ^1.2.0 version: 1.2.0 - rehype-parse: - specifier: ^9.0.1 - version: 9.0.1 - unified: - specifier: ^11.0.5 - version: 11.0.5 - unist-util-visit: - specifier: ^5.0.0 - version: 5.0.0 devDependencies: '@biomejs/biome': specifier: 2.3.10 @@ -1539,9 +1533,6 @@ packages: '@types/express@4.17.23': resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/he@1.2.3': resolution: {integrity: sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==} @@ -1575,9 +1566,6 @@ packages: '@types/stack-trace@0.0.33': resolution: {integrity: sha512-O7in6531Bbvlb2KEsJ0dq0CHZvc3iWSR5ZYMtvGgnHA56VgriAN/AU2LorfmcvAl2xc9N5fbCTRyMRRl8nd74g==} - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1668,9 +1656,6 @@ packages: atomically@2.0.3: resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1691,6 +1676,9 @@ packages: blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + boxen@8.0.1: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} @@ -1730,6 +1718,13 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.1.2: + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1779,9 +1774,6 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commitlint-config-gitmoji@2.3.1: resolution: {integrity: sha512-T15ssbsyNc6szHlnGWo0/xvIA1mObqM70E9TwKNVTpksxhm+OdFht8hvDdKJAVi4nlZX5tcfTeILOi7SHBGH3w==} @@ -1844,6 +1836,13 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + dargs@8.1.0: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} @@ -1896,10 +1895,6 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -1908,9 +1903,6 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1926,6 +1918,19 @@ packages: resolution: {integrity: sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g==} engines: {node: '>=18'} + 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@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -1959,10 +1964,17 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + entities@6.0.1: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} @@ -2048,9 +2060,6 @@ packages: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -2195,22 +2204,13 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hast-util-from-html@2.0.3: - resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} - - hast-util-from-parse5@8.0.3: - resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} - - hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - - hastscript@9.0.1: - resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -2231,6 +2231,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + iconv-lite@0.7.1: resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} engines: {node: '>=0.10.0'} @@ -2331,10 +2335,6 @@ packages: resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} engines: {node: '>=12'} - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -2613,6 +2613,9 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -2694,6 +2697,12 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -2756,9 +2765,6 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -2799,9 +2805,6 @@ packages: resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} engines: {node: '>=12'} - rehype-parse@9.0.1: - resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -2922,9 +2925,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} @@ -3026,9 +3026,6 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-mixer@6.0.4: resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==} @@ -3113,21 +3110,6 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - - unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} - - unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -3143,21 +3125,9 @@ packages: resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} engines: {node: '>= 0.10'} - vfile-location@5.0.3: - resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} - - vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -3165,6 +3135,15 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -4362,10 +4341,6 @@ snapshots: '@types/qs': 6.14.0 '@types/serve-static': 1.15.8 - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - '@types/he@1.2.3': {} '@types/http-errors@2.0.5': {} @@ -4397,8 +4372,6 @@ snapshots: '@types/stack-trace@0.0.33': {} - '@types/unist@3.0.3': {} - '@types/ws@8.18.1': dependencies: '@types/node': 25.0.3 @@ -4470,8 +4443,6 @@ snapshots: stubborn-fs: 1.2.5 when-exit: 2.1.4 - bail@2.0.2: {} - balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -4490,6 +4461,8 @@ snapshots: blake3-wasm@2.1.5: {} + boolbase@1.0.0: {} + boxen@8.0.1: dependencies: ansi-align: 3.0.1 @@ -4534,6 +4507,29 @@ snapshots: chardet@2.1.1: {} + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.1.2: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.0.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.14.0 + whatwg-mimetype: 4.0.0 + ci-info@3.9.0: {} cjs-module-lexer@2.1.1: {} @@ -4576,8 +4572,6 @@ snapshots: color-convert: 2.0.1 color-string: 1.9.1 - comma-separated-tokens@2.0.3: {} - commitlint-config-gitmoji@2.3.1: dependencies: '@commitlint/types': 17.8.1 @@ -4665,6 +4659,16 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.2.2: {} + dargs@8.1.0: {} data-uri-to-buffer@4.0.1: {} @@ -4699,16 +4703,10 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 - dequal@2.0.3: {} - detect-indent@6.1.0: {} detect-libc@2.1.2: {} - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -4738,6 +4736,24 @@ snapshots: - bufferutil - utf-8-validate + 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@5.3.0: dependencies: is-obj: 2.0.0 @@ -4765,11 +4781,18 @@ snapshots: emoji-regex@8.0.0: {} + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + entities@4.5.0: {} + entities@6.0.1: {} env-paths@2.2.1: {} @@ -4911,8 +4934,6 @@ snapshots: exit-hook@2.2.1: {} - extend@3.0.2: {} - extendable-error@0.1.7: {} external-editor@3.1.0: @@ -5075,39 +5096,14 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-from-html@2.0.3: - dependencies: - '@types/hast': 3.0.4 - devlop: 1.1.0 - hast-util-from-parse5: 8.0.3 - parse5: 7.3.0 - vfile: 6.0.3 - vfile-message: 4.0.2 - - hast-util-from-parse5@8.0.3: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - devlop: 1.1.0 - hastscript: 9.0.1 - property-information: 7.1.0 - vfile: 6.0.3 - vfile-location: 5.0.3 - web-namespaces: 2.0.1 - - hast-util-parse-selector@4.0.0: - dependencies: - '@types/hast': 3.0.4 + he@1.2.0: {} - hastscript@9.0.1: + htmlparser2@10.0.0: dependencies: - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 4.0.0 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - - he@1.2.0: {} + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.1 http-proxy-agent@7.0.2: dependencies: @@ -5131,6 +5127,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + iconv-lite@0.7.1: dependencies: safer-buffer: 2.1.2 @@ -5215,8 +5215,6 @@ snapshots: is-path-inside@4.0.0: {} - is-plain-obj@4.1.0: {} - is-reference@1.2.1: dependencies: '@types/estree': 1.0.8 @@ -5436,6 +5434,10 @@ snapshots: dependencies: path-key: 4.0.0 + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -5542,6 +5544,15 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -5590,8 +5601,6 @@ snapshots: prettier@2.8.8: {} - property-information@7.1.0: {} - proto-list@1.2.4: {} proxy-agent@6.5.0: @@ -5645,12 +5654,6 @@ snapshots: dependencies: rc: 1.2.8 - rehype-parse@9.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-from-html: 2.0.3 - unified: 11.0.5 - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -5795,8 +5798,6 @@ snapshots: source-map@0.6.1: optional: true - space-separated-tokens@2.0.2: {} - spawndamnit@3.0.1: dependencies: cross-spawn: 7.0.6 @@ -5880,8 +5881,6 @@ snapshots: tr46@0.0.3: {} - trough@2.2.0: {} - ts-mixer@6.0.4: {} tslib@2.8.1: {} @@ -5942,35 +5941,6 @@ snapshots: unicorn-magic@0.1.0: {} - unified@11.0.5: - dependencies: - '@types/unist': 3.0.3 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.3 - - unist-util-is@6.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.1: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - - unist-util-visit@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - universalify@0.1.2: {} update-notifier@7.3.1: @@ -5990,31 +5960,20 @@ snapshots: validator@13.15.15: {} - vfile-location@5.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile: 6.0.3 - - vfile-message@4.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.2 - wcwidth@1.0.1: dependencies: defaults: 1.0.4 - web-namespaces@2.0.1: {} - web-streams-polyfill@3.3.3: {} webidl-conversions@3.0.1: {} + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 From 526fa6480febe43f6f928580e2a84601fae891a0 Mon Sep 17 00:00:00 2001 From: ItsRauf <31735267+ItsRauf@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:37:46 -0600 Subject: [PATCH 4/5] =?UTF-8?q?=E2=9A=97=EF=B8=8F=20feat(bot,builder):=20a?= =?UTF-8?q?dd=20posthog=20for=20a/b=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added embed link style test --- apps/bot/package.json | 3 +- apps/bot/src/client.ts | 10 +++- apps/bot/src/commands/embed.ts | 21 +++++--- apps/bot/src/listeners/messageCreate.ts | 13 ++++- packages/builder/src/Embed.ts | 69 ++++++++++++++++--------- packages/platforms/src/TikTok.ts | 8 ++- pnpm-lock.yaml | 18 +++++++ 7 files changed, 104 insertions(+), 38 deletions(-) diff --git a/apps/bot/package.json b/apps/bot/package.json index d892f47..d416e83 100644 --- a/apps/bot/package.json +++ b/apps/bot/package.json @@ -39,6 +39,7 @@ "@logtail/node": "^0.5.6", "@sapphire/discord.js-utilities": "^7.3.3", "@sapphire/framework": "^5.4.0", - "discord.js": "~14.25.1" + "discord.js": "~14.25.1", + "posthog-node": "^5.20.0" } } diff --git a/apps/bot/src/client.ts b/apps/bot/src/client.ts index d68d5d0..f089c34 100644 --- a/apps/bot/src/client.ts +++ b/apps/bot/src/client.ts @@ -5,11 +5,13 @@ import { GatewayIntentBits, PresenceUpdateStatus } from "discord.js"; +import { PostHog } from "posthog-node"; declare module "@sapphire/framework" { interface Container { betterstack: Logtail; embed_authors: Map; + posthog: PostHog; } } @@ -43,11 +45,15 @@ export class EmbedlyClient extends SapphireClient { } ); container.embed_authors = new Map(); + container.posthog = new PostHog(process.env.POSTHOG_API_KEY!, { + host: process.env.POSTHOG_HOST + }); return super.login(token); } - public override destroy() { - container.betterstack?.flush(); + public override async destroy() { + await container.betterstack?.flush(); + await container.posthog.shutdown(); return super.destroy(); } } diff --git a/apps/bot/src/commands/embed.ts b/apps/bot/src/commands/embed.ts index 8e64b94..e36f651 100644 --- a/apps/bot/src/commands/embed.ts +++ b/apps/bot/src/commands/embed.ts @@ -1,6 +1,10 @@ import { treaty } from "@elysiajs/eden"; import type { App } from "@embedly/api"; -import { Embed, EmbedFlags } from "@embedly/builder"; +import { + Embed, + EmbedFlagNames, + type EmbedFlags +} from "@embedly/builder"; import { EMBEDLY_EMBED_CREATED_COMMAND, EMBEDLY_NO_LINK_IN_MESSAGE, @@ -99,7 +103,7 @@ export class EmbedCommand extends Command { | Command.ChatInputCommandInteraction | Command.ContextMenuCommandInteraction, content: string, - flags?: Partial> + flags?: Partial ) { const log_ctx = { interaction_id: interaction.id, @@ -190,13 +194,18 @@ export class EmbedCommand extends Command { interaction: Command.ChatInputCommandInteraction ) { const url = interaction.options.getString("url", true); + const link_style = (await this.container.posthog.getFeatureFlag( + "embed-link-styling-test", + interaction.user.id + )) as EmbedFlags[EmbedFlagNames.LinkStyle] | undefined; this.fetchEmbed(interaction, url, { - [EmbedFlags.MediaOnly]: + [EmbedFlagNames.MediaOnly]: interaction.options.getBoolean("media_only") ?? false, - [EmbedFlags.SourceOnly]: + [EmbedFlagNames.SourceOnly]: interaction.options.getBoolean("source_only") ?? false, - [EmbedFlags.Spoiler]: - interaction.options.getBoolean("spoiler") ?? false + [EmbedFlagNames.Spoiler]: + interaction.options.getBoolean("spoiler") ?? false, + [EmbedFlagNames.LinkStyle]: link_style ?? "control" }); } } diff --git a/apps/bot/src/listeners/messageCreate.ts b/apps/bot/src/listeners/messageCreate.ts index 72e16d8..218a28e 100644 --- a/apps/bot/src/listeners/messageCreate.ts +++ b/apps/bot/src/listeners/messageCreate.ts @@ -1,6 +1,10 @@ import { treaty } from "@elysiajs/eden"; import type { App } from "@embedly/api"; -import { Embed, EmbedFlags } from "@embedly/builder"; +import { + Embed, + EmbedFlagNames, + type EmbedFlags +} from "@embedly/builder"; import { EMBEDLY_EMBED_CREATED_MESSAGE, type EmbedlyInteractionContext, @@ -76,10 +80,15 @@ export class MessageListener extends Listener< } const embed = await Platforms[platform.type].createEmbed(data); + const link_style = (await this.container.posthog.getFeatureFlag( + "embed-link-styling-test", + message.author.id + )) as EmbedFlags[EmbedFlagNames.LinkStyle] | undefined; const msg = { components: [ Embed.getDiscordEmbed(embed, { - [EmbedFlags.Spoiler]: isSpoiler(url, message.content) + [EmbedFlagNames.Spoiler]: isSpoiler(url, message.content), + [EmbedFlagNames.LinkStyle]: link_style ?? "control" })! ], flags: MessageFlags.IsComponentsV2, diff --git a/packages/builder/src/Embed.ts b/packages/builder/src/Embed.ts index 2232ba4..8bf15ad 100644 --- a/packages/builder/src/Embed.ts +++ b/packages/builder/src/Embed.ts @@ -31,10 +31,18 @@ export interface EmbedData extends BaseEmbedData { replying_to?: BaseEmbedDataWithoutPlatform; } -export enum EmbedFlags { +export enum EmbedFlagNames { MediaOnly = "MediaOnly", SourceOnly = "SourceOnly", - Spoiler = "Spoiler" + Spoiler = "Spoiler", + LinkStyle = "LinkStyle" +} + +export interface EmbedFlags { + [EmbedFlagNames.MediaOnly]: boolean; + [EmbedFlagNames.SourceOnly]: boolean; + [EmbedFlagNames.Spoiler]: boolean; + [EmbedFlagNames.LinkStyle]: "control" | "inline" | "none"; } export class Embed implements EmbedData { @@ -78,13 +86,11 @@ export class Embed implements EmbedData { this.replying_to = replying_to; } - static getDiscordEmbed( - embed: Embed, - flags?: Partial> - ) { - const media_only = flags?.[EmbedFlags.MediaOnly]; - const source_only = flags?.[EmbedFlags.SourceOnly]; - const hidden = flags?.[EmbedFlags.Spoiler]; + static getDiscordEmbed(embed: Embed, flags?: Partial) { + const media_only = flags?.[EmbedFlagNames.MediaOnly]; + const source_only = flags?.[EmbedFlagNames.SourceOnly]; + const hidden = flags?.[EmbedFlagNames.Spoiler]; + const link_style = flags?.[EmbedFlagNames.LinkStyle] ?? "control"; if (media_only) { return Embed.buildMediaOnlyEmbed(embed, hidden); @@ -109,7 +115,7 @@ export class Embed implements EmbedData { } // Add footer with stats and metadata - Embed.addFooterSection(container, embed); + Embed.addFooterSection(container, embed, link_style); return container.toJSON(); } @@ -294,7 +300,7 @@ export class Embed implements EmbedData { prefix_emoji: string ): string { const username_part = username - ? ` (${hyperlink(`@${escapeMarkdown(username, { italic: true, underline: true })}`, profile_url)})` + ? ` (${hyperlink(`@${escapeMarkdown(username, { italic: true, underline: true })}`, profile_url!)})` : ""; const full_text = `${prefix_emoji} ${name}${username_part}`.trim(); @@ -304,33 +310,46 @@ export class Embed implements EmbedData { private static addFooterSection( container: ContainerBuilder, - embed: Embed + embed: Embed, + link_style: EmbedFlags[EmbedFlagNames.LinkStyle] ) { const stats = Embed.formatStats(embed); - container.addSectionComponents((builder) => { - if (stats.length > 0) { + if (link_style === "control") { + container.addSectionComponents((builder) => { builder.addTextDisplayComponents((builder) => - builder.setContent(subtext(stats.join(" "))) - ); - } - - return builder - .addTextDisplayComponents((builder) => builder.setContent( - `${emojis[embed.platform]} • ${time( + `${stats.length > 0 ? `${subtext(stats.join(" "))}\n` : ""}${emojis[embed.platform]} • ${time( embed.timestamp, - TimestampStyles.ShortDateTime + TimestampStyles.LongDateShortTime )}` ) - ) - .setButtonAccessory((builder) => + ); + + builder.setButtonAccessory((builder) => builder .setStyle(ButtonStyle.Link) .setURL(embed.url) .setLabel(`View on ${embed.platform}`) ); - }); + + return builder; + }); + return; + } + + container.addSeparatorComponents((builder) => + builder.setDivider(true).setSpacing(SeparatorSpacingSize.Large) + ); + + container.addTextDisplayComponents((builder) => + builder.setContent( + `${stats.length > 0 ? `${subtext(stats.join(" "))}\n` : ""}${emojis[embed.platform]} • ${time( + embed.timestamp, + TimestampStyles.LongDateShortTime + )} • ${link_style === "inline" ? hyperlink(`View on ${embed.platform}`, embed.url) : ""}` + ) + ); } private static formatStats(embed: Embed): string[] { diff --git a/packages/platforms/src/TikTok.ts b/packages/platforms/src/TikTok.ts index df8d118..fa34b13 100644 --- a/packages/platforms/src/TikTok.ts +++ b/packages/platforms/src/TikTok.ts @@ -74,12 +74,16 @@ export class TikTok extends EmbedlyPlatform { }; } - createEmbed(post_data: any): Embed { + async createEmbed(post_data: any): Promise { const embed = new Embed(this.transformRawData(post_data)); + const video = await fetch( + post_data.video.bitrateInfo[0].PlayAddr.UrlList[2], + { redirect: "follow" } + ); embed.setMedia([ { media: { - url: post_data.video.bitrateInfo[0].PlayAddr.UrlList[2] + url: video.url } } ]); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56f95ab..f795203 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,6 +129,9 @@ importers: discord.js: specifier: ~14.25.1 version: 14.25.1 + posthog-node: + specifier: ^5.20.0 + version: 5.20.0 devDependencies: '@biomejs/biome': specifier: 2.3.10 @@ -1259,6 +1262,9 @@ packages: '@poppinss/exception@1.2.3': resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@posthog/core@1.9.1': + resolution: {integrity: sha512-kRb1ch2dhQjsAapZmu6V66551IF2LnCbc1rnrQqnR7ArooVyJN9KOPXre16AJ3ObJz2eTfuP7x25BMyS2Y5Exw==} + '@rollup/plugin-alias@6.0.0': resolution: {integrity: sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==} engines: {node: '>=20.19.0'} @@ -2760,6 +2766,10 @@ packages: typescript: optional: true + posthog-node@5.20.0: + resolution: {integrity: sha512-LkR5KfrvEQTnUtNKN97VxFB00KcYG1Iz8iKg8r0e/i7f1eQhg1WSZO+Jp1B4bvtHCmdpIE4HwYbvCCzFoCyjVg==} + engines: {node: '>=20'} + prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -4105,6 +4115,10 @@ snapshots: '@poppinss/exception@1.2.3': {} + '@posthog/core@1.9.1': + dependencies: + cross-spawn: 7.0.6 + '@rollup/plugin-alias@6.0.0(rollup@4.53.5)': optionalDependencies: rollup: 4.53.5 @@ -5599,6 +5613,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 + posthog-node@5.20.0: + dependencies: + '@posthog/core': 1.9.1 + prettier@2.8.8: {} proto-list@1.2.4: {} From 7cc3f370aa9097c889c71d2f214cecc6201430c0 Mon Sep 17 00:00:00 2001 From: ItsRauf <31735267+ItsRauf@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:40:17 -0600 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=94=96=20chore(workspace):=20add=20ch?= =?UTF-8?q?angeset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/fancy-planets-cut.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fancy-planets-cut.md diff --git a/.changeset/fancy-planets-cut.md b/.changeset/fancy-planets-cut.md new file mode 100644 index 0000000..4e96139 --- /dev/null +++ b/.changeset/fancy-planets-cut.md @@ -0,0 +1,6 @@ +--- +"@embedly/builder": minor +"@embedly/bot": minor +--- + +switched out rehype/unified for cheerio. added posthog for a/b testing. added embed link style test