preflightlaunch 0.2.0 → 0.2.2
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.
|
@@ -3,6 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
__commonJS,
|
|
6
|
+
__require,
|
|
6
7
|
__toESM,
|
|
7
8
|
init_esm_shims,
|
|
8
9
|
open_default
|
|
@@ -2980,7 +2981,7 @@ var require_compile = __commonJS({
|
|
|
2980
2981
|
const schOrFunc = root.refs[ref];
|
|
2981
2982
|
if (schOrFunc)
|
|
2982
2983
|
return schOrFunc;
|
|
2983
|
-
let _sch =
|
|
2984
|
+
let _sch = resolve4.call(this, root, ref);
|
|
2984
2985
|
if (_sch === void 0) {
|
|
2985
2986
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2986
2987
|
const { schemaId } = this.opts;
|
|
@@ -3007,7 +3008,7 @@ var require_compile = __commonJS({
|
|
|
3007
3008
|
function sameSchemaEnv(s1, s2) {
|
|
3008
3009
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3009
3010
|
}
|
|
3010
|
-
function
|
|
3011
|
+
function resolve4(root, ref) {
|
|
3011
3012
|
let sch;
|
|
3012
3013
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3013
3014
|
ref = sch;
|
|
@@ -3585,7 +3586,7 @@ var require_fast_uri = __commonJS({
|
|
|
3585
3586
|
}
|
|
3586
3587
|
return uri;
|
|
3587
3588
|
}
|
|
3588
|
-
function
|
|
3589
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
3589
3590
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3590
3591
|
const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3591
3592
|
schemelessOptions.skipEscape = true;
|
|
@@ -3812,7 +3813,7 @@ var require_fast_uri = __commonJS({
|
|
|
3812
3813
|
var fastUri = {
|
|
3813
3814
|
SCHEMES,
|
|
3814
3815
|
normalize,
|
|
3815
|
-
resolve:
|
|
3816
|
+
resolve: resolve4,
|
|
3816
3817
|
resolveComponent,
|
|
3817
3818
|
equal,
|
|
3818
3819
|
serialize,
|
|
@@ -10245,7 +10246,7 @@ var require_compile2 = __commonJS({
|
|
|
10245
10246
|
const schOrFunc = root.refs[ref];
|
|
10246
10247
|
if (schOrFunc)
|
|
10247
10248
|
return schOrFunc;
|
|
10248
|
-
let _sch =
|
|
10249
|
+
let _sch = resolve4.call(this, root, ref);
|
|
10249
10250
|
if (_sch === void 0) {
|
|
10250
10251
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
10251
10252
|
const { schemaId } = this.opts;
|
|
@@ -10272,7 +10273,7 @@ var require_compile2 = __commonJS({
|
|
|
10272
10273
|
function sameSchemaEnv(s1, s2) {
|
|
10273
10274
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
10274
10275
|
}
|
|
10275
|
-
function
|
|
10276
|
+
function resolve4(root, ref) {
|
|
10276
10277
|
let sch;
|
|
10277
10278
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
10278
10279
|
ref = sch;
|
|
@@ -17430,8 +17431,8 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
17430
17431
|
var source_default = chalk;
|
|
17431
17432
|
|
|
17432
17433
|
// src/commands/submit.ts
|
|
17433
|
-
import { readFileSync, statSync as
|
|
17434
|
-
import { basename as basename3, resolve as
|
|
17434
|
+
import { readFileSync, statSync as statSync3 } from "fs";
|
|
17435
|
+
import { basename as basename3, resolve as resolve3 } from "path";
|
|
17435
17436
|
|
|
17436
17437
|
// src/lib/scanner.ts
|
|
17437
17438
|
init_esm_shims();
|
|
@@ -17867,7 +17868,7 @@ var retryifyAsync = (fn, options) => {
|
|
|
17867
17868
|
throw error2;
|
|
17868
17869
|
const delay = Math.round(interval * Math.random());
|
|
17869
17870
|
if (delay > 0) {
|
|
17870
|
-
const delayPromise = new Promise((
|
|
17871
|
+
const delayPromise = new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
17871
17872
|
return delayPromise.then(() => attempt.apply(void 0, args));
|
|
17872
17873
|
} else {
|
|
17873
17874
|
return attempt.apply(void 0, args);
|
|
@@ -18669,8 +18670,8 @@ var Conf = class {
|
|
|
18669
18670
|
}
|
|
18670
18671
|
try {
|
|
18671
18672
|
const initializationVector = data.slice(0, 16);
|
|
18672
|
-
const
|
|
18673
|
-
const decipher = crypto2.createDecipheriv(encryptionAlgorithm,
|
|
18673
|
+
const password2 = crypto2.pbkdf2Sync(this.#encryptionKey, initializationVector.toString(), 1e4, 32, "sha512");
|
|
18674
|
+
const decipher = crypto2.createDecipheriv(encryptionAlgorithm, password2, initializationVector);
|
|
18674
18675
|
const slice = data.slice(17);
|
|
18675
18676
|
const dataUpdate = typeof slice === "string" ? stringToUint8Array(slice) : slice;
|
|
18676
18677
|
return uint8ArrayToString(concatUint8Arrays([decipher.update(dataUpdate), decipher.final()]));
|
|
@@ -18714,8 +18715,8 @@ var Conf = class {
|
|
|
18714
18715
|
let data = this._serialize(value);
|
|
18715
18716
|
if (this.#encryptionKey) {
|
|
18716
18717
|
const initializationVector = crypto2.randomBytes(16);
|
|
18717
|
-
const
|
|
18718
|
-
const cipher = crypto2.createCipheriv(encryptionAlgorithm,
|
|
18718
|
+
const password2 = crypto2.pbkdf2Sync(this.#encryptionKey, initializationVector.toString(), 1e4, 32, "sha512");
|
|
18719
|
+
const cipher = crypto2.createCipheriv(encryptionAlgorithm, password2, initializationVector);
|
|
18719
18720
|
data = concatUint8Arrays([initializationVector, stringToUint8Array(":"), cipher.update(stringToUint8Array(data)), cipher.final()]);
|
|
18720
18721
|
}
|
|
18721
18722
|
if (process8.env.SNAP) {
|
|
@@ -18821,28 +18822,43 @@ var Conf = class {
|
|
|
18821
18822
|
}
|
|
18822
18823
|
};
|
|
18823
18824
|
|
|
18825
|
+
// src/lib/constants.ts
|
|
18826
|
+
init_esm_shims();
|
|
18827
|
+
var SUPABASE_URL = "https://cfqzdyktjhkalfrmcgmw.supabase.co";
|
|
18828
|
+
var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNmcXpkeWt0amhrYWxmcm1jZ213Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNzM4MjYsImV4cCI6MjA4NDg0OTgyNn0.O1bPUNHw7kzpWecAyT4Pizh2ITRSal3PJsrUIkZY04o";
|
|
18829
|
+
var CALLBACK_PORT = 54321;
|
|
18830
|
+
var DEFAULT_API_URL = "https://preflightlaunch.com";
|
|
18831
|
+
|
|
18824
18832
|
// src/lib/config.ts
|
|
18825
18833
|
var config = new Conf({
|
|
18826
18834
|
projectName: "preflight",
|
|
18827
18835
|
schema: {
|
|
18828
18836
|
accessToken: { type: "string" },
|
|
18829
18837
|
refreshToken: { type: "string" },
|
|
18830
|
-
apiUrl: { type: "string", default:
|
|
18838
|
+
apiUrl: { type: "string", default: DEFAULT_API_URL },
|
|
18831
18839
|
userId: { type: "string" },
|
|
18832
18840
|
email: { type: "string" },
|
|
18833
18841
|
hasRunBefore: { type: "boolean", default: false },
|
|
18834
|
-
lastScannedPath: { type: "string" }
|
|
18842
|
+
lastScannedPath: { type: "string" },
|
|
18843
|
+
ascConnected: { type: "boolean", default: false }
|
|
18835
18844
|
}
|
|
18836
18845
|
});
|
|
18846
|
+
try {
|
|
18847
|
+
if (config.get("apiUrl") === "https://preflight.dev") {
|
|
18848
|
+
config.set("apiUrl", DEFAULT_API_URL);
|
|
18849
|
+
}
|
|
18850
|
+
} catch {
|
|
18851
|
+
}
|
|
18837
18852
|
function getConfig() {
|
|
18838
18853
|
return {
|
|
18839
18854
|
accessToken: config.get("accessToken"),
|
|
18840
18855
|
refreshToken: config.get("refreshToken"),
|
|
18841
|
-
apiUrl: config.get("apiUrl") ||
|
|
18856
|
+
apiUrl: config.get("apiUrl") || DEFAULT_API_URL,
|
|
18842
18857
|
userId: config.get("userId"),
|
|
18843
18858
|
email: config.get("email"),
|
|
18844
18859
|
hasRunBefore: config.get("hasRunBefore") || false,
|
|
18845
|
-
lastScannedPath: config.get("lastScannedPath")
|
|
18860
|
+
lastScannedPath: config.get("lastScannedPath"),
|
|
18861
|
+
ascConnected: config.get("ascConnected") || false
|
|
18846
18862
|
};
|
|
18847
18863
|
}
|
|
18848
18864
|
function setTokens(accessToken, refreshToken) {
|
|
@@ -18874,6 +18890,12 @@ function setLastScannedPath(path5) {
|
|
|
18874
18890
|
function getLastScannedPath() {
|
|
18875
18891
|
return config.get("lastScannedPath");
|
|
18876
18892
|
}
|
|
18893
|
+
function getAscConnected() {
|
|
18894
|
+
return config.get("ascConnected") || false;
|
|
18895
|
+
}
|
|
18896
|
+
function setAscConnected(connected) {
|
|
18897
|
+
config.set("ascConnected", connected);
|
|
18898
|
+
}
|
|
18877
18899
|
|
|
18878
18900
|
// ../../node_modules/@supabase/supabase-js/dist/index.mjs
|
|
18879
18901
|
init_esm_shims();
|
|
@@ -18899,11 +18921,11 @@ function __rest(s, e) {
|
|
|
18899
18921
|
}
|
|
18900
18922
|
function __awaiter(thisArg, _arguments, P3, generator) {
|
|
18901
18923
|
function adopt(value) {
|
|
18902
|
-
return value instanceof P3 ? value : new P3(function(
|
|
18903
|
-
|
|
18924
|
+
return value instanceof P3 ? value : new P3(function(resolve4) {
|
|
18925
|
+
resolve4(value);
|
|
18904
18926
|
});
|
|
18905
18927
|
}
|
|
18906
|
-
return new (P3 || (P3 = Promise))(function(
|
|
18928
|
+
return new (P3 || (P3 = Promise))(function(resolve4, reject) {
|
|
18907
18929
|
function fulfilled(value) {
|
|
18908
18930
|
try {
|
|
18909
18931
|
step(generator.next(value));
|
|
@@ -18919,7 +18941,7 @@ function __awaiter(thisArg, _arguments, P3, generator) {
|
|
|
18919
18941
|
}
|
|
18920
18942
|
}
|
|
18921
18943
|
function step(result) {
|
|
18922
|
-
result.done ?
|
|
18944
|
+
result.done ? resolve4(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18923
18945
|
}
|
|
18924
18946
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18925
18947
|
});
|
|
@@ -21472,15 +21494,15 @@ var RealtimeChannel = class _RealtimeChannel {
|
|
|
21472
21494
|
}
|
|
21473
21495
|
}
|
|
21474
21496
|
} else {
|
|
21475
|
-
return new Promise((
|
|
21497
|
+
return new Promise((resolve4) => {
|
|
21476
21498
|
var _a2, _b2, _c;
|
|
21477
21499
|
const push = this._push(args.type, args, opts.timeout || this.timeout);
|
|
21478
21500
|
if (args.type === "broadcast" && !((_c = (_b2 = (_a2 = this.params) === null || _a2 === void 0 ? void 0 : _a2.config) === null || _b2 === void 0 ? void 0 : _b2.broadcast) === null || _c === void 0 ? void 0 : _c.ack)) {
|
|
21479
|
-
|
|
21501
|
+
resolve4("ok");
|
|
21480
21502
|
}
|
|
21481
|
-
push.receive("ok", () =>
|
|
21482
|
-
push.receive("error", () =>
|
|
21483
|
-
push.receive("timeout", () =>
|
|
21503
|
+
push.receive("ok", () => resolve4("ok"));
|
|
21504
|
+
push.receive("error", () => resolve4("error"));
|
|
21505
|
+
push.receive("timeout", () => resolve4("timed out"));
|
|
21484
21506
|
});
|
|
21485
21507
|
}
|
|
21486
21508
|
}
|
|
@@ -21508,16 +21530,16 @@ var RealtimeChannel = class _RealtimeChannel {
|
|
|
21508
21530
|
};
|
|
21509
21531
|
this.joinPush.destroy();
|
|
21510
21532
|
let leavePush = null;
|
|
21511
|
-
return new Promise((
|
|
21533
|
+
return new Promise((resolve4) => {
|
|
21512
21534
|
leavePush = new Push(this, CHANNEL_EVENTS.leave, {}, timeout);
|
|
21513
21535
|
leavePush.receive("ok", () => {
|
|
21514
21536
|
onClose();
|
|
21515
|
-
|
|
21537
|
+
resolve4("ok");
|
|
21516
21538
|
}).receive("timeout", () => {
|
|
21517
21539
|
onClose();
|
|
21518
|
-
|
|
21540
|
+
resolve4("timed out");
|
|
21519
21541
|
}).receive("error", () => {
|
|
21520
|
-
|
|
21542
|
+
resolve4("error");
|
|
21521
21543
|
});
|
|
21522
21544
|
leavePush.send();
|
|
21523
21545
|
if (!this._canPush()) {
|
|
@@ -23217,7 +23239,7 @@ var _getRequestParams = (method, options, parameters, body) => {
|
|
|
23217
23239
|
return _objectSpread2(_objectSpread2({}, params), parameters);
|
|
23218
23240
|
};
|
|
23219
23241
|
async function _handleRequest(fetcher, method, url, options, parameters, body, namespace) {
|
|
23220
|
-
return new Promise((
|
|
23242
|
+
return new Promise((resolve4, reject) => {
|
|
23221
23243
|
fetcher(url, _getRequestParams(method, options, parameters, body)).then((result) => {
|
|
23222
23244
|
if (!result.ok) throw result;
|
|
23223
23245
|
if (options === null || options === void 0 ? void 0 : options.noResolveJson) return result;
|
|
@@ -23227,7 +23249,7 @@ async function _handleRequest(fetcher, method, url, options, parameters, body, n
|
|
|
23227
23249
|
if (!contentType || !contentType.includes("application/json")) return {};
|
|
23228
23250
|
}
|
|
23229
23251
|
return result.json();
|
|
23230
|
-
}).then((data) =>
|
|
23252
|
+
}).then((data) => resolve4(data)).catch((error2) => handleError(error2, reject, options, namespace));
|
|
23231
23253
|
});
|
|
23232
23254
|
}
|
|
23233
23255
|
function createFetchApi(namespace = "storage") {
|
|
@@ -27724,7 +27746,7 @@ var GoTrueClient = class _GoTrueClient {
|
|
|
27724
27746
|
try {
|
|
27725
27747
|
let res;
|
|
27726
27748
|
if ("email" in credentials) {
|
|
27727
|
-
const { email, password, options } = credentials;
|
|
27749
|
+
const { email, password: password2, options } = credentials;
|
|
27728
27750
|
let codeChallenge = null;
|
|
27729
27751
|
let codeChallengeMethod = null;
|
|
27730
27752
|
if (this.flowType === "pkce") {
|
|
@@ -27736,7 +27758,7 @@ var GoTrueClient = class _GoTrueClient {
|
|
|
27736
27758
|
redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
|
|
27737
27759
|
body: {
|
|
27738
27760
|
email,
|
|
27739
|
-
password,
|
|
27761
|
+
password: password2,
|
|
27740
27762
|
data: (_a = options === null || options === void 0 ? void 0 : options.data) !== null && _a !== void 0 ? _a : {},
|
|
27741
27763
|
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken },
|
|
27742
27764
|
code_challenge: codeChallenge,
|
|
@@ -27745,12 +27767,12 @@ var GoTrueClient = class _GoTrueClient {
|
|
|
27745
27767
|
xform: _sessionResponse
|
|
27746
27768
|
});
|
|
27747
27769
|
} else if ("phone" in credentials) {
|
|
27748
|
-
const { phone, password, options } = credentials;
|
|
27770
|
+
const { phone, password: password2, options } = credentials;
|
|
27749
27771
|
res = await _request(this.fetch, "POST", `${this.url}/signup`, {
|
|
27750
27772
|
headers: this.headers,
|
|
27751
27773
|
body: {
|
|
27752
27774
|
phone,
|
|
27753
|
-
password,
|
|
27775
|
+
password: password2,
|
|
27754
27776
|
data: (_b = options === null || options === void 0 ? void 0 : options.data) !== null && _b !== void 0 ? _b : {},
|
|
27755
27777
|
channel: (_c = options === null || options === void 0 ? void 0 : options.channel) !== null && _c !== void 0 ? _c : "sms",
|
|
27756
27778
|
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken }
|
|
@@ -27792,23 +27814,23 @@ var GoTrueClient = class _GoTrueClient {
|
|
|
27792
27814
|
try {
|
|
27793
27815
|
let res;
|
|
27794
27816
|
if ("email" in credentials) {
|
|
27795
|
-
const { email, password, options } = credentials;
|
|
27817
|
+
const { email, password: password2, options } = credentials;
|
|
27796
27818
|
res = await _request(this.fetch, "POST", `${this.url}/token?grant_type=password`, {
|
|
27797
27819
|
headers: this.headers,
|
|
27798
27820
|
body: {
|
|
27799
27821
|
email,
|
|
27800
|
-
password,
|
|
27822
|
+
password: password2,
|
|
27801
27823
|
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken }
|
|
27802
27824
|
},
|
|
27803
27825
|
xform: _sessionResponsePassword
|
|
27804
27826
|
});
|
|
27805
27827
|
} else if ("phone" in credentials) {
|
|
27806
|
-
const { phone, password, options } = credentials;
|
|
27828
|
+
const { phone, password: password2, options } = credentials;
|
|
27807
27829
|
res = await _request(this.fetch, "POST", `${this.url}/token?grant_type=password`, {
|
|
27808
27830
|
headers: this.headers,
|
|
27809
27831
|
body: {
|
|
27810
27832
|
phone,
|
|
27811
|
-
password,
|
|
27833
|
+
password: password2,
|
|
27812
27834
|
gotrue_meta_security: { captcha_token: options === null || options === void 0 ? void 0 : options.captchaToken }
|
|
27813
27835
|
},
|
|
27814
27836
|
xform: _sessionResponsePassword
|
|
@@ -30308,13 +30330,6 @@ function shouldShowDeprecationWarning() {
|
|
|
30308
30330
|
}
|
|
30309
30331
|
if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
|
|
30310
30332
|
|
|
30311
|
-
// src/lib/constants.ts
|
|
30312
|
-
init_esm_shims();
|
|
30313
|
-
var SUPABASE_URL = "https://cfqzdyktjhkalfrmcgmw.supabase.co";
|
|
30314
|
-
var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNmcXpkeWt0amhrYWxmcm1jZ213Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNzM4MjYsImV4cCI6MjA4NDg0OTgyNn0.O1bPUNHw7kzpWecAyT4Pizh2ITRSal3PJsrUIkZY04o";
|
|
30315
|
-
var CALLBACK_PORT = 54321;
|
|
30316
|
-
var DEFAULT_API_URL = "https://preflightlaunch.com";
|
|
30317
|
-
|
|
30318
30333
|
// src/lib/api-client.ts
|
|
30319
30334
|
var API_TIMEOUT_MS = 3e4;
|
|
30320
30335
|
async function refreshSession() {
|
|
@@ -30379,10 +30394,14 @@ function decodeJwtPayload(token) {
|
|
|
30379
30394
|
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
|
|
30380
30395
|
return JSON.parse(decoded);
|
|
30381
30396
|
}
|
|
30382
|
-
async function loginWithBrowser() {
|
|
30397
|
+
async function loginWithBrowser(mode = "login") {
|
|
30383
30398
|
const { apiUrl } = getConfig();
|
|
30384
30399
|
const baseUrl = apiUrl || DEFAULT_API_URL;
|
|
30385
|
-
|
|
30400
|
+
if (mode === "signup") {
|
|
30401
|
+
await open_default(`${baseUrl}/auth/signup`);
|
|
30402
|
+
return null;
|
|
30403
|
+
}
|
|
30404
|
+
return new Promise((resolve4) => {
|
|
30386
30405
|
const server = http.createServer(async (req, res) => {
|
|
30387
30406
|
const url = new URL(req.url, `http://localhost:${CALLBACK_PORT}`);
|
|
30388
30407
|
if (url.pathname === "/callback") {
|
|
@@ -30393,7 +30412,7 @@ async function loginWithBrowser() {
|
|
|
30393
30412
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
30394
30413
|
res.end(htmlPage("Login failed", errorMsg));
|
|
30395
30414
|
server.close();
|
|
30396
|
-
|
|
30415
|
+
resolve4(null);
|
|
30397
30416
|
return;
|
|
30398
30417
|
}
|
|
30399
30418
|
try {
|
|
@@ -30405,12 +30424,12 @@ async function loginWithBrowser() {
|
|
|
30405
30424
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
30406
30425
|
res.end(htmlPage("Logged in to Preflight!", "You can close this tab and return to your terminal."));
|
|
30407
30426
|
server.close();
|
|
30408
|
-
|
|
30427
|
+
resolve4({ email });
|
|
30409
30428
|
} catch (err) {
|
|
30410
30429
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
30411
30430
|
res.end(htmlPage("Login failed", "Could not process authentication tokens."));
|
|
30412
30431
|
server.close();
|
|
30413
|
-
|
|
30432
|
+
resolve4(null);
|
|
30414
30433
|
}
|
|
30415
30434
|
} else {
|
|
30416
30435
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
@@ -30423,7 +30442,7 @@ async function loginWithBrowser() {
|
|
|
30423
30442
|
} else {
|
|
30424
30443
|
console.error(`Server error: ${err.message}`);
|
|
30425
30444
|
}
|
|
30426
|
-
|
|
30445
|
+
resolve4(null);
|
|
30427
30446
|
});
|
|
30428
30447
|
server.listen(CALLBACK_PORT, () => {
|
|
30429
30448
|
const redirectTo = encodeURIComponent(`http://localhost:${CALLBACK_PORT}/callback`);
|
|
@@ -30432,7 +30451,7 @@ async function loginWithBrowser() {
|
|
|
30432
30451
|
});
|
|
30433
30452
|
setTimeout(() => {
|
|
30434
30453
|
server.close();
|
|
30435
|
-
|
|
30454
|
+
resolve4(null);
|
|
30436
30455
|
}, 5 * 60 * 1e3);
|
|
30437
30456
|
});
|
|
30438
30457
|
}
|
|
@@ -32282,11 +32301,66 @@ function ora(options) {
|
|
|
32282
32301
|
return new Ora(options);
|
|
32283
32302
|
}
|
|
32284
32303
|
|
|
32304
|
+
// src/ui/theme.ts
|
|
32305
|
+
init_esm_shims();
|
|
32306
|
+
var brand = source_default.bold.hex("#E8700A");
|
|
32307
|
+
var brandDim = source_default.hex("#E8700A");
|
|
32308
|
+
var heading = source_default.bold.white;
|
|
32309
|
+
var subtext = source_default.dim;
|
|
32310
|
+
var muted = source_default.gray;
|
|
32311
|
+
var ok = source_default.green;
|
|
32312
|
+
var okBold = source_default.bold.green;
|
|
32313
|
+
var warning = source_default.yellow;
|
|
32314
|
+
var warningBold = source_default.bold.yellow;
|
|
32315
|
+
var critical = source_default.red;
|
|
32316
|
+
var criticalBold = source_default.bold.red;
|
|
32317
|
+
var info = source_default.hex("#E8700A");
|
|
32318
|
+
var infoBold = source_default.bold.hex("#E8700A");
|
|
32319
|
+
var icons = {
|
|
32320
|
+
check: ok("\u2714"),
|
|
32321
|
+
cross: critical("\u2716"),
|
|
32322
|
+
warn: warning("!"),
|
|
32323
|
+
info: info("i"),
|
|
32324
|
+
bullet: "\u25CF",
|
|
32325
|
+
circle: "\u25CB",
|
|
32326
|
+
arrow: "\u2192",
|
|
32327
|
+
block: "\u2588",
|
|
32328
|
+
blockDim: source_default.dim("\u2591"),
|
|
32329
|
+
file: "\u{1F4C4}",
|
|
32330
|
+
image: "\u{1F5BC}",
|
|
32331
|
+
plane: "\u{1F6EB}"
|
|
32332
|
+
};
|
|
32333
|
+
function scoreBar(score, width = 20) {
|
|
32334
|
+
const filled = Math.round(score / 100 * width);
|
|
32335
|
+
const empty = width - filled;
|
|
32336
|
+
let color;
|
|
32337
|
+
let label;
|
|
32338
|
+
if (score >= 80) {
|
|
32339
|
+
color = ok;
|
|
32340
|
+
label = "READY";
|
|
32341
|
+
} else if (score >= 60) {
|
|
32342
|
+
color = warning;
|
|
32343
|
+
label = "NEEDS ATTENTION";
|
|
32344
|
+
} else {
|
|
32345
|
+
color = critical;
|
|
32346
|
+
label = "AT RISK";
|
|
32347
|
+
}
|
|
32348
|
+
return `${score}/100 ${color(icons.block.repeat(filled))}${icons.blockDim.repeat(empty)} ${color(label)}`;
|
|
32349
|
+
}
|
|
32350
|
+
function formatBytes(bytes) {
|
|
32351
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
32352
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
32353
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
32354
|
+
}
|
|
32355
|
+
var APP_VERSION = "0.2.0";
|
|
32356
|
+
var APP_NAME = "Preflight";
|
|
32357
|
+
var APP_TAGLINE = "App Store Review Scanner";
|
|
32358
|
+
|
|
32285
32359
|
// src/ui/spinner.ts
|
|
32286
32360
|
function createSpinner(text2) {
|
|
32287
32361
|
return ora({
|
|
32288
32362
|
text: text2,
|
|
32289
|
-
color: "
|
|
32363
|
+
color: "yellow",
|
|
32290
32364
|
spinner: "dots"
|
|
32291
32365
|
});
|
|
32292
32366
|
}
|
|
@@ -32673,6 +32747,76 @@ function D(t2, e, s) {
|
|
|
32673
32747
|
const i = t2 + e, r = Math.max(s.length - 1, 0), n = i < 0 ? r : i > r ? 0 : i;
|
|
32674
32748
|
return s[n].disabled ? D(n, e < 0 ? -1 : 1, s) : n;
|
|
32675
32749
|
}
|
|
32750
|
+
var Mt = class extends x {
|
|
32751
|
+
options;
|
|
32752
|
+
cursor = 0;
|
|
32753
|
+
get _value() {
|
|
32754
|
+
return this.options[this.cursor].value;
|
|
32755
|
+
}
|
|
32756
|
+
get _enabledOptions() {
|
|
32757
|
+
return this.options.filter((e) => e.disabled !== true);
|
|
32758
|
+
}
|
|
32759
|
+
toggleAll() {
|
|
32760
|
+
const e = this._enabledOptions, s = this.value !== void 0 && this.value.length === e.length;
|
|
32761
|
+
this.value = s ? [] : e.map((i) => i.value);
|
|
32762
|
+
}
|
|
32763
|
+
toggleInvert() {
|
|
32764
|
+
const e = this.value;
|
|
32765
|
+
if (!e) return;
|
|
32766
|
+
const s = this._enabledOptions.filter((i) => !e.includes(i.value));
|
|
32767
|
+
this.value = s.map((i) => i.value);
|
|
32768
|
+
}
|
|
32769
|
+
toggleValue() {
|
|
32770
|
+
this.value === void 0 && (this.value = []);
|
|
32771
|
+
const e = this.value.includes(this._value);
|
|
32772
|
+
this.value = e ? this.value.filter((s) => s !== this._value) : [...this.value, this._value];
|
|
32773
|
+
}
|
|
32774
|
+
constructor(e) {
|
|
32775
|
+
super(e, false), this.options = e.options, this.value = [...e.initialValues ?? []];
|
|
32776
|
+
const s = Math.max(this.options.findIndex(({ value: i }) => i === e.cursorAt), 0);
|
|
32777
|
+
this.cursor = this.options[s].disabled ? D(s, 1, this.options) : s, this.on("key", (i) => {
|
|
32778
|
+
i === "a" && this.toggleAll(), i === "i" && this.toggleInvert();
|
|
32779
|
+
}), this.on("cursor", (i) => {
|
|
32780
|
+
switch (i) {
|
|
32781
|
+
case "left":
|
|
32782
|
+
case "up":
|
|
32783
|
+
this.cursor = D(this.cursor, -1, this.options);
|
|
32784
|
+
break;
|
|
32785
|
+
case "down":
|
|
32786
|
+
case "right":
|
|
32787
|
+
this.cursor = D(this.cursor, 1, this.options);
|
|
32788
|
+
break;
|
|
32789
|
+
case "space":
|
|
32790
|
+
this.toggleValue();
|
|
32791
|
+
break;
|
|
32792
|
+
}
|
|
32793
|
+
});
|
|
32794
|
+
}
|
|
32795
|
+
};
|
|
32796
|
+
var Lt = class extends x {
|
|
32797
|
+
_mask = "\u2022";
|
|
32798
|
+
get cursor() {
|
|
32799
|
+
return this._cursor;
|
|
32800
|
+
}
|
|
32801
|
+
get masked() {
|
|
32802
|
+
return this.userInput.replaceAll(/./g, this._mask);
|
|
32803
|
+
}
|
|
32804
|
+
get userInputWithCursor() {
|
|
32805
|
+
if (this.state === "submit" || this.state === "cancel") return this.masked;
|
|
32806
|
+
const e = this.userInput;
|
|
32807
|
+
if (this.cursor >= e.length) return `${this.masked}${import_picocolors.default.inverse(import_picocolors.default.hidden("_"))}`;
|
|
32808
|
+
const s = this.masked, i = s.slice(0, this.cursor), r = s.slice(this.cursor);
|
|
32809
|
+
return `${i}${import_picocolors.default.inverse(r[0])}${r.slice(1)}`;
|
|
32810
|
+
}
|
|
32811
|
+
clear() {
|
|
32812
|
+
this._clearUserInput();
|
|
32813
|
+
}
|
|
32814
|
+
constructor({ mask: e, ...s }) {
|
|
32815
|
+
super(s), this._mask = e ?? "\u2022", this.on("userInput", (i) => {
|
|
32816
|
+
this._setValue(i);
|
|
32817
|
+
});
|
|
32818
|
+
}
|
|
32819
|
+
};
|
|
32676
32820
|
var Wt = class extends x {
|
|
32677
32821
|
options;
|
|
32678
32822
|
cursor = 0;
|
|
@@ -33042,6 +33186,105 @@ ${import_picocolors2.default.gray(x2)} ${e}
|
|
|
33042
33186
|
|
|
33043
33187
|
`);
|
|
33044
33188
|
};
|
|
33189
|
+
var Q2 = (e, r) => e.split(`
|
|
33190
|
+
`).map((s) => r(s)).join(`
|
|
33191
|
+
`);
|
|
33192
|
+
var Lt2 = (e) => {
|
|
33193
|
+
const r = (i, n) => {
|
|
33194
|
+
const o = i.label ?? String(i.value);
|
|
33195
|
+
return n === "disabled" ? `${import_picocolors2.default.gray(z2)} ${Q2(o, (u) => import_picocolors2.default.strikethrough(import_picocolors2.default.gray(u)))}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint ?? "disabled"})`)}` : ""}` : n === "active" ? `${import_picocolors2.default.cyan(te)} ${o}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint})`)}` : ""}` : n === "selected" ? `${import_picocolors2.default.green(G2)} ${Q2(o, import_picocolors2.default.dim)}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint})`)}` : ""}` : n === "cancelled" ? `${Q2(o, (u) => import_picocolors2.default.strikethrough(import_picocolors2.default.dim(u)))}` : n === "active-selected" ? `${import_picocolors2.default.green(G2)} ${o}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint})`)}` : ""}` : n === "submitted" ? `${Q2(o, import_picocolors2.default.dim)}` : `${import_picocolors2.default.dim(z2)} ${Q2(o, import_picocolors2.default.dim)}`;
|
|
33196
|
+
}, s = e.required ?? true;
|
|
33197
|
+
return new Mt({ options: e.options, signal: e.signal, input: e.input, output: e.output, initialValues: e.initialValues, required: s, cursorAt: e.cursorAt, validate(i) {
|
|
33198
|
+
if (s && (i === void 0 || i.length === 0)) return `Please select at least one option.
|
|
33199
|
+
${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
|
|
33200
|
+
}, render() {
|
|
33201
|
+
const i = Bt(e.output, e.message, `${Ee(this.state)} `, `${N2(this.state)} `), n = `${import_picocolors2.default.gray(h)}
|
|
33202
|
+
${i}
|
|
33203
|
+
`, o = this.value ?? [], u = (l, a) => {
|
|
33204
|
+
if (l.disabled) return r(l, "disabled");
|
|
33205
|
+
const d = o.includes(l.value);
|
|
33206
|
+
return a && d ? r(l, "active-selected") : d ? r(l, "selected") : r(l, a ? "active" : "inactive");
|
|
33207
|
+
};
|
|
33208
|
+
switch (this.state) {
|
|
33209
|
+
case "submit": {
|
|
33210
|
+
const l = this.options.filter(({ value: d }) => o.includes(d)).map((d) => r(d, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none"), a = Bt(e.output, l, `${import_picocolors2.default.gray(h)} `);
|
|
33211
|
+
return `${n}${a}`;
|
|
33212
|
+
}
|
|
33213
|
+
case "cancel": {
|
|
33214
|
+
const l = this.options.filter(({ value: d }) => o.includes(d)).map((d) => r(d, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
33215
|
+
if (l.trim() === "") return `${n}${import_picocolors2.default.gray(h)}`;
|
|
33216
|
+
const a = Bt(e.output, l, `${import_picocolors2.default.gray(h)} `);
|
|
33217
|
+
return `${n}${a}
|
|
33218
|
+
${import_picocolors2.default.gray(h)}`;
|
|
33219
|
+
}
|
|
33220
|
+
case "error": {
|
|
33221
|
+
const l = `${import_picocolors2.default.yellow(h)} `, a = this.error.split(`
|
|
33222
|
+
`).map((E, p) => p === 0 ? `${import_picocolors2.default.yellow(x2)} ${import_picocolors2.default.yellow(E)}` : ` ${E}`).join(`
|
|
33223
|
+
`), d = n.split(`
|
|
33224
|
+
`).length, g = a.split(`
|
|
33225
|
+
`).length + 1;
|
|
33226
|
+
return `${n}${l}${J2({ output: e.output, options: this.options, cursor: this.cursor, maxItems: e.maxItems, columnPadding: l.length, rowPadding: d + g, style: u }).join(`
|
|
33227
|
+
${l}`)}
|
|
33228
|
+
${a}
|
|
33229
|
+
`;
|
|
33230
|
+
}
|
|
33231
|
+
default: {
|
|
33232
|
+
const l = `${import_picocolors2.default.cyan(h)} `, a = n.split(`
|
|
33233
|
+
`).length;
|
|
33234
|
+
return `${n}${l}${J2({ output: e.output, options: this.options, cursor: this.cursor, maxItems: e.maxItems, columnPadding: l.length, rowPadding: a + 2, style: u }).join(`
|
|
33235
|
+
${l}`)}
|
|
33236
|
+
${import_picocolors2.default.cyan(x2)}
|
|
33237
|
+
`;
|
|
33238
|
+
}
|
|
33239
|
+
}
|
|
33240
|
+
} }).prompt();
|
|
33241
|
+
};
|
|
33242
|
+
var jt = (e) => import_picocolors2.default.dim(e);
|
|
33243
|
+
var Vt2 = (e, r, s) => {
|
|
33244
|
+
const i = { hard: true, trim: false }, n = q2(e, r, i).split(`
|
|
33245
|
+
`), o = n.reduce((a, d) => Math.max(M2(d), a), 0), u = n.map(s).reduce((a, d) => Math.max(M2(d), a), 0), l = r - (u - o);
|
|
33246
|
+
return q2(e, l, i);
|
|
33247
|
+
};
|
|
33248
|
+
var kt2 = (e = "", r = "", s) => {
|
|
33249
|
+
const i = s?.output ?? P2.stdout, n = (s?.withGuide ?? _.withGuide) !== false, o = s?.format ?? jt, u = ["", ...Vt2(e, rt(i) - 6, o).split(`
|
|
33250
|
+
`).map(o), ""], l = M2(r), a = Math.max(u.reduce((p, y2) => {
|
|
33251
|
+
const $ = M2(y2);
|
|
33252
|
+
return $ > p ? $ : p;
|
|
33253
|
+
}, 0), l) + 2, d = u.map((p) => `${import_picocolors2.default.gray(h)} ${p}${" ".repeat(a - M2(p))}${import_picocolors2.default.gray(h)}`).join(`
|
|
33254
|
+
`), g = n ? `${import_picocolors2.default.gray(h)}
|
|
33255
|
+
` : "", E = n ? Ne : pe;
|
|
33256
|
+
i.write(`${g}${import_picocolors2.default.green(k2)} ${import_picocolors2.default.reset(r)} ${import_picocolors2.default.gray(se.repeat(Math.max(a - l - 1, 1)) + he)}
|
|
33257
|
+
${d}
|
|
33258
|
+
${import_picocolors2.default.gray(E + se.repeat(a + 2) + me)}
|
|
33259
|
+
`);
|
|
33260
|
+
};
|
|
33261
|
+
var Gt = (e) => new Lt({ validate: e.validate, mask: e.mask ?? Pe, signal: e.signal, input: e.input, output: e.output, render() {
|
|
33262
|
+
const r = `${import_picocolors2.default.gray(h)}
|
|
33263
|
+
${N2(this.state)} ${e.message}
|
|
33264
|
+
`, s = this.userInputWithCursor, i = this.masked;
|
|
33265
|
+
switch (this.state) {
|
|
33266
|
+
case "error": {
|
|
33267
|
+
const n = i ? ` ${i}` : "";
|
|
33268
|
+
return e.clearOnError && this.clear(), `${r.trim()}
|
|
33269
|
+
${import_picocolors2.default.yellow(h)}${n}
|
|
33270
|
+
${import_picocolors2.default.yellow(x2)} ${import_picocolors2.default.yellow(this.error)}
|
|
33271
|
+
`;
|
|
33272
|
+
}
|
|
33273
|
+
case "submit": {
|
|
33274
|
+
const n = i ? ` ${import_picocolors2.default.dim(i)}` : "";
|
|
33275
|
+
return `${r}${import_picocolors2.default.gray(h)}${n}`;
|
|
33276
|
+
}
|
|
33277
|
+
case "cancel": {
|
|
33278
|
+
const n = i ? ` ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(i))}` : "";
|
|
33279
|
+
return `${r}${import_picocolors2.default.gray(h)}${n}${i ? `
|
|
33280
|
+
${import_picocolors2.default.gray(h)}` : ""}`;
|
|
33281
|
+
}
|
|
33282
|
+
default:
|
|
33283
|
+
return `${r}${import_picocolors2.default.cyan(h)} ${s}
|
|
33284
|
+
${import_picocolors2.default.cyan(x2)}
|
|
33285
|
+
`;
|
|
33286
|
+
}
|
|
33287
|
+
} }).prompt();
|
|
33045
33288
|
var Ut = import_picocolors2.default.magenta;
|
|
33046
33289
|
var Ie = ({ indicator: e = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: n, frames: o = ee ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: u = ee ? 80 : 120, signal: l, ...a } = {}) => {
|
|
33047
33290
|
const d = ue();
|
|
@@ -33172,61 +33415,6 @@ ${l}
|
|
|
33172
33415
|
}
|
|
33173
33416
|
} }).prompt();
|
|
33174
33417
|
|
|
33175
|
-
// src/ui/theme.ts
|
|
33176
|
-
init_esm_shims();
|
|
33177
|
-
var brand = source_default.bold.cyan;
|
|
33178
|
-
var brandDim = source_default.cyan;
|
|
33179
|
-
var heading = source_default.bold.white;
|
|
33180
|
-
var subtext = source_default.dim;
|
|
33181
|
-
var muted = source_default.gray;
|
|
33182
|
-
var ok = source_default.green;
|
|
33183
|
-
var okBold = source_default.bold.green;
|
|
33184
|
-
var warning = source_default.yellow;
|
|
33185
|
-
var warningBold = source_default.bold.yellow;
|
|
33186
|
-
var critical = source_default.red;
|
|
33187
|
-
var criticalBold = source_default.bold.red;
|
|
33188
|
-
var info = source_default.blue;
|
|
33189
|
-
var infoBold = source_default.bold.blue;
|
|
33190
|
-
var icons = {
|
|
33191
|
-
check: ok("\u2714"),
|
|
33192
|
-
cross: critical("\u2716"),
|
|
33193
|
-
warn: warning("!"),
|
|
33194
|
-
info: info("i"),
|
|
33195
|
-
bullet: "\u25CF",
|
|
33196
|
-
circle: "\u25CB",
|
|
33197
|
-
arrow: "\u2192",
|
|
33198
|
-
block: "\u2588",
|
|
33199
|
-
blockDim: source_default.dim("\u2591"),
|
|
33200
|
-
file: "\u{1F4C4}",
|
|
33201
|
-
image: "\u{1F5BC}",
|
|
33202
|
-
plane: "\u{1F6EB}"
|
|
33203
|
-
};
|
|
33204
|
-
function scoreBar(score, width = 20) {
|
|
33205
|
-
const filled = Math.round(score / 100 * width);
|
|
33206
|
-
const empty = width - filled;
|
|
33207
|
-
let color;
|
|
33208
|
-
let label;
|
|
33209
|
-
if (score >= 80) {
|
|
33210
|
-
color = ok;
|
|
33211
|
-
label = "READY";
|
|
33212
|
-
} else if (score >= 60) {
|
|
33213
|
-
color = warning;
|
|
33214
|
-
label = "NEEDS ATTENTION";
|
|
33215
|
-
} else {
|
|
33216
|
-
color = critical;
|
|
33217
|
-
label = "AT RISK";
|
|
33218
|
-
}
|
|
33219
|
-
return `${score}/100 ${color(icons.block.repeat(filled))}${icons.blockDim.repeat(empty)} ${color(label)}`;
|
|
33220
|
-
}
|
|
33221
|
-
function formatBytes(bytes) {
|
|
33222
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
33223
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
33224
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
33225
|
-
}
|
|
33226
|
-
var APP_VERSION = "0.2.0";
|
|
33227
|
-
var APP_NAME = "Preflight";
|
|
33228
|
-
var APP_TAGLINE = "App Store Review Scanner";
|
|
33229
|
-
|
|
33230
33418
|
// src/ui/report.ts
|
|
33231
33419
|
function renderReport(report, items) {
|
|
33232
33420
|
console.log();
|
|
@@ -33248,7 +33436,7 @@ function renderReport(report, items) {
|
|
|
33248
33436
|
for (const item of criticals) {
|
|
33249
33437
|
console.log(` ${critical(item.title)}`);
|
|
33250
33438
|
if (item.fix_suggestion) {
|
|
33251
|
-
console.log(` ${
|
|
33439
|
+
console.log(` ${brandDim(icons.arrow)} ${item.fix_suggestion}`);
|
|
33252
33440
|
}
|
|
33253
33441
|
if (item.description && item.description !== item.title) {
|
|
33254
33442
|
console.log(` ${subtext(item.description)}`);
|
|
@@ -33262,7 +33450,7 @@ function renderReport(report, items) {
|
|
|
33262
33450
|
for (const item of warnings) {
|
|
33263
33451
|
console.log(` ${warning(item.title)}`);
|
|
33264
33452
|
if (item.fix_suggestion) {
|
|
33265
|
-
console.log(` ${
|
|
33453
|
+
console.log(` ${brandDim(icons.arrow)} ${item.fix_suggestion}`);
|
|
33266
33454
|
}
|
|
33267
33455
|
console.log();
|
|
33268
33456
|
}
|
|
@@ -33312,9 +33500,10 @@ function renderReportJson(report, items) {
|
|
|
33312
33500
|
|
|
33313
33501
|
// src/lib/project-finder.ts
|
|
33314
33502
|
init_esm_shims();
|
|
33315
|
-
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
|
|
33316
|
-
import {
|
|
33317
|
-
import {
|
|
33503
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
33504
|
+
import { execFileSync } from "child_process";
|
|
33505
|
+
import { join as join2, basename as basename2, dirname, resolve as resolve2 } from "path";
|
|
33506
|
+
import { homedir as homedir2, platform } from "os";
|
|
33318
33507
|
|
|
33319
33508
|
// src/ui/interactive.ts
|
|
33320
33509
|
init_esm_shims();
|
|
@@ -33324,24 +33513,24 @@ function intro(title) {
|
|
|
33324
33513
|
R2.info(title);
|
|
33325
33514
|
}
|
|
33326
33515
|
}
|
|
33327
|
-
function showTagline() {
|
|
33328
|
-
R2.message(`${APP_TAGLINE}
|
|
33329
|
-
Catch rejection reasons before Apple does.`);
|
|
33330
|
-
}
|
|
33331
|
-
function brandSplash() {
|
|
33332
|
-
console.log();
|
|
33333
|
-
console.log(brand(` ${APP_NAME.split("").join(" ")}`));
|
|
33334
|
-
console.log();
|
|
33335
|
-
console.log(subtext(` ${APP_TAGLINE}`));
|
|
33336
|
-
console.log(subtext(` v${APP_VERSION}`));
|
|
33337
|
-
console.log();
|
|
33338
|
-
}
|
|
33339
33516
|
function outro(message) {
|
|
33340
33517
|
Wt2(message || "Done!");
|
|
33341
33518
|
}
|
|
33342
33519
|
function tip(message) {
|
|
33343
33520
|
console.log();
|
|
33344
|
-
console.log(subtext(`
|
|
33521
|
+
console.log(subtext(` Tip: ${message}`));
|
|
33522
|
+
console.log();
|
|
33523
|
+
}
|
|
33524
|
+
function renderHeader(email, credits) {
|
|
33525
|
+
clearScreen();
|
|
33526
|
+
console.log();
|
|
33527
|
+
console.log(brand(` ${APP_NAME.split("").map((c) => c.toUpperCase()).join(" ")}`));
|
|
33528
|
+
if (email) {
|
|
33529
|
+
const creditDisplay = credits !== void 0 ? credits < 100 ? source_default.yellow(`${credits} credits`) : `${credits} credits` : "";
|
|
33530
|
+
console.log(subtext(` ${email}${creditDisplay ? ` \xB7 ${creditDisplay}` : ""}`));
|
|
33531
|
+
} else {
|
|
33532
|
+
console.log(subtext(` ${APP_TAGLINE}`));
|
|
33533
|
+
}
|
|
33345
33534
|
console.log();
|
|
33346
33535
|
}
|
|
33347
33536
|
async function select(opts) {
|
|
@@ -33350,7 +33539,17 @@ async function select(opts) {
|
|
|
33350
33539
|
options: opts.options
|
|
33351
33540
|
});
|
|
33352
33541
|
if (Ct(result)) {
|
|
33353
|
-
|
|
33542
|
+
return null;
|
|
33543
|
+
}
|
|
33544
|
+
return result;
|
|
33545
|
+
}
|
|
33546
|
+
async function multiselect(opts) {
|
|
33547
|
+
const result = await Lt2({
|
|
33548
|
+
message: opts.message,
|
|
33549
|
+
options: opts.options,
|
|
33550
|
+
required: opts.required ?? false
|
|
33551
|
+
});
|
|
33552
|
+
if (Ct(result)) {
|
|
33354
33553
|
return null;
|
|
33355
33554
|
}
|
|
33356
33555
|
return result;
|
|
@@ -33358,7 +33557,6 @@ async function select(opts) {
|
|
|
33358
33557
|
async function confirm(message, initialValue = true) {
|
|
33359
33558
|
const result = await Mt2({ message, initialValue });
|
|
33360
33559
|
if (Ct(result)) {
|
|
33361
|
-
Pt("Cancelled.");
|
|
33362
33560
|
return null;
|
|
33363
33561
|
}
|
|
33364
33562
|
return result;
|
|
@@ -33366,7 +33564,13 @@ async function confirm(message, initialValue = true) {
|
|
|
33366
33564
|
async function text(opts) {
|
|
33367
33565
|
const result = await Qt(opts);
|
|
33368
33566
|
if (Ct(result)) {
|
|
33369
|
-
|
|
33567
|
+
return null;
|
|
33568
|
+
}
|
|
33569
|
+
return result;
|
|
33570
|
+
}
|
|
33571
|
+
async function password(opts) {
|
|
33572
|
+
const result = await Gt({ message: opts.message, validate: opts.validate });
|
|
33573
|
+
if (Ct(result)) {
|
|
33370
33574
|
return null;
|
|
33371
33575
|
}
|
|
33372
33576
|
return result;
|
|
@@ -33375,6 +33579,14 @@ function spinner() {
|
|
|
33375
33579
|
return Ie();
|
|
33376
33580
|
}
|
|
33377
33581
|
var log = R2;
|
|
33582
|
+
function clearScreen() {
|
|
33583
|
+
if (process.stdout.isTTY) {
|
|
33584
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
33585
|
+
}
|
|
33586
|
+
}
|
|
33587
|
+
function note(message, title) {
|
|
33588
|
+
kt2(message, title);
|
|
33589
|
+
}
|
|
33378
33590
|
|
|
33379
33591
|
// src/lib/project-finder.ts
|
|
33380
33592
|
var SEARCH_DIRS = [
|
|
@@ -33386,6 +33598,45 @@ var SEARCH_DIRS = [
|
|
|
33386
33598
|
"code",
|
|
33387
33599
|
"dev"
|
|
33388
33600
|
];
|
|
33601
|
+
var NOISE_PATTERNS = [
|
|
33602
|
+
"/Pods/",
|
|
33603
|
+
"/DerivedData/",
|
|
33604
|
+
"/Carthage/",
|
|
33605
|
+
"/build/",
|
|
33606
|
+
"/.build/",
|
|
33607
|
+
"/SourcePackages/",
|
|
33608
|
+
"/Library/Developer/",
|
|
33609
|
+
"/.Trash/"
|
|
33610
|
+
];
|
|
33611
|
+
function spotlightSearch() {
|
|
33612
|
+
if (platform() !== "darwin") return [];
|
|
33613
|
+
try {
|
|
33614
|
+
const output = execFileSync("mdfind", [
|
|
33615
|
+
'kMDItemFSName == "*.xcodeproj" || kMDItemFSName == "*.xcworkspace"'
|
|
33616
|
+
], { encoding: "utf-8", timeout: 5e3 });
|
|
33617
|
+
const lines = output.trim().split("\n").filter(Boolean);
|
|
33618
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33619
|
+
const projects = [];
|
|
33620
|
+
for (const fullPath of lines) {
|
|
33621
|
+
if (NOISE_PATTERNS.some((pattern) => fullPath.includes(pattern))) continue;
|
|
33622
|
+
const projectDir = dirname(fullPath);
|
|
33623
|
+
if (seen.has(projectDir)) continue;
|
|
33624
|
+
seen.add(projectDir);
|
|
33625
|
+
const type = fullPath.endsWith(".xcworkspace") ? "xcworkspace" : "xcodeproj";
|
|
33626
|
+
const name = basename2(fullPath).replace(/\.(xcodeproj|xcworkspace)$/, "");
|
|
33627
|
+
let modifiedAt;
|
|
33628
|
+
try {
|
|
33629
|
+
modifiedAt = statSync2(fullPath).mtimeMs;
|
|
33630
|
+
} catch {
|
|
33631
|
+
}
|
|
33632
|
+
projects.push({ name, path: projectDir, type, fullPath, modifiedAt });
|
|
33633
|
+
}
|
|
33634
|
+
projects.sort((a, b) => (b.modifiedAt ?? 0) - (a.modifiedAt ?? 0));
|
|
33635
|
+
return projects;
|
|
33636
|
+
} catch {
|
|
33637
|
+
return [];
|
|
33638
|
+
}
|
|
33639
|
+
}
|
|
33389
33640
|
function findXcodeProjects(extraDirs = []) {
|
|
33390
33641
|
const home = homedir2();
|
|
33391
33642
|
const projects = [];
|
|
@@ -33447,9 +33698,23 @@ function findProjectInDir(dir) {
|
|
|
33447
33698
|
}
|
|
33448
33699
|
return null;
|
|
33449
33700
|
}
|
|
33701
|
+
function browseWithFinder() {
|
|
33702
|
+
if (platform() !== "darwin") return null;
|
|
33703
|
+
try {
|
|
33704
|
+
const result = execFileSync("osascript", [
|
|
33705
|
+
"-e",
|
|
33706
|
+
'POSIX path of (choose folder with prompt "Select your Xcode project folder")'
|
|
33707
|
+
], { encoding: "utf-8", timeout: 12e4 });
|
|
33708
|
+
const path5 = result.trim();
|
|
33709
|
+
return path5.endsWith("/") ? path5.slice(0, -1) : path5;
|
|
33710
|
+
} catch {
|
|
33711
|
+
return null;
|
|
33712
|
+
}
|
|
33713
|
+
}
|
|
33450
33714
|
function buildProjectChoices(lastScannedPath) {
|
|
33451
33715
|
const choices = [];
|
|
33452
33716
|
const cwd = process.cwd();
|
|
33717
|
+
const addedPaths = /* @__PURE__ */ new Set();
|
|
33453
33718
|
if (lastScannedPath && existsSync2(lastScannedPath)) {
|
|
33454
33719
|
const proj = findProjectInDir(lastScannedPath);
|
|
33455
33720
|
if (proj) {
|
|
@@ -33458,24 +33723,37 @@ function buildProjectChoices(lastScannedPath) {
|
|
|
33458
33723
|
label: `${proj.name} (last scanned)`,
|
|
33459
33724
|
hint: shortenPath(lastScannedPath)
|
|
33460
33725
|
});
|
|
33726
|
+
addedPaths.add(lastScannedPath);
|
|
33461
33727
|
}
|
|
33462
33728
|
}
|
|
33463
33729
|
const cwdProj = findProjectInDir(cwd);
|
|
33464
|
-
if (cwdProj && cwd
|
|
33730
|
+
if (cwdProj && !addedPaths.has(cwd)) {
|
|
33465
33731
|
choices.push({
|
|
33466
33732
|
value: cwd,
|
|
33467
33733
|
label: cwdProj.name,
|
|
33468
|
-
hint: `Current directory
|
|
33734
|
+
hint: `Current directory`
|
|
33469
33735
|
});
|
|
33736
|
+
addedPaths.add(cwd);
|
|
33470
33737
|
}
|
|
33471
|
-
|
|
33472
|
-
|
|
33473
|
-
|
|
33738
|
+
let found = spotlightSearch();
|
|
33739
|
+
if (found.length === 0) {
|
|
33740
|
+
found = findXcodeProjects();
|
|
33741
|
+
}
|
|
33742
|
+
for (const proj of found.slice(0, 8)) {
|
|
33743
|
+
if (addedPaths.has(proj.path)) continue;
|
|
33474
33744
|
choices.push({
|
|
33475
33745
|
value: proj.path,
|
|
33476
33746
|
label: proj.name,
|
|
33477
33747
|
hint: shortenPath(proj.path)
|
|
33478
33748
|
});
|
|
33749
|
+
addedPaths.add(proj.path);
|
|
33750
|
+
}
|
|
33751
|
+
if (platform() === "darwin") {
|
|
33752
|
+
choices.push({
|
|
33753
|
+
value: "__finder__",
|
|
33754
|
+
label: "Open Finder to pick a folder",
|
|
33755
|
+
hint: "Browse with macOS file picker"
|
|
33756
|
+
});
|
|
33479
33757
|
}
|
|
33480
33758
|
choices.push({
|
|
33481
33759
|
value: "__manual__",
|
|
@@ -33486,15 +33764,20 @@ function buildProjectChoices(lastScannedPath) {
|
|
|
33486
33764
|
}
|
|
33487
33765
|
async function interactiveProjectSelect() {
|
|
33488
33766
|
const choices = buildProjectChoices(getLastScannedPath());
|
|
33489
|
-
|
|
33490
|
-
|
|
33491
|
-
|
|
33767
|
+
const realProjects = choices.filter((c) => !c.value.startsWith("__"));
|
|
33768
|
+
if (realProjects.length === 0) {
|
|
33769
|
+
log.info("No Xcode projects found on your Mac.");
|
|
33492
33770
|
}
|
|
33493
33771
|
const selected = await select({
|
|
33494
33772
|
message: "Where's your Xcode project?",
|
|
33495
33773
|
options: choices
|
|
33496
33774
|
});
|
|
33497
33775
|
if (selected === null) return null;
|
|
33776
|
+
if (selected === "__finder__") {
|
|
33777
|
+
const finderPath = browseWithFinder();
|
|
33778
|
+
if (!finderPath) return null;
|
|
33779
|
+
return finderPath;
|
|
33780
|
+
}
|
|
33498
33781
|
if (selected === "__manual__") return promptForManualPath();
|
|
33499
33782
|
return selected;
|
|
33500
33783
|
}
|
|
@@ -33504,6 +33787,8 @@ async function promptForManualPath() {
|
|
|
33504
33787
|
placeholder: "./MyApp",
|
|
33505
33788
|
validate: (val) => {
|
|
33506
33789
|
if (!val?.trim()) return "Path is required";
|
|
33790
|
+
const resolved = resolve2(val.trim());
|
|
33791
|
+
if (!existsSync2(resolved)) return "Directory not found";
|
|
33507
33792
|
}
|
|
33508
33793
|
});
|
|
33509
33794
|
}
|
|
@@ -33527,7 +33812,8 @@ var KNOWN_COMMANDS = [
|
|
|
33527
33812
|
{ name: "status", description: "Check analysis status" },
|
|
33528
33813
|
{ name: "report", description: "View analysis report" },
|
|
33529
33814
|
{ name: "history", description: "List past submissions" },
|
|
33530
|
-
{ name: "setup", description: "Run guided setup" }
|
|
33815
|
+
{ name: "setup", description: "Run guided setup" },
|
|
33816
|
+
{ name: "asc", description: "App Store Connect integration" }
|
|
33531
33817
|
];
|
|
33532
33818
|
function levenshtein(a, b) {
|
|
33533
33819
|
const matrix = [];
|
|
@@ -33568,7 +33854,7 @@ function handleUnknownCommand(cmdName) {
|
|
|
33568
33854
|
console.log();
|
|
33569
33855
|
console.log(source_default.dim(" Did you mean:"));
|
|
33570
33856
|
for (const s of suggestions) {
|
|
33571
|
-
console.log(` ${
|
|
33857
|
+
console.log(` ${brandDim("\u2192")} ${brand(`preflight ${s.name}`)} ${source_default.dim(s.description)}`);
|
|
33572
33858
|
}
|
|
33573
33859
|
}
|
|
33574
33860
|
console.log();
|
|
@@ -33590,14 +33876,532 @@ async function promptLogin() {
|
|
|
33590
33876
|
return result === "login";
|
|
33591
33877
|
}
|
|
33592
33878
|
|
|
33879
|
+
// src/lib/submission-questions.ts
|
|
33880
|
+
init_esm_shims();
|
|
33881
|
+
var CATEGORIES = [
|
|
33882
|
+
"Business",
|
|
33883
|
+
"Developer Tools",
|
|
33884
|
+
"Education",
|
|
33885
|
+
"Finance",
|
|
33886
|
+
"Health & Fitness",
|
|
33887
|
+
"Lifestyle",
|
|
33888
|
+
"Productivity",
|
|
33889
|
+
"Social Networking",
|
|
33890
|
+
"Utilities"
|
|
33891
|
+
];
|
|
33892
|
+
var AGE_RATING_CONTENT_TYPES = [
|
|
33893
|
+
{ value: "cartoonViolence", label: "Cartoon violence", hint: "e.g., Tom & Jerry style" },
|
|
33894
|
+
{ value: "realisticViolence", label: "Realistic violence", hint: "e.g., combat games" },
|
|
33895
|
+
{ value: "prolongedViolence", label: "Graphic/intense violence", hint: "e.g., gore, torture" },
|
|
33896
|
+
{ value: "sexualContent", label: "Sexual content or nudity", hint: "e.g., explicit images" },
|
|
33897
|
+
{ value: "matureSuggestive", label: "Mature or suggestive themes", hint: "e.g., dating, romance" },
|
|
33898
|
+
{ value: "profanity", label: "Swearing or crude humor", hint: "e.g., curse words" },
|
|
33899
|
+
{ value: "alcoholDrugs", label: "Alcohol, tobacco, or drugs", hint: "e.g., drinking scenes" },
|
|
33900
|
+
{ value: "gamblingSimulated", label: "Gambling (no real money)", hint: "e.g., casino games with fake chips" },
|
|
33901
|
+
{ value: "horrorFear", label: "Horror or scary content", hint: "e.g., jump scares" },
|
|
33902
|
+
{ value: "medicalTreatment", label: "Medical or health advice", hint: "e.g., treatment suggestions" },
|
|
33903
|
+
{ value: "gamblingContests", label: "Real money gambling or paid contests", hint: "e.g., betting, fantasy sports" }
|
|
33904
|
+
];
|
|
33905
|
+
var PRIVACY_DATA_TYPES = [
|
|
33906
|
+
{ value: "contact", label: "Contact Info", hint: "Name, email, phone, address" },
|
|
33907
|
+
{ value: "health", label: "Health & Fitness", hint: "Workouts, steps, medical info" },
|
|
33908
|
+
{ value: "financial", label: "Financial Info", hint: "Credit cards, bank details" },
|
|
33909
|
+
{ value: "location", label: "Location", hint: "GPS, location data" },
|
|
33910
|
+
{ value: "sensitive", label: "Sensitive Info", hint: "Race, religion, politics" },
|
|
33911
|
+
{ value: "contacts", label: "Contacts", hint: "Phone contacts, address book" },
|
|
33912
|
+
{ value: "content", label: "User Content", hint: "Photos, videos, posts" },
|
|
33913
|
+
{ value: "browsing", label: "Browsing History", hint: "Websites visited" },
|
|
33914
|
+
{ value: "search", label: "Search History", hint: "In-app searches" },
|
|
33915
|
+
{ value: "identifiers", label: "Identifiers", hint: "User ID, device ID" },
|
|
33916
|
+
{ value: "purchases", label: "Purchases", hint: "Purchase history" },
|
|
33917
|
+
{ value: "usage", label: "Usage Data", hint: "Button taps, feature usage" },
|
|
33918
|
+
{ value: "diagnostics", label: "Diagnostics", hint: "Crash reports (Firebase, Sentry)" },
|
|
33919
|
+
{ value: "other", label: "Other Data", hint: "Anything else not listed" }
|
|
33920
|
+
];
|
|
33921
|
+
var FEATURE_ITEMS = [
|
|
33922
|
+
{ value: "ugc", label: "User Posts & Uploads", hint: "Comments, photos, sharing" },
|
|
33923
|
+
{ value: "login", label: "Account / Login", hint: "Sign up or log in required" },
|
|
33924
|
+
{ value: "iap", label: "Pay to Unlock Features", hint: "One-time purchases" },
|
|
33925
|
+
{ value: "subscriptions", label: "Subscription / Recurring Payment", hint: "Weekly, monthly, yearly" },
|
|
33926
|
+
{ value: "ads", label: "Ads in Your App", hint: "Banner, video, or sponsored" },
|
|
33927
|
+
{ value: "thirdPartyLogin", label: "Sign in with Apple / Google", hint: "Social login" },
|
|
33928
|
+
{ value: "aiContent", label: "AI-Generated Content", hint: "ChatGPT, DALL-E, etc." },
|
|
33929
|
+
{ value: "healthClaims", label: "Health / Medical Advice", hint: "Diagnosis, treatment" },
|
|
33930
|
+
{ value: "crypto", label: "Crypto / NFTs", hint: "Buy, sell, trade" },
|
|
33931
|
+
{ value: "miniApps", label: "Mini Apps / Plugins", hint: "Hosts mini games, chatbots, or plugins" },
|
|
33932
|
+
{ value: "euDistribution", label: "Available in the EU", hint: "Distributed on EU App Store" },
|
|
33933
|
+
{ value: "externalPayments", label: "External Payment Links (US)", hint: "Links to pay outside Apple" }
|
|
33934
|
+
];
|
|
33935
|
+
function calculateAgeRating(answers) {
|
|
33936
|
+
if (answers.prolongedViolence === 2 || answers.sexualContent === 2 || answers.gamblingSimulated === 2 || answers.gamblingContests > 0) {
|
|
33937
|
+
return "17+";
|
|
33938
|
+
}
|
|
33939
|
+
if (answers.realisticViolence > 0 || answers.sexualContent > 0 || answers.matureSuggestive === 2 || answers.alcoholDrugs === 2 || answers.gamblingSimulated > 0) {
|
|
33940
|
+
return "12+";
|
|
33941
|
+
}
|
|
33942
|
+
if (answers.cartoonViolence === 2 || answers.matureSuggestive > 0 || answers.profanity === 2 || answers.horrorFear === 2) {
|
|
33943
|
+
return "9+";
|
|
33944
|
+
}
|
|
33945
|
+
return "4+";
|
|
33946
|
+
}
|
|
33947
|
+
async function collectAppDetails(projectName, defaults) {
|
|
33948
|
+
if (!defaults) {
|
|
33949
|
+
const skipGate = await select({
|
|
33950
|
+
message: "App Details (you can always add these later on the web)",
|
|
33951
|
+
options: [
|
|
33952
|
+
{ value: "fill", label: "Fill in now", hint: "Name, description, keywords, category" },
|
|
33953
|
+
{ value: "skip", label: "Skip for now", hint: "Just use the project name" }
|
|
33954
|
+
]
|
|
33955
|
+
});
|
|
33956
|
+
if (skipGate === null) return null;
|
|
33957
|
+
if (skipGate === "skip") {
|
|
33958
|
+
return {
|
|
33959
|
+
appName: projectName,
|
|
33960
|
+
signInRequired: false
|
|
33961
|
+
};
|
|
33962
|
+
}
|
|
33963
|
+
}
|
|
33964
|
+
const defaultName = defaults?.appName || projectName;
|
|
33965
|
+
const appName = await text({
|
|
33966
|
+
message: "App Name",
|
|
33967
|
+
placeholder: defaultName,
|
|
33968
|
+
defaultValue: defaultName,
|
|
33969
|
+
validate: (val) => {
|
|
33970
|
+
if (!val?.trim()) return "App name is required";
|
|
33971
|
+
}
|
|
33972
|
+
});
|
|
33973
|
+
if (appName === null) return null;
|
|
33974
|
+
const description = await text({
|
|
33975
|
+
message: "Description (press Enter to skip)",
|
|
33976
|
+
placeholder: "Describe your app as it appears in the App Store",
|
|
33977
|
+
...defaults?.description ? { defaultValue: defaults.description } : {}
|
|
33978
|
+
});
|
|
33979
|
+
if (description === null) return null;
|
|
33980
|
+
const keywords = await text({
|
|
33981
|
+
message: "Keywords (press Enter to skip)",
|
|
33982
|
+
placeholder: "Comma-separated, 100 chars max",
|
|
33983
|
+
...defaults?.keywords ? { defaultValue: defaults.keywords } : {},
|
|
33984
|
+
validate: (val) => {
|
|
33985
|
+
if (val && val.length > 100) return "Keywords must be 100 characters or less";
|
|
33986
|
+
}
|
|
33987
|
+
});
|
|
33988
|
+
if (keywords === null) return null;
|
|
33989
|
+
const promotionalText = await text({
|
|
33990
|
+
message: "Promotional Text (press Enter to skip)",
|
|
33991
|
+
placeholder: "Short promotional text, 170 chars max",
|
|
33992
|
+
...defaults?.promotionalText ? { defaultValue: defaults.promotionalText } : {},
|
|
33993
|
+
validate: (val) => {
|
|
33994
|
+
if (val && val.length > 170) return "Promotional text must be 170 characters or less";
|
|
33995
|
+
}
|
|
33996
|
+
});
|
|
33997
|
+
if (promotionalText === null) return null;
|
|
33998
|
+
const categoryOptions = [
|
|
33999
|
+
{ value: "__skip__", label: "Skip", hint: "Choose later" },
|
|
34000
|
+
...CATEGORIES.map((c) => ({ value: c, label: c }))
|
|
34001
|
+
];
|
|
34002
|
+
const category = await select({
|
|
34003
|
+
message: "Primary Category",
|
|
34004
|
+
options: categoryOptions,
|
|
34005
|
+
...defaults?.category ? { initialValue: defaults.category } : {}
|
|
34006
|
+
});
|
|
34007
|
+
if (category === null) return null;
|
|
34008
|
+
const supportUrl = await text({
|
|
34009
|
+
message: "Support URL (press Enter to skip)",
|
|
34010
|
+
placeholder: "https://example.com/support",
|
|
34011
|
+
...defaults?.supportUrl ? { defaultValue: defaults.supportUrl } : {}
|
|
34012
|
+
});
|
|
34013
|
+
if (supportUrl === null) return null;
|
|
34014
|
+
const marketingUrl = await text({
|
|
34015
|
+
message: "Marketing URL (press Enter to skip)",
|
|
34016
|
+
placeholder: "https://example.com",
|
|
34017
|
+
...defaults?.marketingUrl ? { defaultValue: defaults.marketingUrl } : {}
|
|
34018
|
+
});
|
|
34019
|
+
if (marketingUrl === null) return null;
|
|
34020
|
+
const signInRequired = await confirm(
|
|
34021
|
+
"Does your app require sign-in for review?",
|
|
34022
|
+
defaults?.signInRequired ?? false
|
|
34023
|
+
);
|
|
34024
|
+
if (signInRequired === null) return null;
|
|
34025
|
+
let demoUsername;
|
|
34026
|
+
let demoPassword;
|
|
34027
|
+
if (signInRequired) {
|
|
34028
|
+
const email = await text({
|
|
34029
|
+
message: "Demo Email",
|
|
34030
|
+
placeholder: "test@example.com",
|
|
34031
|
+
...defaults?.demoUsername ? { defaultValue: defaults.demoUsername } : {},
|
|
34032
|
+
validate: (val) => {
|
|
34033
|
+
if (!val?.trim()) return "Demo email is required when sign-in is required";
|
|
34034
|
+
}
|
|
34035
|
+
});
|
|
34036
|
+
if (email === null) return null;
|
|
34037
|
+
demoUsername = email;
|
|
34038
|
+
const pass = await password({
|
|
34039
|
+
message: "Demo Password",
|
|
34040
|
+
validate: (val) => {
|
|
34041
|
+
if (!val?.trim()) return "Demo password is required when sign-in is required";
|
|
34042
|
+
}
|
|
34043
|
+
});
|
|
34044
|
+
if (pass === null) return null;
|
|
34045
|
+
demoPassword = pass;
|
|
34046
|
+
}
|
|
34047
|
+
return {
|
|
34048
|
+
appName: appName.trim(),
|
|
34049
|
+
description: description?.trim() || void 0,
|
|
34050
|
+
keywords: keywords?.trim() || void 0,
|
|
34051
|
+
category: category === "__skip__" ? void 0 : category,
|
|
34052
|
+
supportUrl: supportUrl?.trim() || void 0,
|
|
34053
|
+
promotionalText: promotionalText?.trim() || void 0,
|
|
34054
|
+
marketingUrl: marketingUrl?.trim() || void 0,
|
|
34055
|
+
signInRequired,
|
|
34056
|
+
demoUsername,
|
|
34057
|
+
demoPassword
|
|
34058
|
+
};
|
|
34059
|
+
}
|
|
34060
|
+
async function collectAgeRating() {
|
|
34061
|
+
log.step(subtext("Step 1 of 3: Age Rating"));
|
|
34062
|
+
const defaultAnswers = {
|
|
34063
|
+
cartoonViolence: 0,
|
|
34064
|
+
realisticViolence: 0,
|
|
34065
|
+
prolongedViolence: 0,
|
|
34066
|
+
sexualContent: 0,
|
|
34067
|
+
matureSuggestive: 0,
|
|
34068
|
+
profanity: 0,
|
|
34069
|
+
alcoholDrugs: 0,
|
|
34070
|
+
gamblingSimulated: 0,
|
|
34071
|
+
horrorFear: 0,
|
|
34072
|
+
medicalTreatment: 0,
|
|
34073
|
+
gamblingContests: 0,
|
|
34074
|
+
unrestrictedWebAccess: false,
|
|
34075
|
+
madeForKids: false
|
|
34076
|
+
};
|
|
34077
|
+
const hasMatureContent = await confirm(
|
|
34078
|
+
"Does your app contain any mature content? (violence, sexual content, drugs, gambling, horror)",
|
|
34079
|
+
false
|
|
34080
|
+
);
|
|
34081
|
+
if (hasMatureContent === null) return null;
|
|
34082
|
+
if (!hasMatureContent) {
|
|
34083
|
+
const rating = calculateAgeRating(defaultAnswers);
|
|
34084
|
+
log.success(`Age Rating: ${rating} (no mature content)`);
|
|
34085
|
+
const looksRight = await confirm("Does that look right?", true);
|
|
34086
|
+
if (looksRight === null) return null;
|
|
34087
|
+
if (!looksRight) {
|
|
34088
|
+
return collectAgeRatingDetailed(defaultAnswers);
|
|
34089
|
+
}
|
|
34090
|
+
return { answers: defaultAnswers, rating };
|
|
34091
|
+
}
|
|
34092
|
+
return collectAgeRatingDetailed(defaultAnswers);
|
|
34093
|
+
}
|
|
34094
|
+
async function collectAgeRatingDetailed(answers) {
|
|
34095
|
+
const selectedTypes = await multiselect({
|
|
34096
|
+
message: "Which types of content does your app contain? (Space to select, Enter to confirm)",
|
|
34097
|
+
options: AGE_RATING_CONTENT_TYPES
|
|
34098
|
+
});
|
|
34099
|
+
if (selectedTypes === null) return null;
|
|
34100
|
+
const updatedAnswers = { ...answers };
|
|
34101
|
+
for (const typeKey of selectedTypes) {
|
|
34102
|
+
const typeInfo = AGE_RATING_CONTENT_TYPES.find((t2) => t2.value === typeKey);
|
|
34103
|
+
if (!typeInfo) continue;
|
|
34104
|
+
const severity = await select({
|
|
34105
|
+
message: `${typeInfo.label}: how much?`,
|
|
34106
|
+
options: [
|
|
34107
|
+
{ value: "1", label: "A little", hint: "Minor/occasional" },
|
|
34108
|
+
{ value: "2", label: "A lot", hint: "Frequent/prominent" }
|
|
34109
|
+
]
|
|
34110
|
+
});
|
|
34111
|
+
if (severity === null) return null;
|
|
34112
|
+
updatedAnswers[typeKey] = parseInt(severity);
|
|
34113
|
+
}
|
|
34114
|
+
const webAccess = await confirm("Can users browse any website in your app? (e.g., in-app browser)", false);
|
|
34115
|
+
if (webAccess === null) return null;
|
|
34116
|
+
updatedAnswers.unrestrictedWebAccess = webAccess;
|
|
34117
|
+
const madeForKids = await confirm("Is this app designed specifically for kids under 13?", false);
|
|
34118
|
+
if (madeForKids === null) return null;
|
|
34119
|
+
updatedAnswers.madeForKids = madeForKids;
|
|
34120
|
+
const rating = calculateAgeRating(updatedAnswers);
|
|
34121
|
+
log.success(`Age Rating: ${rating}`);
|
|
34122
|
+
return { answers: updatedAnswers, rating };
|
|
34123
|
+
}
|
|
34124
|
+
async function collectPrivacyData() {
|
|
34125
|
+
log.step(subtext("Step 2 of 3: Privacy & Data"));
|
|
34126
|
+
const collectsData = await confirm("Does your app collect any user data?", false);
|
|
34127
|
+
if (collectsData === null) return null;
|
|
34128
|
+
const emptyData = Object.fromEntries(
|
|
34129
|
+
PRIVACY_DATA_TYPES.map((t2) => [t2.value, { collected: false, linked: false }])
|
|
34130
|
+
);
|
|
34131
|
+
if (!collectsData) {
|
|
34132
|
+
return { data: emptyData, tracking: false };
|
|
34133
|
+
}
|
|
34134
|
+
const collectedTypes = await multiselect({
|
|
34135
|
+
message: "What data does your app collect? (Space to select, Enter to confirm)",
|
|
34136
|
+
options: PRIVACY_DATA_TYPES
|
|
34137
|
+
});
|
|
34138
|
+
if (collectedTypes === null) return null;
|
|
34139
|
+
const data = { ...emptyData };
|
|
34140
|
+
for (const typeKey of collectedTypes) {
|
|
34141
|
+
data[typeKey] = { collected: true, linked: false };
|
|
34142
|
+
const linked = await confirm(
|
|
34143
|
+
`Can you tie ${PRIVACY_DATA_TYPES.find((t2) => t2.value === typeKey)?.label || typeKey} to a specific person? (e.g., through their account)`,
|
|
34144
|
+
false
|
|
34145
|
+
);
|
|
34146
|
+
if (linked === null) return null;
|
|
34147
|
+
data[typeKey].linked = linked;
|
|
34148
|
+
}
|
|
34149
|
+
const tracking = await confirm(
|
|
34150
|
+
"Does your app use data to track users across other companies' apps and websites?",
|
|
34151
|
+
false
|
|
34152
|
+
);
|
|
34153
|
+
if (tracking === null) return null;
|
|
34154
|
+
return { data, tracking };
|
|
34155
|
+
}
|
|
34156
|
+
async function collectFeatureChecklist() {
|
|
34157
|
+
log.step(subtext("Step 3 of 3: Features"));
|
|
34158
|
+
const selectedFeatures = await multiselect({
|
|
34159
|
+
message: "Which features does your app include? (Space to select, Enter to confirm)",
|
|
34160
|
+
options: FEATURE_ITEMS
|
|
34161
|
+
});
|
|
34162
|
+
if (selectedFeatures === null) return null;
|
|
34163
|
+
const checklist = {
|
|
34164
|
+
ugc: selectedFeatures.includes("ugc"),
|
|
34165
|
+
login: selectedFeatures.includes("login"),
|
|
34166
|
+
iap: selectedFeatures.includes("iap"),
|
|
34167
|
+
subscriptions: selectedFeatures.includes("subscriptions"),
|
|
34168
|
+
ads: selectedFeatures.includes("ads"),
|
|
34169
|
+
thirdPartyLogin: selectedFeatures.includes("thirdPartyLogin"),
|
|
34170
|
+
aiContent: selectedFeatures.includes("aiContent"),
|
|
34171
|
+
healthClaims: selectedFeatures.includes("healthClaims"),
|
|
34172
|
+
crypto: selectedFeatures.includes("crypto"),
|
|
34173
|
+
miniApps: selectedFeatures.includes("miniApps"),
|
|
34174
|
+
euDistribution: selectedFeatures.includes("euDistribution"),
|
|
34175
|
+
externalPayments: selectedFeatures.includes("externalPayments")
|
|
34176
|
+
};
|
|
34177
|
+
if (checklist.login) {
|
|
34178
|
+
const hasAccountDeletion = await confirm(
|
|
34179
|
+
"Does your app have an account deletion button? (Apple requires this!)",
|
|
34180
|
+
false
|
|
34181
|
+
);
|
|
34182
|
+
if (hasAccountDeletion === null) return null;
|
|
34183
|
+
checklist.accountDeletion = hasAccountDeletion;
|
|
34184
|
+
}
|
|
34185
|
+
if (checklist.iap || checklist.subscriptions) {
|
|
34186
|
+
const hasRestorePurchases = await confirm(
|
|
34187
|
+
'Does your app have a "Restore Purchases" button? (Apple requires this!)',
|
|
34188
|
+
false
|
|
34189
|
+
);
|
|
34190
|
+
if (hasRestorePurchases === null) return null;
|
|
34191
|
+
checklist.restorePurchases = hasRestorePurchases;
|
|
34192
|
+
}
|
|
34193
|
+
if (checklist.ugc) {
|
|
34194
|
+
const hasCreatorAgeGate = await confirm(
|
|
34195
|
+
"Do you verify content creators are 13+ (or local minimum age)?",
|
|
34196
|
+
false
|
|
34197
|
+
);
|
|
34198
|
+
if (hasCreatorAgeGate === null) return null;
|
|
34199
|
+
checklist.creatorAgeGate = hasCreatorAgeGate;
|
|
34200
|
+
}
|
|
34201
|
+
if (checklist.miniApps) {
|
|
34202
|
+
const miniAppsReviewed = await confirm(
|
|
34203
|
+
"Have all mini apps/plugins been individually submitted for Apple review?",
|
|
34204
|
+
false
|
|
34205
|
+
);
|
|
34206
|
+
if (miniAppsReviewed === null) return null;
|
|
34207
|
+
checklist.miniAppsReviewed = miniAppsReviewed;
|
|
34208
|
+
}
|
|
34209
|
+
if (checklist.euDistribution) {
|
|
34210
|
+
const euTraderDeclared = await confirm(
|
|
34211
|
+
"Have you declared your trader status in App Store Connect? (EU DSA requirement)",
|
|
34212
|
+
false
|
|
34213
|
+
);
|
|
34214
|
+
if (euTraderDeclared === null) return null;
|
|
34215
|
+
checklist.euTraderDeclared = euTraderDeclared;
|
|
34216
|
+
}
|
|
34217
|
+
if (checklist.externalPayments) {
|
|
34218
|
+
const externalLinkCompliant = await confirm(
|
|
34219
|
+
"Do you use StoreKit External Link Account API with Apple's disclosure sheet?",
|
|
34220
|
+
false
|
|
34221
|
+
);
|
|
34222
|
+
if (externalLinkCompliant === null) return null;
|
|
34223
|
+
checklist.externalLinkCompliant = externalLinkCompliant;
|
|
34224
|
+
}
|
|
34225
|
+
return checklist;
|
|
34226
|
+
}
|
|
34227
|
+
async function collectCompliance(defaults) {
|
|
34228
|
+
const ageResult = await collectAgeRating();
|
|
34229
|
+
if (ageResult === null) return null;
|
|
34230
|
+
const privacyResult = await collectPrivacyData();
|
|
34231
|
+
if (privacyResult === null) return null;
|
|
34232
|
+
const checklistResult = await collectFeatureChecklist();
|
|
34233
|
+
if (checklistResult === null) return null;
|
|
34234
|
+
return {
|
|
34235
|
+
ageRatingAnswers: ageResult.answers,
|
|
34236
|
+
ageRating: ageResult.rating,
|
|
34237
|
+
privacyDeclarations: privacyResult,
|
|
34238
|
+
checklist: checklistResult
|
|
34239
|
+
};
|
|
34240
|
+
}
|
|
34241
|
+
function formatComplianceForApi(compliance) {
|
|
34242
|
+
return {
|
|
34243
|
+
age_rating: compliance.ageRatingAnswers,
|
|
34244
|
+
age_rating_result: compliance.ageRating,
|
|
34245
|
+
privacy_declarations: {
|
|
34246
|
+
data: compliance.privacyDeclarations.data,
|
|
34247
|
+
tracking: compliance.privacyDeclarations.tracking
|
|
34248
|
+
},
|
|
34249
|
+
checklist: compliance.checklist
|
|
34250
|
+
};
|
|
34251
|
+
}
|
|
34252
|
+
function formatComplianceSummary(compliance) {
|
|
34253
|
+
const lines = [];
|
|
34254
|
+
lines.push(` Age Rating: ${compliance.ageRating}`);
|
|
34255
|
+
const collectedTypes = Object.entries(compliance.privacyDeclarations.data).filter(([_2, v]) => v.collected).map(([k3, _2]) => {
|
|
34256
|
+
const typeInfo = PRIVACY_DATA_TYPES.find((t2) => t2.value === k3);
|
|
34257
|
+
return typeInfo?.label || k3;
|
|
34258
|
+
});
|
|
34259
|
+
if (collectedTypes.length > 0) {
|
|
34260
|
+
lines.push(` Privacy: ${collectedTypes.join(", ")}`);
|
|
34261
|
+
} else {
|
|
34262
|
+
lines.push(` Privacy: No data collected`);
|
|
34263
|
+
}
|
|
34264
|
+
const enabledFeatures = Object.entries(compliance.checklist).filter(([_2, v]) => v === true).map(([k3, _2]) => {
|
|
34265
|
+
const featureInfo = FEATURE_ITEMS.find((f) => f.value === k3);
|
|
34266
|
+
return featureInfo?.label || k3;
|
|
34267
|
+
});
|
|
34268
|
+
if (enabledFeatures.length > 0) {
|
|
34269
|
+
lines.push(` Features: ${enabledFeatures.join(", ")}`);
|
|
34270
|
+
} else {
|
|
34271
|
+
lines.push(` Features: None selected`);
|
|
34272
|
+
}
|
|
34273
|
+
return lines;
|
|
34274
|
+
}
|
|
34275
|
+
|
|
33593
34276
|
// src/commands/submit.ts
|
|
33594
|
-
async function
|
|
34277
|
+
async function openUrl(url) {
|
|
34278
|
+
try {
|
|
34279
|
+
const open = (await import("./open-A77P4RC4.js")).default;
|
|
34280
|
+
await open(url);
|
|
34281
|
+
} catch {
|
|
34282
|
+
console.log(subtext(` Visit: ${url}`));
|
|
34283
|
+
}
|
|
34284
|
+
}
|
|
34285
|
+
async function fetchCredits() {
|
|
34286
|
+
try {
|
|
34287
|
+
const res = await apiRequest("/api/credits");
|
|
34288
|
+
if (!res.ok) return null;
|
|
34289
|
+
const data = await res.json();
|
|
34290
|
+
return data.credits ?? null;
|
|
34291
|
+
} catch {
|
|
34292
|
+
return null;
|
|
34293
|
+
}
|
|
34294
|
+
}
|
|
34295
|
+
async function creditPreCheck() {
|
|
34296
|
+
const credits = await fetchCredits();
|
|
34297
|
+
if (credits === null) {
|
|
34298
|
+
log.warning("Could not verify credit balance. Proceeding anyway.");
|
|
34299
|
+
return true;
|
|
34300
|
+
}
|
|
34301
|
+
if (credits >= 100) return true;
|
|
34302
|
+
log.warning(`You need 100 credits for a review. You currently have ${credits}.`);
|
|
34303
|
+
console.log();
|
|
34304
|
+
const wantsBuy = await confirm("Would you like to buy more credits?");
|
|
34305
|
+
if (wantsBuy === null || !wantsBuy) return false;
|
|
34306
|
+
await openUrl("https://preflightlaunch.com/pricing");
|
|
34307
|
+
log.info("Opened pricing page in browser.");
|
|
34308
|
+
console.log();
|
|
34309
|
+
log.info(subtext("Waiting for credits... Press Enter to check now, or Esc to cancel."));
|
|
34310
|
+
let attempts = 0;
|
|
34311
|
+
const maxAttempts = 60;
|
|
34312
|
+
while (attempts < maxAttempts) {
|
|
34313
|
+
await new Promise((r) => setTimeout(r, 1e4));
|
|
34314
|
+
attempts++;
|
|
34315
|
+
const newCredits = await fetchCredits();
|
|
34316
|
+
if (newCredits !== null && newCredits >= 100) {
|
|
34317
|
+
log.success(`Credits updated! You now have ${newCredits} credits.`);
|
|
34318
|
+
return true;
|
|
34319
|
+
}
|
|
34320
|
+
}
|
|
34321
|
+
log.warning("Still waiting for credits. You can try again later.");
|
|
34322
|
+
return false;
|
|
34323
|
+
}
|
|
34324
|
+
async function offerDraftSave(state) {
|
|
34325
|
+
if (!state.appName) return;
|
|
34326
|
+
const save = await confirm("Save your progress as a draft?", true);
|
|
34327
|
+
if (save === null || !save) return;
|
|
34328
|
+
const s = spinner();
|
|
34329
|
+
s.start("Saving draft...");
|
|
34330
|
+
try {
|
|
34331
|
+
const body = { app_name: state.appName };
|
|
34332
|
+
if (state.description) body.description = state.description;
|
|
34333
|
+
if (state.keywords) body.keywords = state.keywords;
|
|
34334
|
+
if (state.category) body.category = state.category;
|
|
34335
|
+
if (state.supportUrl) body.support_url = state.supportUrl;
|
|
34336
|
+
if (state.promotionalText) body.promotional_text = state.promotionalText;
|
|
34337
|
+
if (state.marketingUrl) body.marketing_url = state.marketingUrl;
|
|
34338
|
+
if (state.signInRequired != null) body.sign_in_required = state.signInRequired;
|
|
34339
|
+
if (state.demoUsername) body.demo_username = state.demoUsername;
|
|
34340
|
+
if (state.demoPassword) body.demo_password = state.demoPassword;
|
|
34341
|
+
if (state.compliance) Object.assign(body, formatComplianceForApi(state.compliance));
|
|
34342
|
+
const res = await apiRequest("/api/submissions", {
|
|
34343
|
+
method: "POST",
|
|
34344
|
+
body: JSON.stringify(body)
|
|
34345
|
+
});
|
|
34346
|
+
s.stop(res.ok ? "Draft saved" : "Could not save draft");
|
|
34347
|
+
if (res.ok) {
|
|
34348
|
+
log.success("Draft saved. Resume it from View Reviews anytime.");
|
|
34349
|
+
}
|
|
34350
|
+
} catch {
|
|
34351
|
+
s.stop("Could not save draft");
|
|
34352
|
+
}
|
|
34353
|
+
}
|
|
34354
|
+
async function offerAscAutofill(appDetails) {
|
|
34355
|
+
const ascConnected = getAscConnected();
|
|
34356
|
+
if (!ascConnected) return appDetails;
|
|
34357
|
+
try {
|
|
34358
|
+
const statusRes = await apiRequest("/api/asc/connect");
|
|
34359
|
+
if (!statusRes.ok) return appDetails;
|
|
34360
|
+
const statusData = await statusRes.json();
|
|
34361
|
+
if (!statusData.connected || !statusData.appId) return appDetails;
|
|
34362
|
+
const useAutofill = await confirm(
|
|
34363
|
+
`Autofill from App Store Connect? (${statusData.appName || "Connected app"})`,
|
|
34364
|
+
true
|
|
34365
|
+
);
|
|
34366
|
+
if (useAutofill === null || !useAutofill) return appDetails;
|
|
34367
|
+
const s = spinner();
|
|
34368
|
+
s.start("Fetching from App Store Connect...");
|
|
34369
|
+
const autofillRes = await apiRequest("/api/asc/autofill", {
|
|
34370
|
+
method: "POST",
|
|
34371
|
+
body: JSON.stringify({ appId: statusData.appId })
|
|
34372
|
+
});
|
|
34373
|
+
const autofillData = await autofillRes.json();
|
|
34374
|
+
s.stop(autofillRes.ok ? "Autofill complete" : "Autofill failed");
|
|
34375
|
+
if (autofillRes.ok && autofillData) {
|
|
34376
|
+
return {
|
|
34377
|
+
appName: appDetails.appName || autofillData.app_name || appDetails.appName,
|
|
34378
|
+
description: appDetails.description || autofillData.description,
|
|
34379
|
+
keywords: appDetails.keywords || autofillData.keywords,
|
|
34380
|
+
category: appDetails.category || autofillData.category,
|
|
34381
|
+
supportUrl: appDetails.supportUrl || autofillData.support_url,
|
|
34382
|
+
promotionalText: appDetails.promotionalText || autofillData.promotional_text,
|
|
34383
|
+
marketingUrl: appDetails.marketingUrl || autofillData.marketing_url,
|
|
34384
|
+
signInRequired: appDetails.signInRequired,
|
|
34385
|
+
demoUsername: appDetails.demoUsername,
|
|
34386
|
+
demoPassword: appDetails.demoPassword
|
|
34387
|
+
};
|
|
34388
|
+
}
|
|
34389
|
+
} catch {
|
|
34390
|
+
}
|
|
34391
|
+
return appDetails;
|
|
34392
|
+
}
|
|
34393
|
+
async function submitCommand(path5, options = {}, fromMenu = false) {
|
|
34394
|
+
const draftState = {};
|
|
33595
34395
|
if (!isLoggedIn()) {
|
|
34396
|
+
if (fromMenu) {
|
|
34397
|
+
log.error("Not logged in.");
|
|
34398
|
+
return;
|
|
34399
|
+
}
|
|
33596
34400
|
const wantsLogin = await promptLogin();
|
|
33597
34401
|
if (wantsLogin) {
|
|
33598
34402
|
const s = spinner();
|
|
33599
34403
|
s.start("Opening browser...");
|
|
33600
|
-
const result = await loginWithBrowser();
|
|
34404
|
+
const result = await loginWithBrowser("login");
|
|
33601
34405
|
if (result) {
|
|
33602
34406
|
s.stop(`Logged in as ${result.email}`);
|
|
33603
34407
|
} else {
|
|
@@ -33610,18 +34414,24 @@ async function submitCommand(path5, options = {}) {
|
|
|
33610
34414
|
return;
|
|
33611
34415
|
}
|
|
33612
34416
|
}
|
|
34417
|
+
if (fromMenu) {
|
|
34418
|
+
const hasCredits = await creditPreCheck();
|
|
34419
|
+
if (!hasCredits) return;
|
|
34420
|
+
}
|
|
33613
34421
|
if (!path5) {
|
|
33614
34422
|
const resolvedPath = await interactiveProjectSelect();
|
|
33615
34423
|
if (!resolvedPath) return;
|
|
33616
34424
|
path5 = resolvedPath;
|
|
33617
34425
|
}
|
|
33618
|
-
const dir =
|
|
34426
|
+
const dir = resolve3(path5);
|
|
33619
34427
|
setLastScannedPath(dir);
|
|
33620
34428
|
const detected = scanProject(dir);
|
|
33621
|
-
const
|
|
33622
|
-
|
|
33623
|
-
|
|
33624
|
-
if (options.
|
|
34429
|
+
const projectName = detected.projectName || "Unknown App";
|
|
34430
|
+
let appName = options.appName || projectName;
|
|
34431
|
+
draftState.appName = appName;
|
|
34432
|
+
if (options.plist) detected.infoPlist = resolve3(options.plist);
|
|
34433
|
+
if (options.manifest) detected.privacyManifest = resolve3(options.manifest);
|
|
34434
|
+
if (options.ipa) detected.ipa = resolve3(options.ipa);
|
|
33625
34435
|
const filesToUpload = [];
|
|
33626
34436
|
if (detected.infoPlist) {
|
|
33627
34437
|
filesToUpload.push({ type: "plist", filename: "Info.plist", path: detected.infoPlist });
|
|
@@ -33641,39 +34451,118 @@ async function submitCommand(path5, options = {}) {
|
|
|
33641
34451
|
});
|
|
33642
34452
|
}
|
|
33643
34453
|
if (filesToUpload.length === 0) {
|
|
33644
|
-
log.warning("No files to upload.
|
|
34454
|
+
log.warning("No files to upload. Make sure you're pointing to an Xcode project directory.");
|
|
34455
|
+
if (fromMenu) return;
|
|
34456
|
+
log.info(subtext("Use --plist, --manifest, --ipa, or --screenshots flags to specify files manually."));
|
|
33645
34457
|
return;
|
|
33646
34458
|
}
|
|
33647
|
-
|
|
33648
|
-
|
|
33649
|
-
|
|
33650
|
-
const
|
|
33651
|
-
|
|
33652
|
-
|
|
33653
|
-
|
|
33654
|
-
|
|
33655
|
-
|
|
33656
|
-
|
|
33657
|
-
|
|
33658
|
-
|
|
34459
|
+
let appDetails = null;
|
|
34460
|
+
let compliance = null;
|
|
34461
|
+
if (fromMenu) {
|
|
34462
|
+
const reviewType = await select({
|
|
34463
|
+
message: "What would you like to include in your review?",
|
|
34464
|
+
options: [
|
|
34465
|
+
{ value: "quick", label: "Quick review (just analyze my project files)", hint: "Fastest option" },
|
|
34466
|
+
{ value: "full", label: "Full review (add app details + compliance info)", hint: "More thorough" }
|
|
34467
|
+
]
|
|
34468
|
+
});
|
|
34469
|
+
if (reviewType === null) {
|
|
34470
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34471
|
+
return;
|
|
34472
|
+
}
|
|
34473
|
+
if (reviewType === "full") {
|
|
34474
|
+
appDetails = await collectAppDetails(projectName);
|
|
34475
|
+
if (appDetails === null) {
|
|
34476
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34477
|
+
return;
|
|
34478
|
+
}
|
|
34479
|
+
appName = appDetails.appName;
|
|
34480
|
+
draftState.appName = appName;
|
|
34481
|
+
draftState.description = appDetails.description;
|
|
34482
|
+
draftState.keywords = appDetails.keywords;
|
|
34483
|
+
draftState.category = appDetails.category;
|
|
34484
|
+
draftState.supportUrl = appDetails.supportUrl;
|
|
34485
|
+
draftState.promotionalText = appDetails.promotionalText;
|
|
34486
|
+
draftState.marketingUrl = appDetails.marketingUrl;
|
|
34487
|
+
draftState.signInRequired = appDetails.signInRequired;
|
|
34488
|
+
draftState.demoUsername = appDetails.demoUsername;
|
|
34489
|
+
draftState.demoPassword = appDetails.demoPassword;
|
|
34490
|
+
appDetails = await offerAscAutofill(appDetails);
|
|
34491
|
+
compliance = await collectCompliance();
|
|
34492
|
+
if (compliance === null) {
|
|
34493
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34494
|
+
return;
|
|
34495
|
+
}
|
|
34496
|
+
draftState.compliance = compliance;
|
|
34497
|
+
}
|
|
34498
|
+
}
|
|
34499
|
+
if (fromMenu) {
|
|
34500
|
+
console.log();
|
|
34501
|
+
note(buildSummary(appName, dir, filesToUpload, compliance), "Review Summary");
|
|
34502
|
+
const action = await select({
|
|
34503
|
+
message: `Submit review? (100 credits)`,
|
|
34504
|
+
options: [
|
|
34505
|
+
{ value: "submit", label: "Submit review", hint: "100 credits will be deducted" },
|
|
34506
|
+
{ value: "cancel", label: "Cancel", hint: "Back to menu" }
|
|
34507
|
+
]
|
|
34508
|
+
});
|
|
34509
|
+
if (action === null || action === "cancel") {
|
|
34510
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34511
|
+
return;
|
|
34512
|
+
}
|
|
34513
|
+
} else {
|
|
34514
|
+
if (!fromMenu) {
|
|
34515
|
+
intro(`Submit ${appName} for analysis`);
|
|
34516
|
+
const fileLines = filesToUpload.map((f) => {
|
|
34517
|
+
const size = getFileSize(f.path);
|
|
34518
|
+
const icon = f.type === "screenshot" ? icons.image : icons.file;
|
|
34519
|
+
return ` ${icon} ${f.filename} ${subtext(`(${formatBytes(size)})`)}`;
|
|
34520
|
+
});
|
|
34521
|
+
log.message(source_default.bold("Files to upload:") + "\n" + fileLines.join("\n"));
|
|
34522
|
+
const shouldContinue = await confirm("This will use 100 credits. Continue?");
|
|
34523
|
+
if (shouldContinue === null || !shouldContinue) {
|
|
34524
|
+
outro("Submission cancelled.");
|
|
34525
|
+
return;
|
|
34526
|
+
}
|
|
34527
|
+
}
|
|
33659
34528
|
}
|
|
34529
|
+
log.info(subtext("Reviews usually take 1-3 minutes."));
|
|
34530
|
+
console.log();
|
|
33660
34531
|
const spinner2 = createSpinner("Creating submission...");
|
|
33661
34532
|
spinner2.start();
|
|
34533
|
+
let activeSpinner = spinner2;
|
|
33662
34534
|
try {
|
|
34535
|
+
const submissionBody = { app_name: appName };
|
|
34536
|
+
if (appDetails) {
|
|
34537
|
+
if (appDetails.description) submissionBody.description = appDetails.description;
|
|
34538
|
+
if (appDetails.keywords) submissionBody.keywords = appDetails.keywords;
|
|
34539
|
+
if (appDetails.category) submissionBody.category = appDetails.category;
|
|
34540
|
+
if (appDetails.supportUrl) submissionBody.support_url = appDetails.supportUrl;
|
|
34541
|
+
if (appDetails.promotionalText) submissionBody.promotional_text = appDetails.promotionalText;
|
|
34542
|
+
if (appDetails.marketingUrl) submissionBody.marketing_url = appDetails.marketingUrl;
|
|
34543
|
+
submissionBody.sign_in_required = appDetails.signInRequired;
|
|
34544
|
+
if (appDetails.demoUsername) submissionBody.demo_username = appDetails.demoUsername;
|
|
34545
|
+
if (appDetails.demoPassword) submissionBody.demo_password = appDetails.demoPassword;
|
|
34546
|
+
}
|
|
34547
|
+
if (compliance) {
|
|
34548
|
+
Object.assign(submissionBody, formatComplianceForApi(compliance));
|
|
34549
|
+
}
|
|
33663
34550
|
const createRes = await apiRequest("/api/submissions", {
|
|
33664
34551
|
method: "POST",
|
|
33665
|
-
body: JSON.stringify(
|
|
34552
|
+
body: JSON.stringify(submissionBody)
|
|
33666
34553
|
});
|
|
33667
34554
|
const createData = await createRes.json();
|
|
33668
34555
|
if (!createRes.ok) {
|
|
33669
34556
|
spinner2.stop();
|
|
33670
34557
|
log.error(createData.message || "Failed to create submission");
|
|
33671
|
-
process.
|
|
34558
|
+
if (!fromMenu) process.exitCode = 1;
|
|
34559
|
+
return;
|
|
33672
34560
|
}
|
|
33673
34561
|
const submissionId = createData.submissionId;
|
|
33674
34562
|
spinner2.succeed("Submission created");
|
|
33675
34563
|
const uploadSpinner = createSpinner("Getting upload URLs...");
|
|
33676
34564
|
uploadSpinner.start();
|
|
34565
|
+
activeSpinner = uploadSpinner;
|
|
33677
34566
|
const urlsRes = await apiRequest(`/api/submissions/${submissionId}/upload-urls`, {
|
|
33678
34567
|
method: "POST",
|
|
33679
34568
|
body: JSON.stringify({
|
|
@@ -33688,7 +34577,8 @@ async function submitCommand(path5, options = {}) {
|
|
|
33688
34577
|
if (!urlsRes.ok) {
|
|
33689
34578
|
uploadSpinner.stop();
|
|
33690
34579
|
log.error(urlsData.message || "Failed to get upload URLs");
|
|
33691
|
-
process.
|
|
34580
|
+
if (!fromMenu) process.exitCode = 1;
|
|
34581
|
+
return;
|
|
33692
34582
|
}
|
|
33693
34583
|
for (let i = 0; i < urlsData.urls.length; i++) {
|
|
33694
34584
|
const urlInfo = urlsData.urls[i];
|
|
@@ -33704,35 +34594,67 @@ async function submitCommand(path5, options = {}) {
|
|
|
33704
34594
|
if (!uploadRes.ok) {
|
|
33705
34595
|
uploadSpinner.stop();
|
|
33706
34596
|
log.error(`Failed to upload ${fileInfo.filename}: HTTP ${uploadRes.status} ${uploadRes.statusText}`);
|
|
33707
|
-
process.
|
|
34597
|
+
if (!fromMenu) process.exitCode = 1;
|
|
34598
|
+
return;
|
|
33708
34599
|
}
|
|
33709
34600
|
}
|
|
33710
34601
|
uploadSpinner.succeed("Files uploaded");
|
|
33711
34602
|
const analyzeSpinner = createSpinner("Starting analysis...");
|
|
33712
34603
|
analyzeSpinner.start();
|
|
33713
|
-
|
|
33714
|
-
|
|
33715
|
-
|
|
33716
|
-
|
|
33717
|
-
|
|
33718
|
-
|
|
33719
|
-
|
|
33720
|
-
|
|
33721
|
-
|
|
33722
|
-
|
|
33723
|
-
|
|
33724
|
-
|
|
33725
|
-
|
|
33726
|
-
|
|
33727
|
-
|
|
34604
|
+
activeSpinner = analyzeSpinner;
|
|
34605
|
+
const finalizePayload = {
|
|
34606
|
+
files: filesToUpload.map((f) => ({
|
|
34607
|
+
type: f.type,
|
|
34608
|
+
index: f.index
|
|
34609
|
+
}))
|
|
34610
|
+
};
|
|
34611
|
+
let finalizeSuccess = false;
|
|
34612
|
+
let maxFinalizeRetries = 3;
|
|
34613
|
+
while (!finalizeSuccess && maxFinalizeRetries > 0) {
|
|
34614
|
+
const finalizeRes = await apiRequest(`/api/submissions/${submissionId}/finalize`, {
|
|
34615
|
+
method: "POST",
|
|
34616
|
+
body: JSON.stringify(finalizePayload)
|
|
34617
|
+
});
|
|
34618
|
+
const finalizeData = await finalizeRes.json();
|
|
34619
|
+
if (finalizeRes.ok) {
|
|
34620
|
+
finalizeSuccess = true;
|
|
34621
|
+
} else if (finalizeRes.status === 402) {
|
|
34622
|
+
analyzeSpinner.stop();
|
|
34623
|
+
log.warning(`Not enough credits. Need ${finalizeData.required ?? 100}, have ${finalizeData.credits ?? 0}.`);
|
|
34624
|
+
console.log();
|
|
34625
|
+
const wantsBuy = await confirm("Would you like to buy more credits?");
|
|
34626
|
+
if (wantsBuy === null || !wantsBuy) return;
|
|
34627
|
+
await openUrl("https://preflightlaunch.com/pricing");
|
|
34628
|
+
log.info("Opened pricing page. Press Enter when you've purchased credits.");
|
|
34629
|
+
const ready = await confirm("Ready to continue?");
|
|
34630
|
+
if (ready === null || !ready) return;
|
|
34631
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
34632
|
+
analyzeSpinner.start();
|
|
34633
|
+
activeSpinner = analyzeSpinner;
|
|
34634
|
+
analyzeSpinner.text = "Retrying analysis...";
|
|
34635
|
+
maxFinalizeRetries--;
|
|
33728
34636
|
} else {
|
|
34637
|
+
analyzeSpinner.stop();
|
|
33729
34638
|
log.error(finalizeData.message || "Failed to finalize submission");
|
|
34639
|
+
if (!fromMenu) process.exitCode = 1;
|
|
34640
|
+
return;
|
|
33730
34641
|
}
|
|
33731
|
-
process.exit(1);
|
|
33732
34642
|
}
|
|
33733
|
-
|
|
34643
|
+
if (!finalizeSuccess) {
|
|
34644
|
+
analyzeSpinner.stop();
|
|
34645
|
+
log.error("Could not finalize after multiple attempts. Your files are saved -- try again later.");
|
|
34646
|
+
return;
|
|
34647
|
+
}
|
|
34648
|
+
analyzeSpinner.text = "AI review in progress... (press Esc to stop waiting)";
|
|
33734
34649
|
const reportData = await pollForReport(submissionId, analyzeSpinner);
|
|
33735
|
-
|
|
34650
|
+
if (reportData.status === "cancelled") {
|
|
34651
|
+
analyzeSpinner.stop();
|
|
34652
|
+
log.info("Analysis continues in the background.");
|
|
34653
|
+
console.log(subtext(` Check status with ${brand(`preflight status ${submissionId}`)} or from View Reviews.`));
|
|
34654
|
+
console.log();
|
|
34655
|
+
return;
|
|
34656
|
+
}
|
|
34657
|
+
analyzeSpinner.succeed("Analysis complete!");
|
|
33736
34658
|
if (reportData.status === "complete" && reportData.data) {
|
|
33737
34659
|
if (options.json) {
|
|
33738
34660
|
console.log(JSON.stringify(reportData.data, null, 2));
|
|
@@ -33740,111 +34662,332 @@ async function submitCommand(path5, options = {}) {
|
|
|
33740
34662
|
renderReport(reportData.data.report, reportData.data.items);
|
|
33741
34663
|
console.log(subtext(` Full report: https://preflightlaunch.com/report/${reportData.data.report.id}`));
|
|
33742
34664
|
console.log();
|
|
33743
|
-
|
|
33744
|
-
|
|
33745
|
-
|
|
33746
|
-
|
|
33747
|
-
|
|
33748
|
-
|
|
33749
|
-
|
|
33750
|
-
|
|
33751
|
-
|
|
33752
|
-
|
|
33753
|
-
|
|
33754
|
-
} else if (next === "another") {
|
|
33755
|
-
await submitCommand();
|
|
34665
|
+
if (!fromMenu) {
|
|
34666
|
+
const next = await select({
|
|
34667
|
+
message: "What next?",
|
|
34668
|
+
options: [
|
|
34669
|
+
{ value: "open", label: "Open full report in browser" },
|
|
34670
|
+
{ value: "done", label: "Done" }
|
|
34671
|
+
]
|
|
34672
|
+
});
|
|
34673
|
+
if (next === "open") {
|
|
34674
|
+
await openUrl(`https://preflightlaunch.com/report/${reportData.data.report.id}`);
|
|
34675
|
+
}
|
|
33756
34676
|
}
|
|
33757
34677
|
}
|
|
33758
34678
|
} else if (reportData.status === "failed") {
|
|
33759
34679
|
log.error("Analysis failed. Please try submitting again or contact support.");
|
|
33760
|
-
process.
|
|
34680
|
+
if (!fromMenu) process.exitCode = 1;
|
|
33761
34681
|
} else {
|
|
33762
34682
|
log.warning("Analysis is still running. Check status with:");
|
|
33763
34683
|
console.log(subtext(` preflight status ${submissionId}`));
|
|
33764
34684
|
}
|
|
33765
34685
|
} catch (err) {
|
|
33766
|
-
|
|
34686
|
+
activeSpinner.stop();
|
|
33767
34687
|
log.error(`Submit failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
33768
|
-
process.
|
|
34688
|
+
if (!fromMenu) process.exitCode = 1;
|
|
34689
|
+
}
|
|
34690
|
+
}
|
|
34691
|
+
async function resumeSubmitCommand(draft) {
|
|
34692
|
+
const submissionId = draft.id;
|
|
34693
|
+
log.step(`Resuming draft: ${draft.app_name || "Unknown"}`);
|
|
34694
|
+
console.log();
|
|
34695
|
+
const lastPath = draft.project_path;
|
|
34696
|
+
let path5;
|
|
34697
|
+
if (lastPath) {
|
|
34698
|
+
const useLast = await confirm(`Use previous project path? (${lastPath})`, true);
|
|
34699
|
+
if (useLast === null) return;
|
|
34700
|
+
if (useLast) path5 = lastPath;
|
|
34701
|
+
}
|
|
34702
|
+
if (!path5) {
|
|
34703
|
+
const resolvedPath = await interactiveProjectSelect();
|
|
34704
|
+
if (!resolvedPath) return;
|
|
34705
|
+
path5 = resolvedPath;
|
|
34706
|
+
}
|
|
34707
|
+
const dir = resolve3(path5);
|
|
34708
|
+
setLastScannedPath(dir);
|
|
34709
|
+
const detected = scanProject(dir);
|
|
34710
|
+
const projectName = detected.projectName || "Unknown App";
|
|
34711
|
+
const filesToUpload = [];
|
|
34712
|
+
if (detected.infoPlist) {
|
|
34713
|
+
filesToUpload.push({ type: "plist", filename: "Info.plist", path: detected.infoPlist });
|
|
34714
|
+
}
|
|
34715
|
+
if (detected.privacyManifest) {
|
|
34716
|
+
filesToUpload.push({ type: "manifest", filename: "PrivacyInfo.xcprivacy", path: detected.privacyManifest });
|
|
34717
|
+
}
|
|
34718
|
+
if (detected.ipa) {
|
|
34719
|
+
filesToUpload.push({ type: "ipa", filename: basename3(detected.ipa), path: detected.ipa });
|
|
34720
|
+
}
|
|
34721
|
+
for (let i = 0; i < Math.min(detected.screenshots.length, 10); i++) {
|
|
34722
|
+
filesToUpload.push({
|
|
34723
|
+
type: "screenshot",
|
|
34724
|
+
index: i,
|
|
34725
|
+
filename: basename3(detected.screenshots[i]),
|
|
34726
|
+
path: detected.screenshots[i]
|
|
34727
|
+
});
|
|
34728
|
+
}
|
|
34729
|
+
if (filesToUpload.length === 0) {
|
|
34730
|
+
log.warning("No files found. Make sure you're pointing to an Xcode project directory.");
|
|
34731
|
+
return;
|
|
34732
|
+
}
|
|
34733
|
+
const draftDefaults = {
|
|
34734
|
+
appName: draft.app_name || projectName,
|
|
34735
|
+
description: draft.description,
|
|
34736
|
+
keywords: draft.keywords,
|
|
34737
|
+
category: draft.category,
|
|
34738
|
+
supportUrl: draft.support_url,
|
|
34739
|
+
promotionalText: draft.promotional_text,
|
|
34740
|
+
marketingUrl: draft.marketing_url,
|
|
34741
|
+
signInRequired: draft.sign_in_required ?? false,
|
|
34742
|
+
demoUsername: draft.demo_username,
|
|
34743
|
+
demoPassword: draft.demo_password
|
|
34744
|
+
};
|
|
34745
|
+
const appDetails = await collectAppDetails(projectName, draftDefaults);
|
|
34746
|
+
if (appDetails === null) return;
|
|
34747
|
+
const appName = appDetails.appName;
|
|
34748
|
+
const compliance = await collectCompliance();
|
|
34749
|
+
if (compliance === null) return;
|
|
34750
|
+
console.log();
|
|
34751
|
+
note(buildSummary(appName, dir, filesToUpload, compliance), "Review Summary");
|
|
34752
|
+
const action = await select({
|
|
34753
|
+
message: `Submit review? (100 credits)`,
|
|
34754
|
+
options: [
|
|
34755
|
+
{ value: "submit", label: "Submit review", hint: "100 credits will be deducted" },
|
|
34756
|
+
{ value: "cancel", label: "Cancel", hint: "Back to menu" }
|
|
34757
|
+
]
|
|
34758
|
+
});
|
|
34759
|
+
if (action === null || action === "cancel") return;
|
|
34760
|
+
log.info(subtext("Reviews usually take 1-3 minutes."));
|
|
34761
|
+
console.log();
|
|
34762
|
+
const spinner2 = createSpinner("Updating submission...");
|
|
34763
|
+
spinner2.start();
|
|
34764
|
+
let activeSpinner = spinner2;
|
|
34765
|
+
try {
|
|
34766
|
+
const submissionBody = {
|
|
34767
|
+
submission_id: submissionId,
|
|
34768
|
+
app_name: appName
|
|
34769
|
+
};
|
|
34770
|
+
if (appDetails.description) submissionBody.description = appDetails.description;
|
|
34771
|
+
if (appDetails.keywords) submissionBody.keywords = appDetails.keywords;
|
|
34772
|
+
if (appDetails.category) submissionBody.category = appDetails.category;
|
|
34773
|
+
if (appDetails.supportUrl) submissionBody.support_url = appDetails.supportUrl;
|
|
34774
|
+
if (appDetails.promotionalText) submissionBody.promotional_text = appDetails.promotionalText;
|
|
34775
|
+
if (appDetails.marketingUrl) submissionBody.marketing_url = appDetails.marketingUrl;
|
|
34776
|
+
submissionBody.sign_in_required = appDetails.signInRequired;
|
|
34777
|
+
if (appDetails.demoUsername) submissionBody.demo_username = appDetails.demoUsername;
|
|
34778
|
+
if (appDetails.demoPassword) submissionBody.demo_password = appDetails.demoPassword;
|
|
34779
|
+
if (compliance) {
|
|
34780
|
+
Object.assign(submissionBody, formatComplianceForApi(compliance));
|
|
34781
|
+
}
|
|
34782
|
+
const createRes = await apiRequest("/api/submissions", {
|
|
34783
|
+
method: "POST",
|
|
34784
|
+
body: JSON.stringify(submissionBody)
|
|
34785
|
+
});
|
|
34786
|
+
const createData = await createRes.json();
|
|
34787
|
+
if (!createRes.ok) {
|
|
34788
|
+
spinner2.stop();
|
|
34789
|
+
log.error(createData.message || "Failed to update submission");
|
|
34790
|
+
return;
|
|
34791
|
+
}
|
|
34792
|
+
const finalId = createData.submissionId || submissionId;
|
|
34793
|
+
spinner2.succeed("Submission updated");
|
|
34794
|
+
const uploadSpinner = createSpinner("Getting upload URLs...");
|
|
34795
|
+
uploadSpinner.start();
|
|
34796
|
+
activeSpinner = uploadSpinner;
|
|
34797
|
+
const urlsRes = await apiRequest(`/api/submissions/${finalId}/upload-urls`, {
|
|
34798
|
+
method: "POST",
|
|
34799
|
+
body: JSON.stringify({
|
|
34800
|
+
files: filesToUpload.map((f) => ({
|
|
34801
|
+
type: f.type,
|
|
34802
|
+
index: f.index,
|
|
34803
|
+
filename: f.filename
|
|
34804
|
+
}))
|
|
34805
|
+
})
|
|
34806
|
+
});
|
|
34807
|
+
const urlsData = await urlsRes.json();
|
|
34808
|
+
if (!urlsRes.ok) {
|
|
34809
|
+
uploadSpinner.stop();
|
|
34810
|
+
log.error(urlsData.message || "Failed to get upload URLs");
|
|
34811
|
+
return;
|
|
34812
|
+
}
|
|
34813
|
+
for (let i = 0; i < urlsData.urls.length; i++) {
|
|
34814
|
+
const urlInfo = urlsData.urls[i];
|
|
34815
|
+
const fileInfo = filesToUpload[i];
|
|
34816
|
+
const fileBuffer = readFileSync(fileInfo.path);
|
|
34817
|
+
const fileSize = fileBuffer.length;
|
|
34818
|
+
uploadSpinner.text = `Uploading ${fileInfo.filename} (${formatBytes(fileSize)})...`;
|
|
34819
|
+
const uploadRes = await fetch(urlInfo.signedUrl, {
|
|
34820
|
+
method: "PUT",
|
|
34821
|
+
body: fileBuffer,
|
|
34822
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
34823
|
+
});
|
|
34824
|
+
if (!uploadRes.ok) {
|
|
34825
|
+
uploadSpinner.stop();
|
|
34826
|
+
log.error(`Failed to upload ${fileInfo.filename}: HTTP ${uploadRes.status} ${uploadRes.statusText}`);
|
|
34827
|
+
return;
|
|
34828
|
+
}
|
|
34829
|
+
}
|
|
34830
|
+
uploadSpinner.succeed("Files uploaded");
|
|
34831
|
+
const analyzeSpinner = createSpinner("Starting analysis...");
|
|
34832
|
+
analyzeSpinner.start();
|
|
34833
|
+
activeSpinner = analyzeSpinner;
|
|
34834
|
+
const finalizeRes = await apiRequest(`/api/submissions/${finalId}/finalize`, {
|
|
34835
|
+
method: "POST",
|
|
34836
|
+
body: JSON.stringify({
|
|
34837
|
+
files: filesToUpload.map((f) => ({ type: f.type, index: f.index }))
|
|
34838
|
+
})
|
|
34839
|
+
});
|
|
34840
|
+
const finalizeData = await finalizeRes.json();
|
|
34841
|
+
if (!finalizeRes.ok) {
|
|
34842
|
+
analyzeSpinner.stop();
|
|
34843
|
+
log.error(finalizeData.message || "Failed to start analysis");
|
|
34844
|
+
return;
|
|
34845
|
+
}
|
|
34846
|
+
analyzeSpinner.text = "AI review in progress... (press Esc to stop waiting)";
|
|
34847
|
+
const reportData = await pollForReport(finalId, analyzeSpinner);
|
|
34848
|
+
if (reportData.status === "cancelled") {
|
|
34849
|
+
analyzeSpinner.stop();
|
|
34850
|
+
log.info("Analysis continues in the background.");
|
|
34851
|
+
console.log(subtext(` Check status with ${brand(`preflight status ${finalId}`)} or from View Reviews.`));
|
|
34852
|
+
console.log();
|
|
34853
|
+
return;
|
|
34854
|
+
}
|
|
34855
|
+
analyzeSpinner.succeed("Analysis complete!");
|
|
34856
|
+
if (reportData.status === "complete" && reportData.data) {
|
|
34857
|
+
renderReport(reportData.data.report, reportData.data.items);
|
|
34858
|
+
console.log(subtext(` Full report: https://preflightlaunch.com/report/${reportData.data.report.id}`));
|
|
34859
|
+
console.log();
|
|
34860
|
+
} else if (reportData.status === "failed") {
|
|
34861
|
+
log.error("Analysis failed. Please try submitting again or contact support.");
|
|
34862
|
+
} else {
|
|
34863
|
+
log.warning("Analysis is still running. Check status with:");
|
|
34864
|
+
console.log(subtext(` preflight status ${finalId}`));
|
|
34865
|
+
}
|
|
34866
|
+
} catch (err) {
|
|
34867
|
+
activeSpinner.stop();
|
|
34868
|
+
log.error(`Resume failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
33769
34869
|
}
|
|
33770
34870
|
}
|
|
34871
|
+
function buildSummary(appName, dir, files, compliance) {
|
|
34872
|
+
const home = __require("os").homedir();
|
|
34873
|
+
const shortDir = dir.startsWith(home) ? "~" + dir.slice(home.length) : dir;
|
|
34874
|
+
const fileTypes = files.map((f) => f.filename).join(", ");
|
|
34875
|
+
const screenshotCount = files.filter((f) => f.type === "screenshot").length;
|
|
34876
|
+
let summary = `App: ${appName}
|
|
34877
|
+
`;
|
|
34878
|
+
summary += `Project: ${shortDir}
|
|
34879
|
+
`;
|
|
34880
|
+
summary += `Files: ${fileTypes}${screenshotCount > 0 ? ` (${screenshotCount} screenshots)` : ""}
|
|
34881
|
+
`;
|
|
34882
|
+
if (compliance) {
|
|
34883
|
+
const complianceLines = formatComplianceSummary(compliance);
|
|
34884
|
+
summary += complianceLines.map((l) => l.trim()).join("\n");
|
|
34885
|
+
}
|
|
34886
|
+
return summary;
|
|
34887
|
+
}
|
|
33771
34888
|
function getFileSize(filePath) {
|
|
33772
34889
|
try {
|
|
33773
|
-
return
|
|
34890
|
+
return statSync3(filePath).size;
|
|
33774
34891
|
} catch {
|
|
33775
34892
|
return 0;
|
|
33776
34893
|
}
|
|
33777
34894
|
}
|
|
33778
34895
|
async function pollForReport(submissionId, spinner2, maxAttempts = 60, interval = 5e3) {
|
|
33779
34896
|
let consecutiveFailures = 0;
|
|
33780
|
-
|
|
33781
|
-
|
|
33782
|
-
|
|
33783
|
-
if (
|
|
33784
|
-
|
|
33785
|
-
|
|
33786
|
-
|
|
34897
|
+
const startTime = Date.now();
|
|
34898
|
+
let cancelled = false;
|
|
34899
|
+
const onKeypress = (data) => {
|
|
34900
|
+
if (data[0] === 27 || data[0] === 3) {
|
|
34901
|
+
cancelled = true;
|
|
34902
|
+
}
|
|
34903
|
+
};
|
|
34904
|
+
if (process.stdin.isTTY) {
|
|
34905
|
+
process.stdin.setRawMode(true);
|
|
34906
|
+
process.stdin.resume();
|
|
34907
|
+
process.stdin.on("data", onKeypress);
|
|
34908
|
+
}
|
|
34909
|
+
try {
|
|
34910
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
34911
|
+
for (let w3 = 0; w3 < interval / 500; w3++) {
|
|
34912
|
+
if (cancelled) return { status: "cancelled" };
|
|
34913
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
33787
34914
|
}
|
|
33788
|
-
if (
|
|
33789
|
-
|
|
34915
|
+
if (cancelled) return { status: "cancelled" };
|
|
34916
|
+
const res = await apiRequest(`/api/submissions/${submissionId}`);
|
|
34917
|
+
if (!res.ok) {
|
|
34918
|
+
consecutiveFailures++;
|
|
34919
|
+
if (res.status === 401) {
|
|
34920
|
+
throw new Error("Session expired. Please run `preflight login` to re-authenticate.");
|
|
34921
|
+
}
|
|
34922
|
+
if (consecutiveFailures >= 3) {
|
|
34923
|
+
throw new Error(`Polling failed after 3 consecutive errors (last status: HTTP ${res.status})`);
|
|
34924
|
+
}
|
|
34925
|
+
continue;
|
|
33790
34926
|
}
|
|
33791
|
-
|
|
33792
|
-
|
|
33793
|
-
|
|
33794
|
-
|
|
33795
|
-
|
|
33796
|
-
|
|
33797
|
-
|
|
33798
|
-
|
|
33799
|
-
|
|
33800
|
-
|
|
33801
|
-
|
|
33802
|
-
|
|
33803
|
-
|
|
34927
|
+
consecutiveFailures = 0;
|
|
34928
|
+
const data = await res.json();
|
|
34929
|
+
const submission = data.data;
|
|
34930
|
+
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
34931
|
+
spinner2.text = `AI review in progress... (${elapsed}s elapsed, press Esc to stop waiting)`;
|
|
34932
|
+
if (submission.status === "complete") {
|
|
34933
|
+
if (submission.report_id) {
|
|
34934
|
+
const reportRes = await apiRequest(`/api/reports/${submission.report_id}`);
|
|
34935
|
+
const reportData = await reportRes.json();
|
|
34936
|
+
return { status: "complete", data: reportData };
|
|
34937
|
+
}
|
|
34938
|
+
return { status: "failed" };
|
|
34939
|
+
}
|
|
34940
|
+
if (submission.status === "failed") {
|
|
34941
|
+
return { status: "failed" };
|
|
33804
34942
|
}
|
|
33805
|
-
return { status: "failed" };
|
|
33806
34943
|
}
|
|
33807
|
-
|
|
33808
|
-
|
|
34944
|
+
return { status: "timeout" };
|
|
34945
|
+
} finally {
|
|
34946
|
+
if (process.stdin.isTTY) {
|
|
34947
|
+
process.stdin.removeListener("data", onKeypress);
|
|
34948
|
+
process.stdin.setRawMode(false);
|
|
34949
|
+
process.stdin.pause();
|
|
33809
34950
|
}
|
|
33810
34951
|
}
|
|
33811
|
-
return { status: "timeout" };
|
|
33812
34952
|
}
|
|
33813
34953
|
|
|
33814
34954
|
export {
|
|
33815
34955
|
source_default,
|
|
34956
|
+
brand,
|
|
34957
|
+
brandDim,
|
|
34958
|
+
subtext,
|
|
34959
|
+
ok,
|
|
34960
|
+
warning,
|
|
34961
|
+
critical,
|
|
34962
|
+
icons,
|
|
33816
34963
|
createSpinner,
|
|
33817
34964
|
success,
|
|
33818
34965
|
error,
|
|
34966
|
+
DEFAULT_API_URL,
|
|
33819
34967
|
getConfig,
|
|
34968
|
+
clearAuth,
|
|
33820
34969
|
isLoggedIn,
|
|
33821
34970
|
hasRunBefore,
|
|
33822
34971
|
markAsRun,
|
|
33823
34972
|
setLastScannedPath,
|
|
34973
|
+
setAscConnected,
|
|
33824
34974
|
loginWithBrowser,
|
|
33825
34975
|
logout,
|
|
33826
34976
|
apiRequest,
|
|
33827
34977
|
scanProject,
|
|
33828
|
-
brand,
|
|
33829
|
-
subtext,
|
|
33830
|
-
ok,
|
|
33831
|
-
warning,
|
|
33832
|
-
critical,
|
|
33833
|
-
icons,
|
|
33834
34978
|
intro,
|
|
33835
|
-
showTagline,
|
|
33836
|
-
brandSplash,
|
|
33837
|
-
outro,
|
|
33838
34979
|
tip,
|
|
34980
|
+
renderHeader,
|
|
33839
34981
|
select,
|
|
34982
|
+
confirm,
|
|
34983
|
+
text,
|
|
33840
34984
|
spinner,
|
|
33841
34985
|
log,
|
|
33842
|
-
findXcodeProjects,
|
|
33843
|
-
findProjectInDir,
|
|
33844
34986
|
interactiveProjectSelect,
|
|
33845
34987
|
renderReport,
|
|
33846
34988
|
renderReportJson,
|
|
33847
34989
|
handleUnknownCommand,
|
|
33848
|
-
submitCommand
|
|
34990
|
+
submitCommand,
|
|
34991
|
+
resumeSubmitCommand
|
|
33849
34992
|
};
|
|
33850
|
-
//# sourceMappingURL=chunk-
|
|
34993
|
+
//# sourceMappingURL=chunk-26P7VL2P.js.map
|