Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9f8960a
refactor(zip): share entry stream, first-entry and output-zip helpers
sunnylqm Oct 12, 2026
5054090
refactor(app-info-parser): parse only what the CLI reads
sunnylqm Oct 12, 2026
834155e
refactor: trim the registry version check, drop the properties depend…
sunnylqm Oct 12, 2026
b6e7233
refactor: remove test-only helpers and dead branches
sunnylqm Oct 12, 2026
0e49211
refactor: one sha256 helper and one HBC header reader
sunnylqm Oct 12, 2026
2959a5f
refactor: one bulk delete for versions and packages
sunnylqm Oct 12, 2026
21dc226
test(e2e): read the upload body before answering in the mock store
sunnylqm Oct 12, 2026
7d534ad
refactor: one paged version search and one pager
sunnylqm Oct 12, 2026
98a749b
refactor(sentry): probe sentry-cli once, resolve the explicit release…
sunnylqm Oct 12, 2026
aafcc6a
refactor: one truthy-env check and one PackageManager type
sunnylqm Oct 12, 2026
c028acb
refactor(package): use the shared option parsers, inline buildTime St…
sunnylqm Oct 12, 2026
779f07e
refactor(versions): one non-interactive check, one version id prompt
sunnylqm Oct 12, 2026
30e4db7
refactor: simpler deps diff, one update.json reader
sunnylqm Oct 12, 2026
eaf285b
refactor(api): one query factory, one proxy-aware network error
sunnylqm Oct 12, 2026
0a6ae2f
refactor(diff): one stream-threshold check, one entry materializer
sunnylqm Oct 12, 2026
fbaa50d
refactor(bundle): one publish-and-upload step
sunnylqm Oct 12, 2026
cde94f1
refactor(dep-versions): drop the depVersions Proxy
sunnylqm Oct 12, 2026
3048b3e
refactor(zip-range): one local-header parser, one location check
sunnylqm Oct 12, 2026
aa4e611
refactor: small simplifications across utils
sunnylqm Oct 12, 2026
2af013b
fix(zip): handle writePromise rejection from the start
sunnylqm Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
"homepage": "https://github.com/reactnativecn/react-native-update-cli",
"dependencies": {
"bplist-parser": "^0.3.2",
"cgbi-to-png": "^1.0.7",
"chalk": "4",
"cli-arguments": "^0.2.1",
"compare-versions": "^6.1.1",
Expand All @@ -73,7 +72,6 @@
"i18next": "^26.4.0",
"plist": "^5.0.0",
"progress": "^2.0.3",
"properties": "^1.2.1",
"protobufjs": "^8.7.2",
"read": "^4.1.0",
"registry-auth-token": "^5.1.1",
Expand Down
8 changes: 1 addition & 7 deletions scripts/prepublish.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bun

import { access, readFile, writeFile } from 'node:fs/promises';
import { readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { $ } from 'bun';

Expand All @@ -11,12 +11,6 @@ async function modifyPackageJson({
}): Promise<void> {
const packageJsonPath = path.join(__dirname, '..', 'package.json');

try {
await access(packageJsonPath);
} catch {
throw new Error(`package.json not found at ${packageJsonPath}`);
}

console.log('Reading package.json...');
const packageJsonContent = await readFile(packageJsonPath, 'utf-8');
const packageJson = JSON.parse(packageJsonContent);
Expand Down
8 changes: 4 additions & 4 deletions src/account.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import chalk from 'chalk';
import { fork } from 'child_process';
import { createHash } from 'crypto';
import fs from 'fs';
import os from 'os';
import path from 'path';
import { getApiToken, getSession } from './api';
import { IS_CRESC, pricingPageUrl } from './utils/constants';
import { sha256Hex } from './utils/hash';
import { t } from './utils/i18n';

export interface AccountInfo {
Expand Down Expand Up @@ -62,9 +62,9 @@ interface AccountCache {
}

export function accountCacheFile(token: string, apiToken = '') {
const key = createHash('sha256')
.update(JSON.stringify([IS_CRESC, process.cwd(), token, apiToken]))
.digest('hex');
const key = sha256Hex(
JSON.stringify([IS_CRESC, process.cwd(), token, apiToken]),
);
return path.join(
os.homedir(),
'.cache',
Expand Down
84 changes: 30 additions & 54 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,21 +165,26 @@ function isProxyRelatedError(error: unknown): boolean {
return PROXY_ERROR_PATTERNS.some((p) => lower.includes(p.toLowerCase()));
}

/** A request error for `url`, with the proxy hints when a proxy is the likely cause. */
function networkError(error: unknown, url: string): Error {
const baseError = createRequestError(error, url);
if (isProxyRelatedError(error)) {
return new Error(
`${baseError.message}\n\n${t('proxyNetworkError')}\n${t('proxyNetworkErrorTips')}`,
{ cause: baseError },
);
}
return baseError;
}

async function query(url: string, options: RuntimeRequestInit) {
const baseUrl = await getBaseUrl();
const fullUrl = `${baseUrl}${url}`;
let resp: RuntimeResponse;
try {
resp = await runtimeFetch(fullUrl, options);
} catch (error) {
const baseError = createRequestError(error, fullUrl);
if (isProxyRelatedError(error)) {
throw new Error(
`${baseError.message}\n\n${t('proxyNetworkError')}\n${t('proxyNetworkErrorTips')}`,
{ cause: baseError },
);
}
throw baseError;
throw networkError(error, fullUrl);
}
const text = await resp.text();
let json: any;
Expand Down Expand Up @@ -207,29 +212,12 @@ async function query(url: string, options: RuntimeRequestInit) {
return json;
}

function queryWithoutBody(method: string) {
return (api: string) => {
const headers: Record<string, string> = {
'User-Agent': userAgent,
};
if (apiToken) {
headers['x-api-token'] = apiToken;
} else if (session?.token) {
headers['X-AccessToken'] = session.token;
}
return query(api, {
method,
headers,
});
};
}

function queryWithBody(method: string) {
function queryWith(method: string, withBody: boolean) {
return (api: string, body?: Record<string, any>) => {
const headers: Record<string, string> = {
'User-Agent': userAgent,
'Content-Type': 'application/json',
};
const headers: Record<string, string> = { 'User-Agent': userAgent };
if (withBody) {
headers['Content-Type'] = 'application/json';
}
if (apiToken) {
headers['x-api-token'] = apiToken;
} else if (session?.token) {
Expand All @@ -238,15 +226,15 @@ function queryWithBody(method: string) {
return query(api, {
method,
headers,
body: JSON.stringify(body),
...(withBody ? { body: JSON.stringify(body) } : {}),
});
};
}

export const get = queryWithoutBody('GET');
export const post = queryWithBody('POST');
export const put = queryWithBody('PUT');
export const doDelete = queryWithBody('DELETE');
export const get: (api: string) => Promise<any> = queryWith('GET', false);
export const post = queryWith('POST', true);
export const put = queryWith('PUT', true);
export const doDelete = queryWith('DELETE', true);

// Upload deadline: generous for a slow link (5 s/MB, i.e. ~200 KB/s, on top
// of a 30 s base, never below 60 s) but bounded, so a stalled connection cannot
Expand Down Expand Up @@ -428,19 +416,16 @@ async function sendChunks(
}
}

export async function uploadFile(
fn: string,
key?: string,
appId?: string | number,
) {
export async function uploadFile(fn: string, appId?: string | number) {
const fileSize = fs.statSync(fn).size;
// appId 用于服务端路由:绑定了自托管节点(rnu-node)的应用,
// 上传指令会指向节点或其对象存储。chunked + size 让支持的服务端(GCS)
// 改发分片并行上传的指令;不认识这两个字段的服务端照旧整文件上传
const resp = await post('/upload', {
ext: path.extname(fn),
...(appId ? { appId: Number(appId) } : {}),
...(key ? {} : { chunked: true, size: fileSize }),
chunked: true,
size: fileSize,
});
const { url, backupUrl, formData, maxSize } = resp;
let realUrl = url;
Expand All @@ -459,7 +444,6 @@ export async function uploadFile(
realUrl = backupUrl;
}
}
// console.log({realUrl});
}

if (maxSize && fileSize > filesizeParser(maxSize)) {
Expand All @@ -484,14 +468,7 @@ export async function uploadFile(
});

const rethrowUploadError = (error: unknown): never => {
const baseError = createRequestError(error, realUrl);
if (isProxyRelatedError(error)) {
throw new Error(
`${baseError.message}\n\n${t('proxyNetworkError')}\n${t('proxyNetworkErrorTips')}`,
{ cause: baseError },
);
}
throw baseError;
throw networkError(error, realUrl);
};

// 自托管节点的 s3 直传:服务端下发预签名 PUT,字节直达用户的对象存储
Expand Down Expand Up @@ -528,7 +505,7 @@ export async function uploadFile(
stream: fileStream,
});

if (resp.chunked && !key) {
if (resp.chunked) {
try {
await sendChunks(fn, realUrl, fileSize, bar, resp.chunked, postForm);
} catch (error) {
Expand All @@ -551,7 +528,7 @@ export async function uploadFile(
realUrl,
fileSize,
bar,
postForm(key ? { ...formData, key } : formData, fileSize),
postForm(formData, fileSize),
);
} catch (error) {
return rethrowUploadError(error);
Expand All @@ -564,8 +541,7 @@ export async function uploadFile(
);
}

// const body = await response.json();
return { hash: key || formData.key };
return { hash: formData.key };
}

/**
Expand Down
Loading
Loading