- API error bodies on one line (errorBody): Google and App Store Connect pretty-print errors, which hid SERVICE_DISABLED from the Play account check (reported permission instead of api-disabled), broke tables and turned serve's apps.list error into a JSON fragment - apps list: failure says the reason and the fix (same words as apps check) - newest App Store version by createdDate, iOS first (the API groups versions by platform); all versions read so no platform is missed; per-app reads four at a time - apps check (apple): team app count from meta.paging.total with a one-app read - Play Money without units (prices under 1.00) read as 0.xx, not NaN (catalog diff, subscription diffs) - health: App Store Connect 404 for perfPowerMetrics / diagnosticSignatures means no field data yet; Play vitals behind a disabled Reporting API say so with the fix - validate: gracRatingClassificationNumber optional, socialMediaAgeRestricted only when socialMedia is yes, socialMedia unanswered is a warning - tests with sanitized recorded responses (tests/store-real-shapes.test.ts)
25 lines
1.2 KiB
TypeScript
25 lines
1.2 KiB
TypeScript
// Plain URL reachability check for public pages (privacy policy, app-ads.txt). No credentials are ever sent.
|
|
import type { UrlCheck } from './validate-rules';
|
|
|
|
export async function checkUrl(url: string, o: { body?: boolean; timeoutMs?: number } = {}): Promise<UrlCheck> {
|
|
try {
|
|
const r = await fetch(url, { redirect: 'follow', signal: AbortSignal.timeout(o.timeoutMs ?? 15_000), headers: { 'User-Agent': 'storebound-validate' } });
|
|
const body = o.body ? (await r.text()).slice(0, 200_000) : undefined;
|
|
if (!o.body) await r.body?.cancel();
|
|
return { url, status: r.status, body };
|
|
} catch (e) {
|
|
return { url, status: null, error: (e as Error).message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An API error body for an Error message: JSON on one line (App Store Connect and Google pretty-print their errors,
|
|
* which broke tables, `lastLine` in serve and matching on the error reason), an HTML page named as such, else the text
|
|
* with its whitespace collapsed. At most `max` characters.
|
|
*/
|
|
export function errorBody(text: string, max = 1500): string {
|
|
if (/^\s*</.test(text)) return '(HTML error page)';
|
|
let s: string;
|
|
try { s = JSON.stringify(JSON.parse(text)); } catch { s = text.replace(/\s+/g, ' ').trim(); }
|
|
return s.length > max ? s.slice(0, max) + '…' : s;
|
|
}
|