
LeTUI
TUI library built from scratch with TypeScript and a Rust core, featuring a component-based API, signal-based state management, CSS-like styling API, a Rust-powered layout and rendering engine, and cell-based diffing.


TUI library built from scratch with TypeScript and a Rust core, featuring a component-based API, signal-based state management, CSS-like styling API, a Rust-powered layout and rendering engine, and cell-based diffing.
original
anime4kIn-browser MKV video streamer and player built from scratch, featuring real-time 4K anime upscaling powered by WGSL (WebGPU) shaders ported from Anime4K.

Web app and CLI for exporting X threads, articles, posts, and replies as Markdown or JSON, built around resumable jobs.
Paid music collaborations and song-cover campaigns for artists worldwide. Led frontend and backend development across both products, built the Next.js frontend from scratch, and owned payments, payouts, and mobile audio features. Cut data processing for 1M+ artists from nine days to seven hours and roughly halved Soundcharts costs.
AI mapping platform for satellite and aerial imagery. Built high-performance React interfaces for large imagery datasets, simplified component state and data flow, and helped move users to centralized sign-in with zero downtime.
Social content and analytics platform with shoppable feeds for brands. Led React adoption within an Angular application and improved embedded app performance for e-commerce sites across Europe. Built a custom state manager that cut the JavaScript bundle by 20% and automated package publishing.
Online clothing brand. Fully automated stock updates with a tested pipeline that transformed Airtable inventory data and stored it in the website's database.
Online sports insurance company for individuals, tournaments, etc. Built payment and subscription workflows and internal support tools, redesigned the PostgreSQL schema, and consolidated two backends into one codebase.
Xport exports X posts, threads, user timelines, and articles — as Markdown or JSON — from a web app, a direct link, or a CLI.
@frixaco/xport on npmXport is built around resumable export jobs. A browser creates a PostgreSQL-backed job, the server runs a disposable in-process worker to fetch pages from the upstream X data API, and each page writes progress, cursors, credits, and fetched posts back to Postgres. If the process dies or the browser reloads, the job can be resumed from its jobId and stored next_cursor.
| Home | Stopped user fetch | Article fetch |
|---|---|---|
![]() | ![]() | ![]() |
@/username or profile URL.xport CLI.jobId URL.xport.frixaco.com/x.com/... and xport.frixaco.com/twitter.com/....web/src/routes/api.cli/src.core.The upstream X data API is only reachable server-side, through X_API_URL and X_API_KEY.
The main backend state machine lives in web/src/lib/fetch-job.ts: create, claim, fetch, store, charge, stop, resume, complete, and fail. A runner claims work with an internal runner_id, writes progress only while it owns the job, and clears runner_id when the job reaches completed, stopped, or failed.
Routes:
/ - main export UI./x.com/<path> and /twitter.com/<path> - direct-export redirects./auth-error and /checkout/success - utility redirects./api/* - server API routes.| Source | Formats | Filename |
|---|---|---|
| Thread | Markdown, JSON | <username>-thread.<ext> |
| User timeline | Markdown, JSON | <username>-user-posts.<ext> |
| User replies | Markdown, JSON | <username>-replies.<ext> |
| Partial | Markdown, JSON | Adds -partial before extension |
| Article | Markdown | <sanitized-article-title>.md |
Requirements: Node.js 24.x, pnpm 11.x, PostgreSQL.
pn install
pn run db:migrate
pn run devThe app needs environment variables to boot — see Environment. The minimal set to sign in and run an export: DATABASE_URL, BETTER_AUTH_URL, BETTER_AUTH_SECRET, one OAuth provider (GitHub or Google), and X_API_URL/X_API_KEY. PostHog and Polar are optional; without Polar credits, grants can be issued locally with pn --filter web run credits:grant -- <email> <credits> "reason".
Useful commands:
pn run build
pn run check
pn run lint
pn run format
pn run db:generate
pn run db:migrateRequired:
DATABASE_URL - PostgreSQL connection string.BETTER_AUTH_URL - origin used for auth; must match the deployed domain in production.BETTER_AUTH_SECRET - auth secret.X_API_URL, X_API_KEY - upstream X data API; server-side only.GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET or GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET - at least one OAuth provider for sign-in.Optional:
SITE_URL - canonical origin for SEO metadata; falls back to BETTER_AUTH_URL, then http://localhost:3000.POLAR_ENV, POLAR_ACCESS_TOKEN or SANDBOX_POLAR_ACCESS_TOKEN, POLAR_WEBHOOK_SECRET, POLAR_CREDITS_50_CREDITS_PRODUCT_ID or SANDBOX_POLAR_CREDITS_50_CREDITS_PRODUCT_ID, POLAR_CREDITS_500_CREDITS_PRODUCT_ID or SANDBOX_POLAR_CREDITS_500_CREDITS_PRODUCT_ID. Signup credits are granted locally after user creation, with an idempotent retry on session creation to recover interrupted signups. Purchased credits are granted by the verified Polar order.paid webhook; enable that event at /api/auth/polar/webhooks and set the endpoint API version to 2026-10 in both sandbox and production. The SDK pins requests to 2026-10; updating the SDK does not migrate existing webhook endpoints. The legacy product environment-variable names map to 125 and 1250 credits respectively; see web/src/lib/billing-products.ts.BILLING_MAINTENANCE=true - blocks new billing requests, signup/purchase processing, and Polar delivery during cutover; job stop requests remain available.PUBLIC_POSTHOG_KEY, PUBLIC_POSTHOG_HOST. Omitted safely when unset.Production runs on Railway at https://xport.frixaco.com.
Railway uses railway.json:
pn buildpn start/railway service xport-web
railway up --detach
railway service status
railway logsDatabase schema is managed with Drizzle:
web/src/db/schema.tsweb/migrations/pn run db:generatepn run db:migrateThe CLI (cli/src, published as @frixaco/xport) calls the deployed or local Xport server APIs; Social API credentials stay server-side.
Install from npm:
npm install -g @frixaco/xport
xport login
xport export --format markdown --out . "https://x.com/burakeregar/status/2020852442230120752"
xport posts --format markdown --out . "@frixaco"
xport replies --format markdown --out . "@frixaco"
xport stop <jobId>Prepare the npm package locally:
pn --filter @frixaco/xport build
pn --filter @frixaco/xport pack --pack-destination /tmpWorkspace usage:
pn --filter @frixaco/xport xport login
pn --filter @frixaco/xport xport whoami
pn --filter @frixaco/xport xport credits
pn --filter @frixaco/xport xport export --format markdown --out . "https://x.com/burakeregar/status/2020852442230120752"
pn --filter @frixaco/xport xport export --format json --stdout "@frixaco"
pn --filter @frixaco/xport xport posts --format markdown --out . "@frixaco"
pn --filter @frixaco/xport xport replies --format json --stdout "@frixaco"
pn --filter @frixaco/xport xport stop <jobId>
pn --filter @frixaco/xport xport logoutCLI export options must come before the input: xport export [options] <input> is the only supported form. Thread and user exports print Job ID: <uuid> to stderr as soon as the resumable job is created, and xport stop <jobId> stops that job from another terminal. Auth uses the Better Auth device code flow; tokens are stored at ~/.config/xport/config.json with 0600 permissions. For automation, set XPORT_TOKEN; for local servers, set XPORT_BASE_URL.
Older local extraction scripts in cli/ talk to the upstream API directly and read cli/.env; see cli/.env.example.
MIT.
pnpm --filter web exec playwright install chromium # once per machine
pnpm test # logic + PostgreSQL + HTTP/CLI + browser, no provider credentials
pnpm test:live # real X API + Polar sandbox checkout/webhook/delivery
pnpm test:oauth # real GitHub/Google login using signed-in Helium on :9222
pnpm checkDocker must be running. Test commands build the app and use a disposable PostgreSQL database, removed on exit. Run them sequentially because they share build output. Keep ports 3108/3210/3211 free for routine tests and 3000 for live/OAuth tests (XPORT_TEST_PORT overrides it).
The routine suite covers exports/downloads, stop/reload/recovery, billing, access controls, OAuth callbacks, CLI flows, and public UI. It uses real app routes and PostgreSQL with mocked providers. GitHub Actions runs this suite on pushes/PRs; browser coverage is Chromium, including a mobile viewport.
web/.env.local with POLAR_ENV=sandbox. Disposable customers use aliases from XPORT_TEST_EMAIL or the first local database user's email; personal credits are not spent. Tests cover real exports and checkout/webhook/delivery, capped at 50 X API calls. Full exports use @frixaco; stop/reload checks save at most two pages. Test customers are deleted; sandbox orders remain.web/.env.local. Use signed-in Helium on http://127.0.0.1:9222 (XPORT_BROWSER_CDP overrides it), with callbacks registered at http://localhost:3000/api/auth/callback/github and /google. Update callbacks if changing the port. Tests restore localhost auth cookies afterward; avoid another localhost Better Auth app during the run. Expired sessions or MFA may need interaction.Reports are in web/playwright-report/; failure traces/screenshots are in web/test-results/. Artifacts may contain private data and are ignored by Git. OAuth capture is disabled for the personal browser. Live and OAuth tests run explicitly, outside CI.
For a focused billing-only run against a manually provisioned local *_test database:
DATABASE_URL=postgresql://<user>:<password>@127.0.0.1:5432/xport_billing_test pnpm --filter web test:billingThe schema adds user.credit_balance and xport_credit_transactions. A ledger row records each grant/debit and its optional Polar delivery payload. Nitro runs billing-delivery every minute; failed or interrupted deliveries retry using the same operation key. pnpm --filter web credits:deliver also runs delivery manually. Neither path changes local balances during delivery.
Direct article, thread, user-info, and user-tweets APIs now require POST and an Idempotency-Key header (1–128 letters, digits, _, or -). Query parameters are unchanged. Reuse the key only when retrying the same request. The ledger retains the paid direct response so a lost response can be replayed without another fetch or debit. Browser and CLI article callers generate a fresh key per export; existing CLI releases using GET must be updated alongside the server.
Prepare and test the release first. This is a single maintenance-window cutover, not parallel Polar/local spending. All script commands require DATABASE_URL in the shell; snapshot and delivery additionally use the configured Polar token/environment. Node scripts do not automatically load .env.local (use Node's --env-file locally if needed).
pnpm db:migrate, then start the new release with BILLING_MAINTENANCE=true. Keep ingress maintenance active. Do not expose a zero-balance cutover before import.BILLING_MAINTENANCE=true in the script environment, run pnpm --filter web credits:migrate snapshot /secure/path/credits.json. Snapshot collection is read-only and refuses to overwrite an existing file. It fails on unmapped customers instead of guessing by email.polarCustomerId: null and a reviewed opening balance). Wait for in-flight checkouts and Polar meter changes to settle. includedOrderIds must list exactly the paid purchases represented in originalBalance; late purchases excluded from the balance must be removed from this list so their webhook can grant them. Mark each reviewed row confirmed: true. Reconcile any negative Polar balance separately: importing zero locally does not repair it externally.pnpm --filter web credits:migrate import /secure/path/credits.json. The import is atomic and replay-safe; it rejects an account with other local ledger activity. Original balances and included order IDs stay in opening-entry metadata, and imports never queue Polar grants.web/scripts/reconcile-credits.sql, verify the local balances, then disable maintenance and replay outstanding purchase webhooks. Imported purchase IDs and historical signup grants are skipped. Check pending deliveries and Railway logs after reopening ingress.For a manual positive adjustment, use a stable operation key and reuse it on retries:
pnpm --filter web credits:grant user@example.com 50 "Support adjustment" --key support-ticket-123Do not edit balances directly or send standalone negative Polar usage events. Reporting behavior follows Polar's event ingestion API; purchases use order.paid.