wayBetter for X

This commit is contained in:
CRTkafa 2026-10-01 06:01:13 +07:00
commit df5dff619e
720 changed files with 147465 additions and 0 deletions

View file

@ -0,0 +1,292 @@
// Fixes for docs/X-BUG-BACKLOG.md: "See new posts" jump, silent media upload failures, Following order.
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { chronologicalVariables, installFollowingChronological, rewriteFollowingBody, rewriteFollowingUrl } from '../src/fixes/followingOrder.ts';
import {
MAX_UPLOAD_RETRIES, classifyUpload, installUploadRetry, parseUpload, processingFailure, shouldRetryUpload, uploadFailureMessage, uploadRetryDelay,
} from '../src/fixes/uploadRetry.ts';
import { isNewPostsShortcut, restoreStep } from '../src/fixes/timeline.ts';
const BASE = 'https://x.com/home';
const LATEST = '/i/api/graphql/OQPHTgwczzp9RMAPt6BH9A/HomeLatestTimeline';
const UPLOAD = 'https://upload.x.com/i/media/upload.json';
// ---------- fake XHR (records what reaches the wire, replies on demand) ----------
type Reply = { status: number } | 'error' | 'timeout';
function xhrEnv() {
const wire: { method: string; url: string; body: unknown; headers: [string, string][]; xhr: FakeXHR }[] = [];
class FakeXHR extends EventTarget {
#s = { readyState: 0, status: 0, text: '', method: '', url: '', headers: [] as [string, string][] };
responseType: XMLHttpRequestResponseType = '';
timeout = 0;
withCredentials = false;
get readyState() { return this.#s.readyState; }
get status() { return this.#s.status; }
get responseText() { return this.#s.text; }
open(method: string, url: string) {
Object.assign(this.#s, { method, url, readyState: 1, status: 0, text: '', headers: [] });
this.dispatchEvent(new Event('readystatechange'));
}
setRequestHeader(name: string, value: string) { this.#s.headers.push([name, value]); }
send(body: unknown) { wire.push({ method: this.#s.method, url: this.#s.url, body, headers: [...this.#s.headers], xhr: this }); }
reply(reply: Reply, text = '{}') {
const failed = typeof reply === 'string';
Object.assign(this.#s, { readyState: 4, status: failed ? 0 : reply.status, text: failed ? '' : text });
for (const type of ['readystatechange', failed ? reply : 'load', 'loadend']) this.dispatchEvent(new Event(type));
}
abort() {
if (this.#s.readyState === 4 || this.#s.readyState === 0) return;
Object.assign(this.#s, { readyState: 4, status: 0 });
for (const type of ['readystatechange', 'abort', 'loadend']) this.dispatchEvent(new Event(type));
this.#s.readyState = 0;
}
}
const timers: (() => void)[] = [];
const delays: number[] = [];
const win = {
fetch: (async () => new Response('{}')) as typeof fetch,
location: { href: BASE },
XMLHttpRequest: FakeXHR as unknown as typeof XMLHttpRequest,
setTimeout: (run: () => void, ms: number) => { delays.push(ms); timers.push(run); return timers.length; },
clearTimeout: (id: number) => { timers[id - 1] = () => {}; },
};
const runTimers = () => { while (timers.some(Boolean)) { const index = timers.findIndex(Boolean); const run = timers[index]!; timers[index] = undefined as never; run(); } };
return { win, wire, delays, runTimers, FakeXHR };
}
/** X's uploader: open, on* handlers after open, headers, send; records what X's handlers saw. */
function xUpload(win: { XMLHttpRequest: typeof XMLHttpRequest }, command: string, extra = '', body: unknown = 'chunk') {
const xhr = new win.XMLHttpRequest();
const seen: string[] = [];
xhr.open(command === 'STATUS' ? 'GET' : 'POST', `${UPLOAD}?command=${command}&media_id=77${extra}`, true);
for (const type of ['load', 'error', 'timeout', 'abort', 'loadend']) xhr.addEventListener(type, () => seen.push(type === 'load' ? `load ${xhr.status}` : type));
xhr.addEventListener('readystatechange', () => { if (xhr.readyState === 4) seen.push('rs4'); });
xhr.setRequestHeader('authorization', 'Bearer test');
xhr.setRequestHeader('x-csrf-token', 'csrf');
xhr.send(body as XMLHttpRequestBodyInit);
return { xhr, seen };
}
// ---------- Following in time order ----------
test('chronologicalVariables turns only enableRanking: true off', () => {
assert.deepEqual(chronologicalVariables({ count: 40, enableRanking: true, seenTweetIds: ['1'] }), { count: 40, enableRanking: false, seenTweetIds: ['1'] });
for (const value of [{ enableRanking: false }, { count: 20 }, { enableRanking: 'true' }, null, [], 'x']) {
assert.equal(chronologicalVariables(value), null, JSON.stringify(value));
}
});
test('Following rewrite: GET query and POST body of HomeLatestTimeline, nothing else', () => {
const get = `${LATEST}?variables=${encodeURIComponent(JSON.stringify({ count: 20, enableRanking: true, requestContext: 'launch' }))}&features=%7B%7D`;
const next = rewriteFollowingUrl(get, BASE)!;
assert.ok(next.startsWith(LATEST), 'relative stays relative');
const params = new URL(next, BASE).searchParams;
assert.deepEqual(JSON.parse(params.get('variables')!), { count: 20, enableRanking: false, requestContext: 'launch' });
assert.equal(params.get('features'), '{}', 'other parameters kept');
assert.match(rewriteFollowingUrl(`https://x.com${get}`, BASE)!, /^https:\/\/x\.com\//);
const body = JSON.stringify({ variables: { count: 40, cursor: 'c', enableRanking: true, seenTweetIds: ['9'] }, features: { a: true }, queryId: 'q' });
assert.deepEqual(JSON.parse(rewriteFollowingBody(`https://x.com${LATEST}`, BASE, body)!),
{ variables: { count: 40, cursor: 'c', enableRanking: false, seenTweetIds: ['9'] }, features: { a: true }, queryId: 'q' });
const recent = JSON.stringify({ variables: { enableRanking: false } });
assert.equal(rewriteFollowingBody(LATEST, BASE, recent), null, 'X\'s own "Recent" sort untouched');
assert.equal(rewriteFollowingBody('/i/api/graphql/q/HomeTimeline', BASE, body), null, 'For you untouched');
assert.equal(rewriteFollowingBody(`https://api.example${LATEST}`, BASE, body), null, 'other origins untouched');
assert.equal(rewriteFollowingBody(LATEST, BASE, '{bad'), null);
assert.equal(rewriteFollowingBody(LATEST, BASE, new Blob([body])), null);
assert.equal(rewriteFollowingUrl(`${LATEST}?variables=%7Bbad`, BASE), null);
assert.equal(rewriteFollowingUrl('/i/api/graphql/q/HomeTimeline?variables=%7B%22enableRanking%22%3Atrue%7D', BASE), null);
});
test('Following rewrite on X\'s XHR: POST body and GET URL rewritten, others untouched, dispose restores', () => {
const env = xhrEnv();
const nativeOpen = env.FakeXHR.prototype.open;
const nativeSend = env.FakeXHR.prototype.send;
const handle = installFollowingChronological(env.win);
const post = new env.win.XMLHttpRequest();
post.open('POST', `https://x.com${LATEST}`);
post.send(JSON.stringify({ variables: { enableRanking: true, count: 40 }, queryId: 'q' }));
assert.equal(JSON.parse(String(env.wire[0]!.body)).variables.enableRanking, false);
assert.equal(env.wire[0]!.xhr, post, 'sent on X\'s own object');
const get = new env.win.XMLHttpRequest();
get.open('GET', `https://x.com${LATEST}?variables=${encodeURIComponent('{"enableRanking":true}')}`);
get.send(null);
assert.deepEqual(JSON.parse(new URL(env.wire[1]!.url).searchParams.get('variables')!), { enableRanking: false });
const other = new env.win.XMLHttpRequest();
const otherBody = JSON.stringify({ variables: { enableRanking: true } });
other.open('POST', 'https://x.com/i/api/graphql/q/HomeTimeline');
other.send(otherBody);
assert.equal(env.wire[2]!.body, otherBody);
handle.dispose();
assert.equal(env.FakeXHR.prototype.open, nativeOpen);
assert.equal(env.FakeXHR.prototype.send, nativeSend);
});
// ---------- media upload retry ----------
test('parseUpload: X\'s chunked upload commands on upload.x.com only', () => {
assert.deepEqual(parseUpload(`${UPLOAD}?command=APPEND&media_id=77&segment_index=3`), { command: 'APPEND', mediaId: '77', segment: '3' });
assert.deepEqual(parseUpload('https://upload.twitter.com/i/media/upload.json?command=STATUS&media_id=77'), { command: 'STATUS', mediaId: '77', segment: null });
assert.equal(parseUpload(`${UPLOAD}?command=INIT&total_bytes=1`)?.command, 'INIT');
for (const url of [
`${UPLOAD}?command=DELETE`, UPLOAD, 'https://evil.example/i/media/upload.json?command=APPEND',
'http://upload.x.com/i/media/upload.json?command=APPEND', 'https://x.com/i/api/graphql/q/CreateTweet', 'not a url',
]) assert.equal(parseUpload(url), null, url);
});
test('retry policy: only APPEND/STATUS, only failures X gives up on, at most 3 times with backoff', () => {
const append = parseUpload(`${UPLOAD}?command=APPEND&media_id=1&segment_index=0`)!;
const status = parseUpload(`${UPLOAD}?command=STATUS&media_id=1`)!;
const init = parseUpload(`${UPLOAD}?command=INIT`)!;
const finalize = parseUpload(`${UPLOAD}?command=FINALIZE&media_id=1`)!;
for (const outcome of [{ kind: 'error' }, { kind: 'timeout' }, { kind: 'load', status: 503 }, { kind: 'load', status: 0 }] as const) {
assert.equal(classifyUpload(outcome), 'fatal');
assert.equal(shouldRetryUpload(append, outcome, 0), true);
assert.equal(shouldRetryUpload(status, outcome, 2), true);
assert.equal(shouldRetryUpload(append, outcome, MAX_UPLOAD_RETRIES), false, 'bounded');
assert.equal(shouldRetryUpload(init, outcome, 0), false, 'INIT never repeated');
assert.equal(shouldRetryUpload(finalize, outcome, 0), false, 'FINALIZE never repeated');
}
for (const outcome of [{ kind: 'load', status: 200 }, { kind: 'load', status: 204 }, { kind: 'load', status: 400 }, { kind: 'load', status: 500 }, { kind: 'abort' }] as const) {
assert.equal(shouldRetryUpload(append, outcome, 0), false, JSON.stringify(outcome));
}
assert.equal(classifyUpload({ kind: 'load', status: 500 }), 'failed', 'X retries this one itself');
assert.equal(classifyUpload({ kind: 'abort' }), 'aborted');
assert.deepEqual([0, 1, 2].map(uploadRetryDelay), [1000, 2000, 4000]);
assert.equal(MAX_UPLOAD_RETRIES, 3);
});
test('processingFailure and failure messages', () => {
assert.match(processingFailure('{"media_id":1,"processing_info":{"state":"failed","error":{"code":1,"name":"InvalidMedia","message":"Unsupported video format"}}}')!, /Unsupported video format/);
assert.match(processingFailure('{"processing_info":{"state":"failed"}}')!, /could not process/);
for (const text of ['{"processing_info":{"state":"succeeded"}}', '{"processing_info":{"state":"in_progress"}}', '{}', '{bad', 'null']) assert.equal(processingFailure(text), null, text);
const append = parseUpload(`${UPLOAD}?command=APPEND&media_id=1&segment_index=0`)!;
assert.match(uploadFailureMessage(append, { kind: 'load', status: 503 }, 3), /APPEND, HTTP 503 after 3 retries/);
assert.match(uploadFailureMessage(append, { kind: 'timeout' }, 0), /APPEND, timed out\)/);
assert.match(uploadFailureMessage(append, { kind: 'error' }, 1), /network error after 1 retry/);
});
test('upload retry: a failed APPEND is re-sent on X\'s own XHR; X only sees the final success', () => {
const env = xhrEnv();
const notes: string[] = [];
const handle = installUploadRetry(env.win, message => notes.push(message));
const form = { chunk: 'bytes' };
const { xhr, seen } = xUpload(env.win, 'APPEND', '&segment_index=2', form);
assert.equal(env.wire.length, 1);
(env.wire[0]!.xhr).reply('error');
assert.deepEqual(seen, [], 'failed attempt hidden from X');
env.runTimers();
assert.equal(env.wire.length, 2, 'one retry');
(env.wire[1]!.xhr).reply({ status: 503 });
env.runTimers();
(env.wire[2]!.xhr).reply('timeout');
env.runTimers();
(env.wire[3]!.xhr).reply({ status: 204 }, '');
assert.deepEqual(env.delays, [1000, 2000, 4000]);
for (const sent of env.wire) {
assert.equal(sent.xhr, xhr, 'same XHR object');
assert.equal(sent.body, form, 'same body');
assert.equal(sent.url, env.wire[0]!.url, 'same request');
assert.deepEqual(sent.headers, [['authorization', 'Bearer test'], ['x-csrf-token', 'csrf']], 'headers re-applied once');
}
assert.deepEqual(seen, ['rs4', 'load 204', 'loadend']);
assert.deepEqual(notes, []);
handle.dispose();
});
test('upload retry: gives up after 3 retries, X sees the failure and a toast explains it', () => {
const env = xhrEnv();
const notes: string[] = [];
installUploadRetry(env.win, message => notes.push(message));
const { seen } = xUpload(env.win, 'STATUS');
for (let attempt = 0; attempt <= MAX_UPLOAD_RETRIES; attempt++) {
env.wire[attempt]!.xhr.reply({ status: 503 });
env.runTimers();
}
assert.equal(env.wire.length, MAX_UPLOAD_RETRIES + 1);
assert.deepEqual(seen, ['rs4', 'load 503', 'loadend']);
assert.equal(notes.length, 1);
assert.match(notes[0]!, /STATUS, HTTP 503 after 3 retries/);
});
test('upload retry: INIT/FINALIZE and ordinary HTTP errors pass straight through', () => {
const env = xhrEnv();
const notes: string[] = [];
installUploadRetry(env.win, message => notes.push(message));
const init = xUpload(env.win, 'INIT');
env.wire[0]!.xhr.reply('error');
env.runTimers();
assert.equal(env.wire.length, 1, 'INIT not repeated');
assert.deepEqual(init.seen, ['rs4', 'error', 'loadend']);
assert.match(notes[0]!, /INIT, network error\)/);
const bad = xUpload(env.win, 'APPEND', '&segment_index=0');
env.wire[1]!.xhr.reply({ status: 400 }, '{"errors":[]}');
env.runTimers();
assert.equal(env.wire.length, 2, 'X\'s own retry policy handles this one');
assert.deepEqual(bad.seen, ['rs4', 'load 400', 'loadend']);
assert.equal(notes.length, 1);
const finalize = xUpload(env.win, 'FINALIZE');
env.wire[2]!.xhr.reply({ status: 200 }, '{"processing_info":{"state":"failed","error":{"message":"Video too long"}}}');
assert.deepEqual(finalize.seen, ['rs4', 'load 200', 'loadend']);
assert.match(notes[1]!, /Video too long/);
});
test('upload retry: X cancelling during the backoff gets abort/loadend and nothing is re-sent', () => {
const env = xhrEnv();
const notes: string[] = [];
installUploadRetry(env.win, message => notes.push(message));
const { xhr, seen } = xUpload(env.win, 'APPEND', '&segment_index=0');
env.wire[0]!.xhr.reply('error');
xhr.abort();
env.runTimers();
assert.equal(env.wire.length, 1);
assert.deepEqual(seen, ['abort', 'loadend']);
assert.deepEqual(notes, []);
const second = xUpload(env.win, 'APPEND', '&segment_index=1');
second.xhr.abort();
env.runTimers();
assert.equal(env.wire.length, 2);
assert.deepEqual(second.seen, ['rs4', 'abort', 'loadend'], 'in-flight abort is native');
});
test('upload retry: non-upload XHRs are untouched and dispose unhooks', () => {
const env = xhrEnv();
const nativeSend = env.FakeXHR.prototype.send;
const handle = installUploadRetry(env.win, () => {});
const xhr = new env.win.XMLHttpRequest();
xhr.open('POST', 'https://x.com/i/api/graphql/q/CreateTweet');
xhr.send('{}');
assert.equal(Object.getOwnPropertyNames(xhr).includes('abort'), false, 'no per-object overrides');
handle.dispose();
assert.equal(env.FakeXHR.prototype.send, nativeSend);
});
// ---------- "See new posts" ----------
test('"." shortcut counts only without modifiers and outside text fields', () => {
const none = { ctrl: false, alt: false, meta: false };
assert.equal(isNewPostsShortcut('.', none, false), true);
assert.equal(isNewPostsShortcut('.', none, true), false);
assert.equal(isNewPostsShortcut('.', { ...none, ctrl: true }, false), false);
assert.equal(isNewPostsShortcut(',', none, false), false);
});
test('pill restore: X jumped to the top, the post is brought back, then held while posts insert above', () => {
const state = { fallback: true, jumped: false, stable: 0 };
// Right after X's scrollTo(0, 0): the post is still rendered, 3000px lower than before.
assert.deepEqual(restoreStep(state, { top: 3120, targetTop: 120, scrollY: 0, targetScrollY: 3000, viewport: 800 }), { kind: 'scrollBy', dy: 3000 });
// Virtualized away: jump near the old offset once, then look again.
assert.deepEqual(restoreStep(state, { top: null, targetTop: 120, scrollY: 0, targetScrollY: 3000, viewport: 800 }), { kind: 'scrollTo', y: 3000 });
// New posts inserted above push it down by their height: compensated.
assert.deepEqual(restoreStep({ ...state, jumped: true }, { top: 1020, targetTop: 120, scrollY: 3000, targetScrollY: 3000, viewport: 800 }), { kind: 'scrollBy', dy: 900 });
assert.deepEqual(restoreStep({ ...state, stable: 2 }, { top: 121, targetTop: 120, scrollY: 3900, targetScrollY: 3000, viewport: 800 }), { kind: 'done' });
});