shipup 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/LICENSE +14 -0
- package/README.md +112 -0
- package/README.zh-CN.md +105 -0
- package/SECURITY.md +32 -0
- package/creds.example.yaml +62 -0
- package/dist/adapters/android/honor/api.js +360 -0
- package/dist/adapters/android/honor/api.js.map +1 -0
- package/dist/adapters/android/honor/index.js +122 -0
- package/dist/adapters/android/honor/index.js.map +1 -0
- package/dist/adapters/android/huawei/api.js +389 -0
- package/dist/adapters/android/huawei/api.js.map +1 -0
- package/dist/adapters/android/huawei/index.js +128 -0
- package/dist/adapters/android/huawei/index.js.map +1 -0
- package/dist/adapters/android/meizu/api.js +367 -0
- package/dist/adapters/android/meizu/api.js.map +1 -0
- package/dist/adapters/android/meizu/index.js +129 -0
- package/dist/adapters/android/meizu/index.js.map +1 -0
- package/dist/adapters/android/oppo/api.js +397 -0
- package/dist/adapters/android/oppo/api.js.map +1 -0
- package/dist/adapters/android/oppo/index.js +135 -0
- package/dist/adapters/android/oppo/index.js.map +1 -0
- package/dist/adapters/android/qq/api.js +184 -0
- package/dist/adapters/android/qq/api.js.map +1 -0
- package/dist/adapters/android/qq/index.js +80 -0
- package/dist/adapters/android/qq/index.js.map +1 -0
- package/dist/adapters/android/samsung/api.js +355 -0
- package/dist/adapters/android/samsung/api.js.map +1 -0
- package/dist/adapters/android/samsung/index.js +111 -0
- package/dist/adapters/android/samsung/index.js.map +1 -0
- package/dist/adapters/android/specs.js +106 -0
- package/dist/adapters/android/specs.js.map +1 -0
- package/dist/adapters/android/stub.js +27 -0
- package/dist/adapters/android/stub.js.map +1 -0
- package/dist/adapters/android/vivo/api.js +236 -0
- package/dist/adapters/android/vivo/api.js.map +1 -0
- package/dist/adapters/android/vivo/index.js +121 -0
- package/dist/adapters/android/vivo/index.js.map +1 -0
- package/dist/adapters/android/xiaomi/api.js +163 -0
- package/dist/adapters/android/xiaomi/api.js.map +1 -0
- package/dist/adapters/android/xiaomi/index.js +108 -0
- package/dist/adapters/android/xiaomi/index.js.map +1 -0
- package/dist/adapters/ios/appstore/asc.js +280 -0
- package/dist/adapters/ios/appstore/asc.js.map +1 -0
- package/dist/adapters/ios/appstore/index.js +323 -0
- package/dist/adapters/ios/appstore/index.js.map +1 -0
- package/dist/adapters/registry.js +34 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/util.js +26 -0
- package/dist/adapters/util.js.map +1 -0
- package/dist/cli/index.js +479 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.js +65 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/core/exit.js +20 -0
- package/dist/core/exit.js.map +1 -0
- package/dist/core/orchestrator.js +24 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/types.js +20 -0
- package/dist/core/types.js.map +1 -0
- package/dist/creds/load.js +68 -0
- package/dist/creds/load.js.map +1 -0
- package/dist/creds/schema.js +30 -0
- package/dist/creds/schema.js.map +1 -0
- package/dist/infra/crypto.js +12 -0
- package/dist/infra/crypto.js.map +1 -0
- package/dist/infra/http.js +87 -0
- package/dist/infra/http.js.map +1 -0
- package/dist/pkginfo/apk-icon.js +89 -0
- package/dist/pkginfo/apk-icon.js.map +1 -0
- package/dist/pkginfo/icon.js +83 -0
- package/dist/pkginfo/icon.js.map +1 -0
- package/dist/pkginfo/index.js +33 -0
- package/dist/pkginfo/index.js.map +1 -0
- package/docs/android-markets.md +190 -0
- package/docs/credentials.md +72 -0
- package/docs/providers.md +60 -0
- package/lib/common.mjs +265 -0
- package/lib/harmony.mjs +190 -0
- package/lib/huawei.mjs +153 -0
- package/lib/ios.mjs +477 -0
- package/package.json +70 -0
- package/shipup.mjs +160 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// 小米按 packageName 识别应用,无 app_id。鉴权方式:把 {password, sig:[...]} 这段 JSON
|
|
2
|
+
// 用开发者 RSA 公钥(PKCS#1 v1.5)整体加密、hex 编码后放入 multipart 字段 SIG;
|
|
3
|
+
// 服务端解密后校验 password 及各文件/字段的 md5(sig 数组里的 hash)。
|
|
4
|
+
// 流程:dev/query 取线上 appName 等信息 → dev/push(multipart:RequestData + SIG + apk + icon)即提交。
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { basename } from 'node:path';
|
|
7
|
+
import { createPublicKey, publicEncrypt, constants } from 'node:crypto';
|
|
8
|
+
import { md5Hex } from '../../../infra/crypto.js';
|
|
9
|
+
import { UploadError } from '../../../core/types.js';
|
|
10
|
+
const BASE = 'https://api.developer.xiaomi.com/devupload/dev';
|
|
11
|
+
// 其 base64url(JWK 的 e 字段)即 "AQAB"。
|
|
12
|
+
const RSA_EXPONENT_B64URL = Buffer.from([0x01, 0x00, 0x01]).toString('base64url');
|
|
13
|
+
// PKCS#1 v1.5 type2 加密填充开销固定 11 字节;故单块明文上限 = 模数字节数 - 11。
|
|
14
|
+
const PKCS1_PADDING_OVERHEAD = 11;
|
|
15
|
+
/** 把十六进制模数字符串规整为偶数长度的小写 hex。 */
|
|
16
|
+
function normalizeHex(hex) {
|
|
17
|
+
let h = hex.trim().toLowerCase().replace(/^0x/, '');
|
|
18
|
+
if (h.length % 2 !== 0)
|
|
19
|
+
h = `0${h}`;
|
|
20
|
+
return h;
|
|
21
|
+
}
|
|
22
|
+
/** 模数 hex → 大端无符号字节(去掉前导 0 字节,作为 JWK n / 计算密钥字节长度)。 */
|
|
23
|
+
function modulusBuffer(rsaModulusHex) {
|
|
24
|
+
const buf = Buffer.from(normalizeHex(rsaModulusHex), 'hex');
|
|
25
|
+
let i = 0;
|
|
26
|
+
while (i < buf.length - 1 && buf[i] === 0)
|
|
27
|
+
i++;
|
|
28
|
+
return i > 0 ? buf.subarray(i) : buf;
|
|
29
|
+
}
|
|
30
|
+
/** 用模数 hex + 指数 65537 构造 RSA 公钥(经 JWK 导入)。 */
|
|
31
|
+
function buildPublicKey(rsaModulusHex) {
|
|
32
|
+
const modBuf = modulusBuffer(rsaModulusHex);
|
|
33
|
+
if (modBuf.length === 0)
|
|
34
|
+
throw new UploadError('rsa', 'rsa_modulus 为空或非法');
|
|
35
|
+
const key = createPublicKey({
|
|
36
|
+
key: { kty: 'RSA', n: modBuf.toString('base64url'), e: RSA_EXPONENT_B64URL },
|
|
37
|
+
format: 'jwk',
|
|
38
|
+
});
|
|
39
|
+
return { key, keyBytes: modBuf.length };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 各密文块拼接后 hex 编码返回。
|
|
43
|
+
* 注意:PKCS#1 v1.5 type2 填充是随机的(Node 与 PointyCastle 皆然),密文每次不同但
|
|
44
|
+
* 语义等价——服务端解密得到同一明文,这是预期行为,不是 bug。
|
|
45
|
+
*/
|
|
46
|
+
export function rsaSignature(body, rsaModulusHex) {
|
|
47
|
+
const { key, keyBytes } = buildPublicKey(rsaModulusHex);
|
|
48
|
+
const inputBlockSize = keyBytes - PKCS1_PADDING_OVERHEAD;
|
|
49
|
+
if (inputBlockSize <= 0)
|
|
50
|
+
throw new UploadError('rsa', 'rsa_modulus 过短,无法 PKCS#1 加密');
|
|
51
|
+
const input = Buffer.from(body, 'utf8');
|
|
52
|
+
const blocks = [];
|
|
53
|
+
for (let off = 0; off < input.length; off += inputBlockSize) {
|
|
54
|
+
const chunk = input.subarray(off, Math.min(off + inputBlockSize, input.length));
|
|
55
|
+
const enc = publicEncrypt({ key, padding: constants.RSA_PKCS1_PADDING }, chunk);
|
|
56
|
+
blocks.push(enc);
|
|
57
|
+
}
|
|
58
|
+
return Buffer.concat(blocks).toString('hex');
|
|
59
|
+
}
|
|
60
|
+
/** 对 UTF-8 字符串计算小写 MD5 hex。 */
|
|
61
|
+
function md5OfString(s) {
|
|
62
|
+
return md5Hex(Buffer.from(s, 'utf8'));
|
|
63
|
+
}
|
|
64
|
+
/** 构造并 RSA 加密 SIG 字段:{password, sig:[{name,hash}...]}。 */
|
|
65
|
+
function buildSig(password, sig, rsaModulusHex) {
|
|
66
|
+
const sigBody = JSON.stringify({ password, sig });
|
|
67
|
+
return rsaSignature(sigBody, rsaModulusHex);
|
|
68
|
+
}
|
|
69
|
+
/** POST multipart,校验 http 状态与业务 result===0,返回解析后的 JSON。 */
|
|
70
|
+
async function postMultipart(url, form, timeoutMs) {
|
|
71
|
+
const res = await fetch(url, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
body: form,
|
|
74
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
75
|
+
});
|
|
76
|
+
const text = await res.text();
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
throw new UploadError(res.status, `http ${res.status}: ${text.slice(0, 500)}`);
|
|
79
|
+
}
|
|
80
|
+
let data;
|
|
81
|
+
try {
|
|
82
|
+
data = JSON.parse(text);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
throw new UploadError('parse', `非 JSON 响应: ${text.slice(0, 500)}`);
|
|
86
|
+
}
|
|
87
|
+
if (Number(data?.result) !== 0) {
|
|
88
|
+
throw new UploadError(data?.result ?? -1, String(data?.message ?? 'unknown'));
|
|
89
|
+
}
|
|
90
|
+
return data;
|
|
91
|
+
}
|
|
92
|
+
/** dev/query:查询应用线上信息(appName、线上版本等)。 */
|
|
93
|
+
export async function getAppInfo(args) {
|
|
94
|
+
// 注意:requestData 必须「序列化一次、字段与哈希共用同一字符串」,
|
|
95
|
+
// 否则 SIG 内的 md5 与实际 RequestData 字段不一致,服务端会校验失败。
|
|
96
|
+
const requestData = JSON.stringify({
|
|
97
|
+
userName: args.userName,
|
|
98
|
+
packageName: args.packageName,
|
|
99
|
+
});
|
|
100
|
+
const sig = buildSig(args.password, [{ name: 'RequestData', hash: md5OfString(requestData) }], args.rsaModulus);
|
|
101
|
+
const form = new FormData();
|
|
102
|
+
form.append('RequestData', requestData);
|
|
103
|
+
form.append('SIG', sig);
|
|
104
|
+
const data = await postMultipart(`${BASE}/query`, form, args.timeoutMs);
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* dev/push:上传 APK + 图标 + 版本信息并提交(小米 push 即提交,无独立提审步骤)。
|
|
109
|
+
*/
|
|
110
|
+
export async function uploadXiaomi(args) {
|
|
111
|
+
const appInfo = await getAppInfo({
|
|
112
|
+
userName: args.userName,
|
|
113
|
+
password: args.password,
|
|
114
|
+
rsaModulus: args.rsaModulus,
|
|
115
|
+
packageName: args.packageName,
|
|
116
|
+
timeoutMs: args.queryTimeoutMs,
|
|
117
|
+
});
|
|
118
|
+
// appName 必传:优先用调用方覆盖值,否则沿用线上值(保持现有行为)。
|
|
119
|
+
const onlineAppName = appInfo.packageInfo.appName;
|
|
120
|
+
// appInfo:brief/desc 按需生效——仅当有值才加入 RequestData,否则保持原字段集不变。
|
|
121
|
+
const appInfoBody = {
|
|
122
|
+
appName: args.appName ?? onlineAppName,
|
|
123
|
+
packageName: args.packageName,
|
|
124
|
+
updateDesc: args.updateDesc,
|
|
125
|
+
versionName: args.versionName,
|
|
126
|
+
privacyUrl: args.privacyUrl,
|
|
127
|
+
};
|
|
128
|
+
if (args.brief !== undefined)
|
|
129
|
+
appInfoBody.brief = args.brief;
|
|
130
|
+
if (args.desc !== undefined)
|
|
131
|
+
appInfoBody.desc = args.desc;
|
|
132
|
+
const body = {
|
|
133
|
+
userName: args.userName,
|
|
134
|
+
synchroType: 1,
|
|
135
|
+
appInfo: appInfoBody,
|
|
136
|
+
};
|
|
137
|
+
const requestData = JSON.stringify(body);
|
|
138
|
+
const apkBytes = await readFile(args.apkPath);
|
|
139
|
+
const apkMd5 = md5Hex(apkBytes);
|
|
140
|
+
const iconMd5 = md5Hex(args.iconBytes);
|
|
141
|
+
// SIG 的 md5 列表与 multipart 文件部分须严格一致:RequestData → apk → icon → screenshot_1..N。
|
|
142
|
+
const sigEntries = [
|
|
143
|
+
{ name: 'RequestData', hash: md5OfString(requestData) },
|
|
144
|
+
{ name: 'apk', hash: apkMd5 },
|
|
145
|
+
{ name: 'icon', hash: iconMd5 },
|
|
146
|
+
];
|
|
147
|
+
const screenshots = args.screenshots ?? [];
|
|
148
|
+
for (let i = 0; i < screenshots.length; i++) {
|
|
149
|
+
sigEntries.push({ name: `screenshot_${i + 1}`, hash: md5Hex(screenshots[i]) });
|
|
150
|
+
}
|
|
151
|
+
const sig = buildSig(args.password, sigEntries, args.rsaModulus);
|
|
152
|
+
const form = new FormData();
|
|
153
|
+
form.append('RequestData', requestData);
|
|
154
|
+
form.append('SIG', sig);
|
|
155
|
+
form.append('apk', new Blob([apkBytes], { type: 'application/octet-stream' }), basename(args.apkPath));
|
|
156
|
+
form.append('icon', new Blob([args.iconBytes], { type: 'image/png' }), args.iconFileName);
|
|
157
|
+
for (let i = 0; i < screenshots.length; i++) {
|
|
158
|
+
const field = `screenshot_${i + 1}`;
|
|
159
|
+
form.append(field, new Blob([screenshots[i]], { type: 'image/png' }), `${field}.png`);
|
|
160
|
+
}
|
|
161
|
+
await postMultipart(`${BASE}/push`, form, args.uploadTimeoutMs);
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../src/adapters/android/xiaomi/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { toFailed } from '../../util.js';
|
|
2
|
+
import { prepareScreenshot } from '../../../pkginfo/icon.js';
|
|
3
|
+
import { resolveIconBytes } from '../../../pkginfo/apk-icon.js';
|
|
4
|
+
import { getChannelSpec, warnIfTooLong } from '../specs.js';
|
|
5
|
+
import { getAppInfo, uploadXiaomi } from './api.js';
|
|
6
|
+
/**
|
|
7
|
+
* 小米 getAppInfo 返回的是【线上已审核通过】的版本,没有独立的「提交版本审核态」接口。
|
|
8
|
+
* - 目标版本(versionCode 优先,其次 versionName)已是线上版本 → approved;
|
|
9
|
+
* - 指定了目标版本但与线上不一致 → 仍在审核/排队 → pending_review;
|
|
10
|
+
* - 未指定目标版本 → 仅说明存在线上版本 → approved。
|
|
11
|
+
*/
|
|
12
|
+
function mapXiaomiStatus(ctx, onlineVersionName, onlineVersionCode) {
|
|
13
|
+
if (ctx.versionCode && onlineVersionCode === ctx.versionCode)
|
|
14
|
+
return 'approved';
|
|
15
|
+
if (ctx.versionName && onlineVersionName === ctx.versionName)
|
|
16
|
+
return 'approved';
|
|
17
|
+
if (ctx.versionCode || ctx.versionName)
|
|
18
|
+
return 'pending_review';
|
|
19
|
+
return 'approved';
|
|
20
|
+
}
|
|
21
|
+
/** 小米应用市场:dev/push 即提交(上传 + 提审一步完成),故 upload 始终为 upload+submit。 */
|
|
22
|
+
export class XiaomiAdapter {
|
|
23
|
+
channel = 'xiaomi';
|
|
24
|
+
async upload(ctx) {
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
try {
|
|
27
|
+
const spec = getChannelSpec('xiaomi');
|
|
28
|
+
// 小米 push 必传图标:优先 --icon,否则用 aapt 从 APK 自动提取(按 spec 缩放 512²、压到 ≤1MB)。
|
|
29
|
+
const iconBytes = await resolveIconBytes({
|
|
30
|
+
iconPath: ctx.iconPath,
|
|
31
|
+
apkPath: ctx.pkg,
|
|
32
|
+
size: spec.iconSize,
|
|
33
|
+
maxBytes: spec.iconMaxBytes,
|
|
34
|
+
});
|
|
35
|
+
// 文案按需更新:写前对一句话简介(brief)做长度告警(小米 ≤17 汉字,仅告警不阻断)。
|
|
36
|
+
warnIfTooLong('xiaomi', 'summary', ctx.summary, spec.summaryMax);
|
|
37
|
+
// 截图按需更新:仅当 ctx.screenshots 非空时,按 spec 处理为 png 并随表单上传;空=沿用线上。
|
|
38
|
+
let screenshots;
|
|
39
|
+
if (ctx.screenshots && ctx.screenshots.length > 0) {
|
|
40
|
+
screenshots = [];
|
|
41
|
+
for (const p of ctx.screenshots) {
|
|
42
|
+
screenshots.push(await prepareScreenshot(p, spec.screenshotFormat, spec.screenshotMaxBytes));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
await uploadXiaomi({
|
|
46
|
+
userName: ctx.creds.user_name,
|
|
47
|
+
password: ctx.creds.password,
|
|
48
|
+
rsaModulus: ctx.creds.rsa_modulus,
|
|
49
|
+
packageName: ctx.packageName,
|
|
50
|
+
versionName: ctx.versionName,
|
|
51
|
+
updateDesc: ctx.releaseNote ?? '',
|
|
52
|
+
privacyUrl: ctx.privacyUrl ?? '',
|
|
53
|
+
appName: ctx.appName,
|
|
54
|
+
brief: ctx.summary,
|
|
55
|
+
desc: ctx.description,
|
|
56
|
+
apkPath: ctx.pkg,
|
|
57
|
+
iconBytes,
|
|
58
|
+
iconFileName: 'icon.png',
|
|
59
|
+
screenshots,
|
|
60
|
+
queryTimeoutMs: ctx.timeoutMs,
|
|
61
|
+
uploadTimeoutMs: Math.max(ctx.timeoutMs, 900000),
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
channel: this.channel,
|
|
65
|
+
packageName: ctx.packageName,
|
|
66
|
+
versionName: ctx.versionName,
|
|
67
|
+
versionCode: ctx.versionCode,
|
|
68
|
+
action: 'upload+submit',
|
|
69
|
+
status: 'submitted',
|
|
70
|
+
errorCode: null,
|
|
71
|
+
message: '',
|
|
72
|
+
durationMs: Date.now() - start,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return toFailed(this.channel, e, start, 'upload+submit', ctx);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async queryStatus(ctx) {
|
|
80
|
+
const start = Date.now();
|
|
81
|
+
try {
|
|
82
|
+
const appInfo = await getAppInfo({
|
|
83
|
+
userName: ctx.creds.user_name,
|
|
84
|
+
password: ctx.creds.password,
|
|
85
|
+
rsaModulus: ctx.creds.rsa_modulus,
|
|
86
|
+
packageName: ctx.packageName ?? '',
|
|
87
|
+
timeoutMs: ctx.timeoutMs,
|
|
88
|
+
});
|
|
89
|
+
const onlineVersionName = appInfo.packageInfo.versionName;
|
|
90
|
+
const onlineVersionCode = String(appInfo.packageInfo.versionCode);
|
|
91
|
+
return {
|
|
92
|
+
channel: this.channel,
|
|
93
|
+
packageName: ctx.packageName,
|
|
94
|
+
versionName: ctx.versionName,
|
|
95
|
+
versionCode: ctx.versionCode,
|
|
96
|
+
action: 'status',
|
|
97
|
+
status: mapXiaomiStatus(ctx, onlineVersionName, onlineVersionCode),
|
|
98
|
+
marketVersion: onlineVersionName,
|
|
99
|
+
errorCode: null,
|
|
100
|
+
durationMs: Date.now() - start,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
return toFailed(this.channel, e, start, 'status');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/adapters/android/xiaomi/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// App Store Connect API v1 客户端。
|
|
2
|
+
// 鉴权:JWT ES256(node:crypto,EC P-256)。封装 ascFetch 自动带 Authorization: Bearer。
|
|
3
|
+
import { createPrivateKey, sign } from 'node:crypto';
|
|
4
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
5
|
+
import { UploadError } from '../../../core/types.js';
|
|
6
|
+
import { withRetry } from '../../../infra/http.js';
|
|
7
|
+
const ASC_BASE = 'https://api.appstoreconnect.apple.com';
|
|
8
|
+
function base64url(input) {
|
|
9
|
+
return Buffer.from(input).toString('base64url');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 生成 ASC JWT(ES256)。
|
|
13
|
+
* header {alg:'ES256', kid:key_id, typ:'JWT'}
|
|
14
|
+
* payload {iss:issuer_id, iat:now, exp:now+1200, aud:'appstoreconnect-v1'}
|
|
15
|
+
* 签名用 dsaEncoding:'ieee-p1363' 直接得到 JOSE(R||S) 格式(无需 DER→JOSE 手工转换)。
|
|
16
|
+
*/
|
|
17
|
+
export function makeAscToken(creds) {
|
|
18
|
+
const now = Math.floor(Date.now() / 1000);
|
|
19
|
+
const header = { alg: 'ES256', kid: creds.keyId, typ: 'JWT' };
|
|
20
|
+
const payload = { iss: creds.issuerId, iat: now, exp: now + 1200, aud: 'appstoreconnect-v1' };
|
|
21
|
+
const signingInput = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(payload))}`;
|
|
22
|
+
const key = createPrivateKey(creds.privateKey);
|
|
23
|
+
const signature = sign('sha256', Buffer.from(signingInput), { key, dsaEncoding: 'ieee-p1363' });
|
|
24
|
+
return `${signingInput}.${base64url(signature)}`;
|
|
25
|
+
}
|
|
26
|
+
const enc = encodeURIComponent;
|
|
27
|
+
export class AscClient {
|
|
28
|
+
creds;
|
|
29
|
+
requestTimeoutMs;
|
|
30
|
+
token;
|
|
31
|
+
tokenExp = 0;
|
|
32
|
+
constructor(creds, requestTimeoutMs = 60_000) {
|
|
33
|
+
this.creds = creds;
|
|
34
|
+
this.requestTimeoutMs = requestTimeoutMs;
|
|
35
|
+
}
|
|
36
|
+
authToken() {
|
|
37
|
+
const now = Math.floor(Date.now() / 1000);
|
|
38
|
+
if (!this.token || now >= this.tokenExp - 60) {
|
|
39
|
+
this.token = makeAscToken(this.creds);
|
|
40
|
+
this.tokenExp = now + 1200;
|
|
41
|
+
}
|
|
42
|
+
return this.token;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 通用请求:method/path(+body),自动带鉴权;非 2xx 抛 UploadError(携带 ASC 错误码,合并全部 errors)。
|
|
46
|
+
* 仅对 **GET** 在 5xx/429/网络/超时 时重试(幂等);POST/PATCH 不自动重试,避免重复创建/重复提交。
|
|
47
|
+
*/
|
|
48
|
+
async ascFetch(method, path, body) {
|
|
49
|
+
const url = path.startsWith('http') ? path : `${ASC_BASE}${path}`;
|
|
50
|
+
return withRetry(async () => {
|
|
51
|
+
const res = await fetch(url, {
|
|
52
|
+
method,
|
|
53
|
+
headers: {
|
|
54
|
+
Authorization: `Bearer ${this.authToken()}`,
|
|
55
|
+
...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
|
56
|
+
},
|
|
57
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
58
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
59
|
+
});
|
|
60
|
+
const text = await res.text();
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
let code = res.status;
|
|
63
|
+
let msg = text.slice(0, 800);
|
|
64
|
+
try {
|
|
65
|
+
const errs = JSON.parse(text)?.errors;
|
|
66
|
+
if (Array.isArray(errs) && errs.length > 0) {
|
|
67
|
+
code = errs[0].code ?? res.status;
|
|
68
|
+
// 提审失败常一次返回多条(缺截图/缺合规/缺 whatsNew),全部拼出便于诊断。
|
|
69
|
+
msg =
|
|
70
|
+
errs
|
|
71
|
+
.map((e) => [e.title, e.detail].filter(Boolean).join(' — '))
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.join(' | ') || msg;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* 保留原始文本 */
|
|
78
|
+
}
|
|
79
|
+
throw new UploadError(code, `ASC ${method} ${path} -> ${res.status}: ${msg}`);
|
|
80
|
+
}
|
|
81
|
+
if (!text)
|
|
82
|
+
return null; // 204 No Content(如 PATCH relationships)
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(text);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
throw new UploadError('parse', `ASC 非 JSON 响应: ${text.slice(0, 300)}`);
|
|
88
|
+
}
|
|
89
|
+
}, { label: `ASC ${method} ${path}`, retries: method === 'GET' ? 3 : 0 });
|
|
90
|
+
}
|
|
91
|
+
// ── builds ──
|
|
92
|
+
/**
|
|
93
|
+
* 轮询 build 直到 processingState=VALID,返回 build id。
|
|
94
|
+
* GET /v1/builds?filter[app]=<appId>&filter[version]=<buildVersion>
|
|
95
|
+
*
|
|
96
|
+
* 两点健壮性(避免「上游上传失败 → 静默轮询到超时」的坑):
|
|
97
|
+
* - build 完全查不到(空结果)通常意味着上游 altool 上传失败/未完成:只容忍 notFoundGraceMs,
|
|
98
|
+
* 之后 **快速失败** 并给出可诊断信息,而不是一路静默轮询到 deadline(默认 30min)。
|
|
99
|
+
* 一旦见过该 build(哪怕仍在 PROCESSING),就转为正常等待到 deadline。
|
|
100
|
+
* - 每次轮询打一行 stderr 进度(VALID 命中直接返回、不打日志,健康路径保持安静)。
|
|
101
|
+
*/
|
|
102
|
+
async waitBuildValid(appId, buildVersion, preReleaseVersion, deadlineMs, intervalMs = 15_000, notFoundGraceMs = 300_000) {
|
|
103
|
+
// 用 CFBundleVersion + 营销版本(版本列车)双过滤,避免跨版本列车复用同一 build 号时选错;
|
|
104
|
+
// sort=-uploadedDate 取最新上传,避免 data[0] 顺序不确定。
|
|
105
|
+
const train = preReleaseVersion
|
|
106
|
+
? `&filter[preReleaseVersion.version]=${enc(preReleaseVersion)}`
|
|
107
|
+
: '';
|
|
108
|
+
const query = `/v1/builds?filter[app]=${enc(appId)}&filter[version]=${enc(buildVersion)}${train}&sort=-uploadedDate&limit=1`;
|
|
109
|
+
const label = `${buildVersion}(${preReleaseVersion ?? '-'})`;
|
|
110
|
+
const notFoundDeadline = Math.min(Date.now() + notFoundGraceMs, deadlineMs);
|
|
111
|
+
let seen = false;
|
|
112
|
+
for (let attempt = 1;; attempt++) {
|
|
113
|
+
const data = await this.ascFetch('GET', query);
|
|
114
|
+
const build = data?.data?.[0];
|
|
115
|
+
const state = build?.attributes?.processingState;
|
|
116
|
+
if (state === 'VALID')
|
|
117
|
+
return build.id;
|
|
118
|
+
if (state === 'FAILED' || state === 'INVALID') {
|
|
119
|
+
throw new UploadError(state, `build ${label} 处理失败: ${state}`);
|
|
120
|
+
}
|
|
121
|
+
if (build)
|
|
122
|
+
seen = true;
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
console.error(`[asc] 等待 build ${label} 就绪… 第 ${attempt} 次,state=${state ?? '未出现(ASC 上暂无此 build)'}`);
|
|
125
|
+
// 从未出现且已过宽限期:几乎必然是上游没传上来,快速失败(而非空转到 deadline)。
|
|
126
|
+
if (!seen && now > notFoundDeadline) {
|
|
127
|
+
throw new UploadError('build_not_found', `ASC 上找不到 build ${label}(${Math.round(notFoundGraceMs / 1000)}s 内始终未出现)。` +
|
|
128
|
+
'通常是上游 altool 上传失败/未完成——请检查 asc-app 部署日志确认 build 已成功上传到 App Store Connect,再重试提审。');
|
|
129
|
+
}
|
|
130
|
+
if (now > deadlineMs) {
|
|
131
|
+
throw new UploadError('timeout', `等待 build ${label} 进入 VALID 超时(最后 state=${state ?? '未出现'})`);
|
|
132
|
+
}
|
|
133
|
+
await sleep(intervalMs);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// ── appStoreVersions ──
|
|
137
|
+
/** GET /v1/apps/<appId>/appStoreVersions?filter[versionString]&filter[platform]=IOS,取第一条或 null。 */
|
|
138
|
+
async getAppStoreVersion(appId, versionString) {
|
|
139
|
+
const data = await this.ascFetch('GET', `/v1/apps/${enc(appId)}/appStoreVersions?filter[versionString]=${enc(versionString)}&filter[platform]=IOS&limit=1`);
|
|
140
|
+
return data?.data?.[0] ?? null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 用 bundleId 反查 app 的数字 Apple ID。
|
|
144
|
+
* altool 传包靠 ipa 里的 bundleId 定位 app(不需数字 id),但 REST API 要数字 id 引用资源。
|
|
145
|
+
*/
|
|
146
|
+
async getAppIdByBundleId(bundleId) {
|
|
147
|
+
const data = await this.ascFetch('GET', `/v1/apps?filter[bundleId]=${enc(bundleId)}&limit=1`);
|
|
148
|
+
const id = data?.data?.[0]?.id;
|
|
149
|
+
if (!id) {
|
|
150
|
+
throw new UploadError('not_found', `ASC 未找到 bundleId=${bundleId} 对应的 app(确认 bundle id 正确、且该 API key 有此 app 权限)`);
|
|
151
|
+
}
|
|
152
|
+
return String(id);
|
|
153
|
+
}
|
|
154
|
+
/** app_id 优先;否则用 bundleId 反查。两者都没有则报错。 */
|
|
155
|
+
async resolveAppId(appId, bundleId) {
|
|
156
|
+
if (appId)
|
|
157
|
+
return appId;
|
|
158
|
+
if (bundleId)
|
|
159
|
+
return this.getAppIdByBundleId(bundleId);
|
|
160
|
+
throw new UploadError('input', '需要 --app-id 或 --bundle-id 之一');
|
|
161
|
+
}
|
|
162
|
+
/** 取已有 appStoreVersion,否则创建(POST /v1/appStoreVersions)。返回 version id。 */
|
|
163
|
+
async ensureAppStoreVersion(appId, versionString, releaseType) {
|
|
164
|
+
const existing = await this.getAppStoreVersion(appId, versionString);
|
|
165
|
+
if (existing) {
|
|
166
|
+
// 已存在版本:若 releaseType 与现值不同则纠正(仅在可编辑态有效,否则 ASC 会拒)。
|
|
167
|
+
if (releaseType && existing.attributes?.releaseType !== releaseType) {
|
|
168
|
+
await this.ascFetch('PATCH', `/v1/appStoreVersions/${enc(existing.id)}`, {
|
|
169
|
+
data: { type: 'appStoreVersions', id: existing.id, attributes: { releaseType } },
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return existing.id;
|
|
173
|
+
}
|
|
174
|
+
const attributes = { platform: 'IOS', versionString };
|
|
175
|
+
if (releaseType)
|
|
176
|
+
attributes.releaseType = releaseType;
|
|
177
|
+
const res = await this.ascFetch('POST', '/v1/appStoreVersions', {
|
|
178
|
+
data: {
|
|
179
|
+
type: 'appStoreVersions',
|
|
180
|
+
attributes,
|
|
181
|
+
relationships: { app: { data: { type: 'apps', id: appId } } },
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
return res.data.id;
|
|
185
|
+
}
|
|
186
|
+
// ── localizations ──
|
|
187
|
+
/** 写多地区文案:已存在的 locale PATCH,否则 POST(仅写非空字段)。 */
|
|
188
|
+
async writeLocalizations(versionId, byLocale) {
|
|
189
|
+
const locales = Object.keys(byLocale);
|
|
190
|
+
if (locales.length === 0)
|
|
191
|
+
return;
|
|
192
|
+
const list = await this.ascFetch('GET', `/v1/appStoreVersions/${enc(versionId)}/appStoreVersionLocalizations?limit=50`);
|
|
193
|
+
const existing = list?.data ?? [];
|
|
194
|
+
for (const locale of locales) {
|
|
195
|
+
const attrs = {};
|
|
196
|
+
for (const [k, v] of Object.entries(byLocale[locale])) {
|
|
197
|
+
if (v != null && v !== '')
|
|
198
|
+
attrs[k] = v;
|
|
199
|
+
}
|
|
200
|
+
if (Object.keys(attrs).length === 0)
|
|
201
|
+
continue;
|
|
202
|
+
const found = existing.find((l) => l.attributes?.locale === locale);
|
|
203
|
+
if (found) {
|
|
204
|
+
await this.ascFetch('PATCH', `/v1/appStoreVersionLocalizations/${enc(found.id)}`, {
|
|
205
|
+
data: { type: 'appStoreVersionLocalizations', id: found.id, attributes: attrs },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
await this.ascFetch('POST', '/v1/appStoreVersionLocalizations', {
|
|
210
|
+
data: {
|
|
211
|
+
type: 'appStoreVersionLocalizations',
|
|
212
|
+
attributes: { locale, ...attrs },
|
|
213
|
+
relationships: { appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } } },
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** 关联 build:PATCH /v1/appStoreVersions/<id>/relationships/build。 */
|
|
220
|
+
async attachBuild(versionId, buildId) {
|
|
221
|
+
await this.ascFetch('PATCH', `/v1/appStoreVersions/${enc(versionId)}/relationships/build`, {
|
|
222
|
+
data: { type: 'builds', id: buildId },
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
// ── reviewSubmissions(三步)──
|
|
226
|
+
async submitForReview(appId, versionId) {
|
|
227
|
+
const sub = await this.ascFetch('POST', '/v1/reviewSubmissions', {
|
|
228
|
+
data: {
|
|
229
|
+
type: 'reviewSubmissions',
|
|
230
|
+
attributes: { platform: 'IOS' },
|
|
231
|
+
relationships: { app: { data: { type: 'apps', id: appId } } },
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
const subId = sub.data.id;
|
|
235
|
+
await this.ascFetch('POST', '/v1/reviewSubmissionItems', {
|
|
236
|
+
data: {
|
|
237
|
+
type: 'reviewSubmissionItems',
|
|
238
|
+
relationships: {
|
|
239
|
+
reviewSubmission: { data: { type: 'reviewSubmissions', id: subId } },
|
|
240
|
+
appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } },
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
await this.ascFetch('PATCH', `/v1/reviewSubmissions/${enc(subId)}`, {
|
|
245
|
+
data: { type: 'reviewSubmissions', id: subId, attributes: { submitted: true } },
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// ── phased release / release request ──
|
|
249
|
+
/** GET /v1/appStoreVersions/<id>/appStoreVersionPhasedRelease(to-one),无则 null。 */
|
|
250
|
+
async getPhasedRelease(versionId) {
|
|
251
|
+
const data = await this.ascFetch('GET', `/v1/appStoreVersions/${enc(versionId)}/appStoreVersionPhasedRelease`);
|
|
252
|
+
return data?.data ?? null;
|
|
253
|
+
}
|
|
254
|
+
/** 开启 7 天灰度:POST /v1/appStoreVersionPhasedReleases。 */
|
|
255
|
+
async startPhasedRelease(versionId) {
|
|
256
|
+
const res = await this.ascFetch('POST', '/v1/appStoreVersionPhasedReleases', {
|
|
257
|
+
data: {
|
|
258
|
+
type: 'appStoreVersionPhasedReleases',
|
|
259
|
+
relationships: { appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } } },
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
return res.data;
|
|
263
|
+
}
|
|
264
|
+
/** PATCH /v1/appStoreVersionPhasedReleases/<id> 设 phasedReleaseState(合法值 ACTIVE/PAUSED/COMPLETE)。 */
|
|
265
|
+
async setPhasedReleaseState(phasedId, state) {
|
|
266
|
+
await this.ascFetch('PATCH', `/v1/appStoreVersionPhasedReleases/${enc(phasedId)}`, {
|
|
267
|
+
data: { type: 'appStoreVersionPhasedReleases', id: phasedId, attributes: { phasedReleaseState: state } },
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/** 手动触发发布:POST /v1/appStoreVersionReleaseRequests。 */
|
|
271
|
+
async requestRelease(versionId) {
|
|
272
|
+
await this.ascFetch('POST', '/v1/appStoreVersionReleaseRequests', {
|
|
273
|
+
data: {
|
|
274
|
+
type: 'appStoreVersionReleaseRequests',
|
|
275
|
+
relationships: { appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } } },
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=asc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asc.js","sourceRoot":"","sources":["../../../../src/adapters/ios/appstore/asc.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,4EAA4E;AAC5E,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,QAAQ,GAAG,uCAAuC,CAAC;AAmBzD,SAAS,SAAS,CAAC,KAAsB;IACvC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAClD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC9D,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,EAAE,GAAG,EAAE,oBAAoB,EAAE,CAAC;IAC9F,MAAM,YAAY,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IAClG,MAAM,GAAG,GAAG,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,CAAC;IAChG,OAAO,GAAG,YAAY,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,GAAG,GAAG,kBAAkB,CAAC;AAE/B,MAAM,OAAO,SAAS;IACH,KAAK,CAAW;IAChB,gBAAgB,CAAS;IAClC,KAAK,CAAU;IACf,QAAQ,GAAG,CAAC,CAAC;IAErB,YAAY,KAAe,EAAE,gBAAgB,GAAG,MAAM;QACpD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC3C,CAAC;IAEO,SAAS;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,IAAI,IAAI,CAAC,QAAQ,GAAG,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtC,IAAI,CAAC,QAAQ,GAAG,GAAG,GAAG,IAAI,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,IAAY,EAAE,IAAc;QACzD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,IAAI,EAAE,CAAC;QAClE,OAAO,SAAS,CACd,KAAK,IAAI,EAAE;YACT,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM;gBACN,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,IAAI,CAAC,SAAS,EAAE,EAAE;oBAC3C,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACtE;gBACD,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC3D,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC;aACnD,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,IAAI,IAAI,GAAoB,GAAG,CAAC,MAAM,CAAC;gBACvC,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBAC7B,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;oBACtC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC3C,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC;wBAClC,4CAA4C;wBAC5C,GAAG;4BACD,IAAI;iCACD,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;iCAChE,MAAM,CAAC,OAAO,CAAC;iCACf,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;oBAC1B,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,YAAY;gBACd,CAAC;gBACD,MAAM,IAAI,WAAW,CAAC,IAAI,EAAE,OAAO,MAAM,IAAI,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC;YAChF,CAAC;YACD,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,CAAC,wCAAwC;YAChE,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,WAAW,CAAC,OAAO,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACzE,CAAC;QACH,CAAC,EACD,EAAE,KAAK,EAAE,OAAO,MAAM,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CACtE,CAAC;IACJ,CAAC;IAED,eAAe;IAEf;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc,CAClB,KAAa,EACb,YAAoB,EACpB,iBAAqC,EACrC,UAAkB,EAClB,UAAU,GAAG,MAAM,EACnB,eAAe,GAAG,OAAO;QAEzB,4DAA4D;QAC5D,6CAA6C;QAC7C,MAAM,KAAK,GAAG,iBAAiB;YAC7B,CAAC,CAAC,sCAAsC,GAAG,CAAC,iBAAiB,CAAC,EAAE;YAChE,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,KAAK,GAAG,0BAA0B,GAAG,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,YAAY,CAAC,GAAG,KAAK,6BAA6B,CAAC;QAC7H,MAAM,KAAK,GAAG,GAAG,YAAY,IAAI,iBAAiB,IAAI,GAAG,GAAG,CAAC;QAC7D,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,EAAE,UAAU,CAAC,CAAC;QAC5E,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC/C,MAAM,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,KAAK,GAAuB,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC;YACrE,IAAI,KAAK,KAAK,OAAO;gBAAE,OAAO,KAAK,CAAC,EAAY,CAAC;YACjD,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC9C,MAAM,IAAI,WAAW,CAAC,KAAK,EAAE,SAAS,KAAK,UAAU,KAAK,EAAE,CAAC,CAAC;YAChE,CAAC;YACD,IAAI,KAAK;gBAAE,IAAI,GAAG,IAAI,CAAC;YACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,OAAO,CAAC,KAAK,CACX,kBAAkB,KAAK,UAAU,OAAO,YAAY,KAAK,IAAI,qBAAqB,EAAE,CACrF,CAAC;YACF,+CAA+C;YAC/C,IAAI,CAAC,IAAI,IAAI,GAAG,GAAG,gBAAgB,EAAE,CAAC;gBACpC,MAAM,IAAI,WAAW,CACnB,iBAAiB,EACjB,kBAAkB,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,YAAY;oBACvE,iFAAiF,CACpF,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,GAAG,UAAU,EAAE,CAAC;gBACrB,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,YAAY,KAAK,yBAAyB,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC;YAChG,CAAC;YACD,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,yBAAyB;IAEzB,mGAAmG;IACnG,KAAK,CAAC,kBAAkB,CAAC,KAAa,EAAE,aAAqB;QAC3D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC9B,KAAK,EACL,YAAY,GAAG,CAAC,KAAK,CAAC,2CAA2C,GAAG,CAAC,aAAa,CAAC,+BAA+B,CACnH,CAAC;QACF,OAAO,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACjC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,kBAAkB,CAAC,QAAgB;QACvC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,6BAA6B,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC9F,MAAM,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,IAAI,WAAW,CACnB,WAAW,EACX,oBAAoB,QAAQ,gDAAgD,CAC7E,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAED,0CAA0C;IAC1C,KAAK,CAAC,YAAY,CAAC,KAAc,EAAE,QAAiB;QAClD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,IAAI,QAAQ;YAAE,OAAO,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACvD,MAAM,IAAI,WAAW,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;IACjE,CAAC;IAED,yEAAyE;IACzE,KAAK,CAAC,qBAAqB,CAAC,KAAa,EAAE,aAAqB,EAAE,WAAoB;QACpF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACrE,IAAI,QAAQ,EAAE,CAAC;YACb,oDAAoD;YACpD,IAAI,WAAW,IAAI,QAAQ,CAAC,UAAU,EAAE,WAAW,KAAK,WAAW,EAAE,CAAC;gBACpE,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,wBAAwB,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE;oBACvE,IAAI,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,WAAW,EAAE,EAAE;iBACjF,CAAC,CAAC;YACL,CAAC;YACD,OAAO,QAAQ,CAAC,EAAY,CAAC;QAC/B,CAAC;QACD,MAAM,UAAU,GAA2B,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAC9E,IAAI,WAAW;YAAE,UAAU,CAAC,WAAW,GAAG,WAAW,CAAC;QACtD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,sBAAsB,EAAE;YAC9D,IAAI,EAAE;gBACJ,IAAI,EAAE,kBAAkB;gBACxB,UAAU;gBACV,aAAa,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;aAC9D;SACF,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAY,CAAC;IAC/B,CAAC;IAED,sBAAsB;IAEtB,gDAAgD;IAChD,KAAK,CAAC,kBAAkB,CAAC,SAAiB,EAAE,QAA4C;QACtF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC9B,KAAK,EACL,wBAAwB,GAAG,CAAC,SAAS,CAAC,wCAAwC,CAC/E,CAAC;QACF,MAAM,QAAQ,GAAU,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;QACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,KAAK,GAA2B,EAAE,CAAC;YACzC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBACtD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE;oBAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC1C,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;YACpE,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,oCAAoC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE;oBAChF,IAAI,EAAE,EAAE,IAAI,EAAE,8BAA8B,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE;iBAChF,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,kCAAkC,EAAE;oBAC9D,IAAI,EAAE;wBACJ,IAAI,EAAE,8BAA8B;wBACpC,UAAU,EAAE,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE;wBAChC,aAAa,EAAE,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;qBAC1F;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,OAAe;QAClD,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,wBAAwB,GAAG,CAAC,SAAS,CAAC,sBAAsB,EAAE;YACzF,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE;SACtC,CAAC,CAAC;IACL,CAAC;IAED,6BAA6B;IAE7B,KAAK,CAAC,eAAe,CAAC,KAAa,EAAE,SAAiB;QACpD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE;YAC/D,IAAI,EAAE;gBACJ,IAAI,EAAE,mBAAmB;gBACzB,UAAU,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;gBAC/B,aAAa,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;aAC9D;SACF,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAY,CAAC;QACpC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,2BAA2B,EAAE;YACvD,IAAI,EAAE;gBACJ,IAAI,EAAE,uBAAuB;gBAC7B,aAAa,EAAE;oBACb,gBAAgB,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE;oBACpE,eAAe,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;iBACvE;aACF;SACF,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,yBAAyB,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE;YAClE,IAAI,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;SAChF,CAAC,CAAC;IACL,CAAC;IAED,yCAAyC;IAEzC,kFAAkF;IAClF,KAAK,CAAC,gBAAgB,CAAC,SAAiB;QACtC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC9B,KAAK,EACL,wBAAwB,GAAG,CAAC,SAAS,CAAC,+BAA+B,CACtE,CAAC;QACF,OAAO,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC;IAC5B,CAAC;IAED,uDAAuD;IACvD,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,mCAAmC,EAAE;YAC3E,IAAI,EAAE;gBACJ,IAAI,EAAE,+BAA+B;gBACrC,aAAa,EAAE,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;aAC1F;SACF,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IAED,qGAAqG;IACrG,KAAK,CAAC,qBAAqB,CAAC,QAAgB,EAAE,KAAuC;QACnF,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,qCAAqC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE;YACjF,IAAI,EAAE,EAAE,IAAI,EAAE,+BAA+B,EAAE,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,EAAE;SACzG,CAAC,CAAC;IACL,CAAC;IAED,sDAAsD;IACtD,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,oCAAoC,EAAE;YAChE,IAAI,EAAE;gBACJ,IAAI,EAAE,gCAAgC;gBACtC,aAAa,EAAE,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;aAC1F;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
|