@wix/himalaya-cli 0.1533.0 → 0.1535.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -2380,11 +2380,446 @@ var init_app_link = __esm({
2380
2380
  });
2381
2381
 
2382
2382
  // ../../core/serve-control/src/dist-jobs.ts
2383
- var TOKEN_TTL_MS, CUSTOMER_UPLOAD_LEASE_MS, DIST_SHELLS, DEFAULT_DIST_SHELL, ANDROID_SIGNING_POLICIES, ANDROID_SIGNING_ALIASES;
2383
+ var dist_jobs_exports = {};
2384
+ __export(dist_jobs_exports, {
2385
+ ANDROID_FORMATS: () => ANDROID_FORMATS,
2386
+ ANDROID_FORMAT_NOT_BUILT: () => ANDROID_FORMAT_NOT_BUILT,
2387
+ ANDROID_FORMAT_NOT_REQUESTED: () => ANDROID_FORMAT_NOT_REQUESTED,
2388
+ ANDROID_FORMAT_WRONG_SIGNER: () => ANDROID_FORMAT_WRONG_SIGNER,
2389
+ ANDROID_SIGNING_ALIASES: () => ANDROID_SIGNING_ALIASES,
2390
+ ANDROID_SIGNING_POLICIES: () => ANDROID_SIGNING_POLICIES,
2391
+ CUSTOMER_UPLOAD_LEASE_MS: () => CUSTOMER_UPLOAD_LEASE_MS,
2392
+ DEFAULT_ANDROID_FORMAT: () => DEFAULT_ANDROID_FORMAT,
2393
+ DEFAULT_ANDROID_SIGNING: () => DEFAULT_ANDROID_SIGNING,
2394
+ DEFAULT_BORROWABLE_IOS_IDS: () => DEFAULT_BORROWABLE_IOS_IDS,
2395
+ DEFAULT_DIST_SHELL: () => DEFAULT_DIST_SHELL,
2396
+ DEFAULT_DIST_SIGNING: () => DEFAULT_DIST_SIGNING,
2397
+ DEFAULT_GIT_URL: () => DEFAULT_GIT_URL,
2398
+ DIST_PACKAGE_NAME: () => DIST_PACKAGE_NAME,
2399
+ DIST_PARAMS_FLAG: () => DIST_PARAMS_FLAG,
2400
+ DIST_SHELLS: () => DIST_SHELLS,
2401
+ JobServiceBroker: () => JobServiceBroker,
2402
+ JobServiceError: () => JobServiceError,
2403
+ MAX_MEASURABLE_IDENTITY_WINDOW: () => MAX_MEASURABLE_IDENTITY_WINDOW,
2404
+ SCOPE_CANCEL: () => SCOPE_CANCEL,
2405
+ SCOPE_CREATE: () => SCOPE_CREATE,
2406
+ SCOPE_READ: () => SCOPE_READ,
2407
+ WIX_MANAGED_SIGNING_ALIASES: () => WIX_MANAGED_SIGNING_ALIASES,
2408
+ androidFormatSkewFailed: () => androidFormatSkewFailed,
2409
+ borrowableIosIds: () => borrowableIosIds,
2410
+ credsFromConfig: () => credsFromConfig,
2411
+ customerUploadClaimable: () => customerUploadClaimable,
2412
+ distJobSpec: () => distJobSpec,
2413
+ encodeDistParams: () => encodeDistParams,
2414
+ isBorrowableIosId: () => isBorrowableIosId,
2415
+ isSuccess: () => isSuccess,
2416
+ isTerminal: () => isTerminal,
2417
+ isTerminalTestflightStatus: () => isTerminalTestflightStatus,
2418
+ isValidDistAttemptId: () => isValidDistAttemptId,
2419
+ nextCustomerUploadClaim: () => nextCustomerUploadClaim,
2420
+ nextCustomerUploadSettlement: () => nextCustomerUploadSettlement,
2421
+ resolveAndroidFormat: () => resolveAndroidFormat,
2422
+ resolveAndroidSigning: () => resolveAndroidSigning,
2423
+ resolveBorrowedIosId: () => resolveBorrowedIosId,
2424
+ resolveDistSigning: () => resolveDistSigning,
2425
+ testflightStatusRank: () => testflightStatusRank
2426
+ });
2427
+ function isTerminal(status) {
2428
+ return !!status && !IN_FLIGHT.has(status);
2429
+ }
2430
+ function isSuccess(status) {
2431
+ return status === "SUCCESS";
2432
+ }
2433
+ function snapshot(job) {
2434
+ return {
2435
+ id: String(job.id),
2436
+ status: job.status ?? "STATUS_UNKNOWN",
2437
+ ...job.buildUrl ? { buildUrl: String(job.buildUrl) } : {},
2438
+ ...job.output ? { output: String(job.output) } : {},
2439
+ ...typeof job.attemptNumber === "number" ? { attemptNumber: job.attemptNumber } : {}
2440
+ };
2441
+ }
2442
+ function truncate(s2, max = 400) {
2443
+ return s2.length > max ? `${s2.slice(0, max)}\u2026` : s2;
2444
+ }
2445
+ function credsFromConfig(cfg) {
2446
+ const appDefId = cfg("POCO_APP_DEF_ID")?.trim();
2447
+ const appSecret = cfg("POCO_APP_SECRET")?.trim();
2448
+ if (!appDefId || !appSecret) return void 0;
2449
+ return { appDefId, appSecret };
2450
+ }
2451
+ function isValidDistAttemptId(value2) {
2452
+ return typeof value2 === "string" && /^[A-Za-z0-9_-]{1,128}$/.test(value2);
2453
+ }
2454
+ function customerUploadClaimable(pending, now) {
2455
+ if (!pending) return false;
2456
+ if (pending.status === "READY_FOR_UPLOAD") return true;
2457
+ if (pending.status !== "UPLOADING") return false;
2458
+ if (!pending.claimedAt) return true;
2459
+ const claimedAt = Date.parse(pending.claimedAt);
2460
+ const at2 = Date.parse(now);
2461
+ return Number.isFinite(claimedAt) && Number.isFinite(at2) && at2 - claimedAt >= CUSTOMER_UPLOAD_LEASE_MS;
2462
+ }
2463
+ function nextCustomerUploadClaim(record2, now) {
2464
+ if (!record2 || record2.status !== "SUCCESS" || !customerUploadClaimable(record2.customerAppStore, now)) return void 0;
2465
+ return {
2466
+ ...record2,
2467
+ customerAppStore: { ...record2.customerAppStore, status: "UPLOADING", claimedAt: now },
2468
+ updatedAt: now
2469
+ };
2470
+ }
2471
+ function nextCustomerUploadSettlement(record2, claimedAt, update, now) {
2472
+ const pending = record2?.customerAppStore;
2473
+ if (!record2 || pending?.status !== "UPLOADING" || pending.claimedAt !== claimedAt) return void 0;
2474
+ return {
2475
+ ...record2,
2476
+ customerAppStore: {
2477
+ version: pending.version,
2478
+ mode: pending.mode,
2479
+ claimedAt: pending.claimedAt,
2480
+ status: update.status,
2481
+ ...update.error ? { error: update.error } : {},
2482
+ ...update.delivery ? { delivery: update.delivery } : {}
2483
+ },
2484
+ updatedAt: now
2485
+ };
2486
+ }
2487
+ function isTerminalTestflightStatus(status) {
2488
+ return /(?:^|_)(?:COMPLETED|FAILED|NOT_FOUND)$/.test(status?.toUpperCase() ?? "");
2489
+ }
2490
+ function testflightStatusRank(status) {
2491
+ const normalized = status?.toUpperCase() ?? "";
2492
+ if (isTerminalTestflightStatus(normalized)) return 3;
2493
+ if (/(?:^|_)UPLOADING$/.test(normalized)) return 2;
2494
+ if (/(?:^|_)PENDING$/.test(normalized)) return 1;
2495
+ return 0;
2496
+ }
2497
+ function borrowableIosIds(raw) {
2498
+ const parsed = (raw ?? "").split(",").map((s2) => s2.trim().toLowerCase()).filter(Boolean);
2499
+ return parsed.length > 0 ? parsed : DEFAULT_BORROWABLE_IOS_IDS;
2500
+ }
2501
+ function isBorrowableIosId(id, allowed2 = DEFAULT_BORROWABLE_IOS_IDS) {
2502
+ const candidate = id.toLowerCase();
2503
+ return allowed2.some(
2504
+ (pattern) => pattern.endsWith("*") ? candidate.startsWith(pattern.slice(0, -1)) : candidate === pattern
2505
+ );
2506
+ }
2507
+ function resolveBorrowedIosId(opts) {
2508
+ const { requested, platform } = opts;
2509
+ if (requested === void 0 || requested === null || requested === "") return {};
2510
+ if (opts.selfServe || opts.role === "owner") {
2511
+ return {
2512
+ error: {
2513
+ error: "operator_only_borrow_id",
2514
+ message: "borrowIosBundleId is not available to self-serve credentials",
2515
+ hint: "Ship the change over-the-air instead \u2014 OTA needs no rebuild and no borrowed signing identity."
2516
+ }
2517
+ };
2518
+ }
2519
+ if (typeof requested !== "string") {
2520
+ return { error: { error: "invalid_borrow_id", message: "borrowIosBundleId must be a string" } };
2521
+ }
2522
+ const id = requested.trim().toLowerCase();
2523
+ if (platform !== "ios") {
2524
+ return {
2525
+ error: {
2526
+ error: "borrow_id_ios_only",
2527
+ message: `borrowIosBundleId applies to iOS signing only (got platform "${platform}")`,
2528
+ hint: "Android needs no provisioning profile \u2014 drop the flag for android builds."
2529
+ }
2530
+ };
2531
+ }
2532
+ if (!BUNDLE_ID_RE.test(id)) {
2533
+ return {
2534
+ error: {
2535
+ error: "invalid_borrow_id",
2536
+ message: `"${requested}" is not a valid reverse-DNS bundle id`
2537
+ }
2538
+ };
2539
+ }
2540
+ const allowed2 = opts.allowed ?? DEFAULT_BORROWABLE_IOS_IDS;
2541
+ if (!isBorrowableIosId(id, allowed2)) {
2542
+ return {
2543
+ error: {
2544
+ error: "borrow_id_not_allowed",
2545
+ message: `"${id}" is not borrowable; allowed: ${allowed2.join(", ")}`,
2546
+ hint: "Only internal enterprise ids may be borrowed \u2014 a store id would replace the real App Store app on the device. An operator can change this with HIMI_DIST_BORROWABLE_IOS_IDS."
2547
+ }
2548
+ };
2549
+ }
2550
+ return { bundleId: id };
2551
+ }
2552
+ function resolveDistSigning(requested) {
2553
+ if (requested === void 0 || requested === null || requested === "") {
2554
+ return { policy: DEFAULT_DIST_SIGNING };
2555
+ }
2556
+ if (typeof requested !== "string") {
2557
+ return { error: { status: 400, body: { error: "invalid_signing", message: "signing must be a string" } } };
2558
+ }
2559
+ const want = requested.trim().toLowerCase();
2560
+ if (WIX_MANAGED_SIGNING_ALIASES.includes(want)) return { policy: "wix-managed" };
2561
+ if (want === "byo") return { policy: "byo" };
2562
+ return {
2563
+ error: {
2564
+ status: 501,
2565
+ body: {
2566
+ error: "dist_signing_unsupported",
2567
+ message: `signing "${requested}" is not implemented`,
2568
+ supported: [DEFAULT_DIST_SIGNING, "byo"]
2569
+ }
2570
+ }
2571
+ };
2572
+ }
2573
+ function resolveAndroidSigning(requested) {
2574
+ if (requested === void 0 || requested === null || requested === "") {
2575
+ return { policy: DEFAULT_ANDROID_SIGNING };
2576
+ }
2577
+ if (typeof requested !== "string") {
2578
+ return { error: { status: 400, body: { error: "invalid_android_signing", message: "androidSigning must be a string" } } };
2579
+ }
2580
+ const want = requested.trim().toLowerCase();
2581
+ const policy = Object.hasOwn(ANDROID_SIGNING_ALIASES, want) ? ANDROID_SIGNING_ALIASES[want] : void 0;
2582
+ if (policy) return { policy };
2583
+ return {
2584
+ error: {
2585
+ status: 400,
2586
+ body: {
2587
+ error: "invalid_android_signing",
2588
+ message: `androidSigning "${requested}" is not a known policy`,
2589
+ supported: ["qa-keystore", "wix-keystore", "debug"]
2590
+ }
2591
+ }
2592
+ };
2593
+ }
2594
+ function androidFormatSkewFailed(record2) {
2595
+ return record2.platform === "android" && typeof record2.error === "string" && [ANDROID_FORMAT_NOT_BUILT, ANDROID_FORMAT_WRONG_SIGNER, ANDROID_FORMAT_NOT_REQUESTED].some((code) => record2.error.includes(`${code}:`));
2596
+ }
2597
+ function resolveAndroidFormat(requested) {
2598
+ if (requested === void 0 || requested === null || requested === "") return { format: DEFAULT_ANDROID_FORMAT };
2599
+ if (typeof requested === "string" && ANDROID_FORMATS.includes(requested)) {
2600
+ return { format: requested };
2601
+ }
2602
+ return {
2603
+ error: {
2604
+ status: 400,
2605
+ body: {
2606
+ error: "invalid_android_format",
2607
+ message: `androidFormat ${typeof requested === "string" ? `"${requested.slice(0, 40)}"` : `of type ${typeof requested}`} is not a known format`,
2608
+ supported: [...ANDROID_FORMATS]
2609
+ }
2610
+ }
2611
+ };
2612
+ }
2613
+ function encodeDistParams(env) {
2614
+ return Buffer.from(JSON.stringify(env), "utf8").toString("base64url");
2615
+ }
2616
+ function distJobSpec(req) {
2617
+ const envVars = {
2618
+ APP_DIR: req.appDir,
2619
+ // WHICH APP this binary is FOR, as distinct from which shell builds it. Without it the pool
2620
+ // can only infer an app from APP_DIR — i.e. the shell — so it baked the SHELL's content and
2621
+ // resolved the SHELL's OTA app id. A device-verified consequence: `himi dist --app demo-app`
2622
+ // produced a binary rendering himi_branded_home. The shell is the container; this is the app.
2623
+ HIMI_DIST_APP: req.app,
2624
+ // (HIMI_DIST_PROVISIONING_ENV is set at the END of this object, after `req.env` — see there.)
2625
+ // run-dist gates iOS on DIST_ENABLE_IOS / DIST_PROVISIONING_PROFILE / an explicit
2626
+ // `[dist:ios]` IN THE COMMIT MESSAGE. A serve-brokered job has no commit by design, and
2627
+ // `--force` only bypasses the `[dist]` tag gate — so without this an `--platform ios`
2628
+ // request would skip the build and finish as SUCCESS with no install link. Asking for iOS
2629
+ // through the API IS the explicit opt-in that `[dist:ios]` represents.
2630
+ ...req.platform === "ios" ? { DIST_ENABLE_IOS: "1" } : {},
2631
+ ...req.release ? { HIMI_DIST_RELEASE_PIN: req.release } : {},
2632
+ ...req.contractUrl ? { HIMI_DIST_CONTRACT_URL: req.contractUrl } : {},
2633
+ // Where the pool fetches the RELEASE to bake its offline seed from. Its absence is what makes
2634
+ // the classic Falcon `[dist]` path fall back to building the seed from repo source.
2635
+ ...req.seedUrl ? { HIMI_DIST_SEED_URL: req.seedUrl } : {},
2636
+ ...req.platform === "android" && req.androidConfigUrl ? { HIMI_DIST_ANDROID_CONFIG_URL: req.androidConfigUrl } : {},
2637
+ // Where the pool fetches its SIGNING IDENTITY (profile + p12 + password). A URL, not the
2638
+ // bytes: the envelope is ~20KB of base64 and `command` is capped at MAX_COMMAND_CHARS — the
2639
+ // same reason the brand contract travels this way. It also keeps key material out of the
2640
+ // job params, which the pool echoes into its log.
2641
+ ...req.signingUrl ? { HIMI_DIST_SIGNING_URL: req.signingUrl } : {},
2642
+ // WHICH KEYSTORE the Android build signs with. Android only — on an iOS job this would be a
2643
+ // variable nothing reads, and dispatching one implies a decision that was never made.
2644
+ // A bundle dispatches the QA key BY NAME, never `wix-keystore`. `--aab` always means the Wix
2645
+ // production key, and the new run-dist derives that from DIST_ANDROID_FORMAT itself, overriding
2646
+ // this. Naming `wix-keystore` here would reach a run-dist OLDER than `--aab` too (the pool builds
2647
+ // the release's own commit): it ignores the format, builds an installable APK, signs it with the
2648
+ // Wix production key and uploads and announces it — the exact artifact the self-serve 403
2649
+ // withholds. Naming NOTHING is not safe either: a pool machine with DIST_ANDROID_USE_WIX_KEYSTORE=1
2650
+ // then hands that old runner the Wix key anyway. A named QA key outranks that machine setting, so
2651
+ // an old runner signs an ordinary QA APK and the job fails as `android_format_not_built`.
2652
+ ...req.platform === "android" && req.androidSigning ? { DIST_ANDROID_SIGNING: req.androidFormat === "aab" ? "qa-keystore" : req.androidSigning } : {},
2653
+ // A Play bundle instead of an APK. Only when asked: the APK default needs no parameter, and a
2654
+ // runner that predates the variable ignores it — which the result callback catches.
2655
+ ...req.platform === "android" && req.androidFormat === "aab" ? { DIST_ANDROID_FORMAT: "aab" } : {},
2656
+ ...req.previewRoute ? { HIMI_DIST_PREVIEW_ROUTE: req.previewRoute } : {},
2657
+ ...req.callbackUrl ? { HIMI_DIST_CALLBACK_URL: req.callbackUrl } : {},
2658
+ ...req.callbackToken ? { HIMI_DIST_CALLBACK_TOKEN: req.callbackToken } : {},
2659
+ // The per-job artifact path component. Without it two concurrent jobs for one
2660
+ // app+shell+platform+commit wrote the same ipa, manifest.plist and install.html, and the
2661
+ // loser's install page then handed a tester the other build's binary.
2662
+ ...req.jobNonce ? { HIMI_DIST_JOB_NONCE: req.jobNonce } : {},
2663
+ // Who to DM the install link to. See DistJobRequest.requestedBy: the git-history guess the pool
2664
+ // falls back to misroutes brokered builds to an unrelated colleague.
2665
+ ...req.requestedBy ? { HIMI_DIST_REQUESTED_BY: req.requestedBy } : {},
2666
+ ...req.env,
2667
+ ...req.buildOrigin ? { HIMI_DIST_BUILD_ORIGIN: req.buildOrigin } : {},
2668
+ // AFTER `req.env`, deliberately. This one field is the SHELL's declared answer, and it is the
2669
+ // only thing standing between owner-lite and a build that reads the fetched contract under
2670
+ // branded-lite's key — i.e. ignores it and ships unbranded while reporting success. `req.env`
2671
+ // is operator config (`deps.env`), so a stray HIMI_DIST_PROVISIONING_ENV there is a plausible
2672
+ // misconfiguration, and it would silently unbrand EVERY shell at once. Generic overrides do
2673
+ // not get to answer a per-shell question.
2674
+ ...req.provisioningEnv ? { HIMI_DIST_PROVISIONING_ENV: req.provisioningEnv } : {}
2675
+ };
2676
+ const command = `dist:${req.platform} ${DIST_PARAMS_FLAG} ${encodeDistParams(envVars)}`;
2677
+ if (command.length > MAX_COMMAND_CHARS) {
2678
+ throw new Error(
2679
+ `dist command is ${command.length} chars (cap ${MAX_COMMAND_CHARS}); shrink the job env \u2014 pass large values as a URL the Mac job fetches, as the brand contract does`
2680
+ );
2681
+ }
2682
+ return {
2683
+ gitUrl: req.gitUrl ?? DEFAULT_GIT_URL,
2684
+ gitCommitSha: req.gitCommitSha,
2685
+ packageName: DIST_PACKAGE_NAME,
2686
+ command,
2687
+ // Job Service renders this metadata in Buildkite's job label. User labels are sanitized,
2688
+ // and the runner also carries buildOrigin so it does not treat them as GitHub PR refs.
2689
+ branchName: req.buildLabel ?? req.branchName ?? "master",
2690
+ envVars,
2691
+ ...req.xcodeVersion ? { xcodeVersion: req.xcodeVersion } : {}
2692
+ };
2693
+ }
2694
+ var IN_FLIGHT, SCOPE_CREATE, SCOPE_READ, SCOPE_CANCEL, DEFAULT_BASE_URL, DEFAULT_OAUTH_URL, TOKEN_TTL_MS, JobServiceError, JobServiceBroker, CUSTOMER_UPLOAD_LEASE_MS, MAX_MEASURABLE_IDENTITY_WINDOW, DIST_SHELLS, DEFAULT_DIST_SHELL, DEFAULT_BORROWABLE_IOS_IDS, BUNDLE_ID_RE, DEFAULT_DIST_SIGNING, WIX_MANAGED_SIGNING_ALIASES, DEFAULT_ANDROID_SIGNING, ANDROID_SIGNING_POLICIES, ANDROID_SIGNING_ALIASES, DEFAULT_ANDROID_FORMAT, ANDROID_FORMATS, ANDROID_FORMAT_NOT_BUILT, ANDROID_FORMAT_WRONG_SIGNER, ANDROID_FORMAT_NOT_REQUESTED, DEFAULT_GIT_URL, DIST_PACKAGE_NAME, DIST_PARAMS_FLAG, MAX_COMMAND_CHARS;
2384
2695
  var init_dist_jobs = __esm({
2385
2696
  "../../core/serve-control/src/dist-jobs.ts"() {
2697
+ IN_FLIGHT = /* @__PURE__ */ new Set(["CREATED", "QUEUED", "WORKING"]);
2698
+ SCOPE_CREATE = "MRE.JOB_CREATE";
2699
+ SCOPE_READ = "MRE.JOB_READ";
2700
+ SCOPE_CANCEL = "MRE.CANCEL_JOB";
2701
+ DEFAULT_BASE_URL = "https://bo.wix.com/job-service";
2702
+ DEFAULT_OAUTH_URL = "https://www.wixapis.com/oauth/access";
2386
2703
  TOKEN_TTL_MS = 5 * 60 * 1e3;
2704
+ JobServiceError = class extends Error {
2705
+ constructor(message2, status, body) {
2706
+ super(message2);
2707
+ this.status = status;
2708
+ this.body = body;
2709
+ this.name = "JobServiceError";
2710
+ }
2711
+ };
2712
+ JobServiceBroker = class {
2713
+ baseUrl;
2714
+ oauthUrl;
2715
+ fetchImpl;
2716
+ now;
2717
+ creds;
2718
+ /** scope -> { token, expiresAt } */
2719
+ tokens = /* @__PURE__ */ new Map();
2720
+ constructor(opts) {
2721
+ if (!opts.creds?.appDefId || !opts.creds?.appSecret) {
2722
+ throw new JobServiceError("job service credentials missing (POCO_APP_DEF_ID / POCO_APP_SECRET)");
2723
+ }
2724
+ this.creds = opts.creds;
2725
+ this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
2726
+ this.oauthUrl = opts.oauthUrl ?? DEFAULT_OAUTH_URL;
2727
+ this.fetchImpl = opts.fetchImpl ?? fetch;
2728
+ this.now = opts.now ?? Date.now;
2729
+ }
2730
+ /** Mint (or reuse) a token for one MRE scope. */
2731
+ async token(scope2) {
2732
+ const cached3 = this.tokens.get(scope2);
2733
+ if (cached3 && cached3.expiresAt > this.now()) return cached3.token;
2734
+ const res = await this.fetchImpl(this.oauthUrl, {
2735
+ method: "POST",
2736
+ headers: { "content-type": "application/json" },
2737
+ body: JSON.stringify({
2738
+ grant_type: "client_credentials",
2739
+ client_id: this.creds.appDefId,
2740
+ client_secret: this.creds.appSecret,
2741
+ scope: scope2
2742
+ })
2743
+ });
2744
+ const text11 = await res.text();
2745
+ if (!res.ok) {
2746
+ throw new JobServiceError(`oauth failed for scope ${scope2}`, res.status, truncate(text11));
2747
+ }
2748
+ let token;
2749
+ try {
2750
+ token = JSON.parse(text11).access_token;
2751
+ } catch {
2752
+ throw new JobServiceError(`oauth returned non-JSON for scope ${scope2}`, res.status, truncate(text11));
2753
+ }
2754
+ if (!token) throw new JobServiceError(`oauth returned no access_token for scope ${scope2}`, res.status);
2755
+ this.tokens.set(scope2, { token, expiresAt: this.now() + TOKEN_TTL_MS });
2756
+ return token;
2757
+ }
2758
+ async call(scope2, method, path2, body) {
2759
+ const token = await this.token(scope2);
2760
+ const res = await this.fetchImpl(`${this.baseUrl}${path2}`, {
2761
+ method,
2762
+ // BARE token, not `Bearer <token>` — matches jobServiceApi's own header.
2763
+ headers: { Authorization: token, ...body ? { "content-type": "application/json" } : {} },
2764
+ ...body ? { body: JSON.stringify(body) } : {}
2765
+ });
2766
+ const text11 = await res.text();
2767
+ if (!res.ok) {
2768
+ throw new JobServiceError(`job service ${method} ${path2} failed`, res.status, truncate(text11));
2769
+ }
2770
+ if (!text11) return {};
2771
+ try {
2772
+ return JSON.parse(text11);
2773
+ } catch {
2774
+ throw new JobServiceError(`job service ${method} ${path2} returned non-JSON`, res.status, truncate(text11));
2775
+ }
2776
+ }
2777
+ /**
2778
+ * Create a NODE_COMMAND job on the Mac (or Linux) pool. Returns as soon as Job Service has
2779
+ * queued it — this never waits for the build.
2780
+ */
2781
+ async createNodeCommand(spec, os2 = "mac", notify) {
2782
+ for (const key3 of ["gitUrl", "gitCommitSha", "packageName", "command", "branchName"]) {
2783
+ if (!spec[key3]) throw new JobServiceError(`createNodeCommand: missing ${key3}`);
2784
+ }
2785
+ const optionsKey = os2 === "linux" ? "linuxNodeCommandOptions" : "nodeCommandOptions";
2786
+ const type = os2 === "linux" ? "LINUX_NODE_COMMAND" : "NODE_COMMAND";
2787
+ const options = {
2788
+ packageManager: "YARN",
2789
+ // the only value Job Service accepts
2790
+ gitUrl: spec.gitUrl,
2791
+ gitCommitSha: spec.gitCommitSha,
2792
+ packageName: spec.packageName,
2793
+ command: spec.command,
2794
+ branchName: spec.branchName,
2795
+ ...spec.envVars ? { envVars: spec.envVars } : {}
2796
+ };
2797
+ if (os2 === "mac" && spec.xcodeVersion) options.xcodeVersion = spec.xcodeVersion;
2798
+ const subscriptions = {
2799
+ ...notify?.ownershipTags?.length ? { subscribedOwnershipTags: notify.ownershipTags } : {},
2800
+ ...notify?.userIds?.length ? { subscribedUserIds: notify.userIds } : {}
2801
+ };
2802
+ const data = await this.call(SCOPE_CREATE, "POST", "/v1/job", {
2803
+ job: { type, [optionsKey]: options, ...subscriptions }
2804
+ });
2805
+ const job = data?.job;
2806
+ if (!job?.id) throw new JobServiceError("job service create returned no job id");
2807
+ return snapshot(job);
2808
+ }
2809
+ /** Current state of a job. */
2810
+ async read(jobId) {
2811
+ const data = await this.call(SCOPE_READ, "GET", `/v1/job/${encodeURIComponent(jobId)}`);
2812
+ const job = data?.job;
2813
+ if (!job) throw new JobServiceError(`job service read returned no job for ${jobId}`);
2814
+ return snapshot({ id: jobId, ...job });
2815
+ }
2816
+ /** Ask Job Service to cancel a queued/running job. */
2817
+ async cancel(jobId) {
2818
+ await this.call(SCOPE_CANCEL, "POST", `/v1/job/${encodeURIComponent(jobId)}/cancel`);
2819
+ }
2820
+ };
2387
2821
  CUSTOMER_UPLOAD_LEASE_MS = 15 * 60 * 1e3;
2822
+ MAX_MEASURABLE_IDENTITY_WINDOW = 100;
2388
2823
  DIST_SHELLS = {
2389
2824
  "branded-lite": {
2390
2825
  appDir: "apps/himi-branded-lite",
@@ -2437,6 +2872,11 @@ var init_dist_jobs = __esm({
2437
2872
  }
2438
2873
  };
2439
2874
  DEFAULT_DIST_SHELL = "branded-lite";
2875
+ DEFAULT_BORROWABLE_IOS_IDS = ["ent.*"];
2876
+ BUNDLE_ID_RE = /^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$/;
2877
+ DEFAULT_DIST_SIGNING = "wix-managed";
2878
+ WIX_MANAGED_SIGNING_ALIASES = ["wix-managed", "wix", "pool-enterprise"];
2879
+ DEFAULT_ANDROID_SIGNING = "qa-keystore";
2440
2880
  ANDROID_SIGNING_POLICIES = ["qa-keystore", "wix-keystore", "debug"];
2441
2881
  ANDROID_SIGNING_ALIASES = {
2442
2882
  // The canonical spellings, derived so the two lists cannot drift apart.
@@ -2446,6 +2886,15 @@ var init_dist_jobs = __esm({
2446
2886
  wix: "wix-keystore",
2447
2887
  oneapp: "wix-keystore"
2448
2888
  };
2889
+ DEFAULT_ANDROID_FORMAT = "apk";
2890
+ ANDROID_FORMATS = ["apk", "aab"];
2891
+ ANDROID_FORMAT_NOT_BUILT = "android_format_not_built";
2892
+ ANDROID_FORMAT_WRONG_SIGNER = "android_format_wrong_signer";
2893
+ ANDROID_FORMAT_NOT_REQUESTED = "android_format_not_requested";
2894
+ DEFAULT_GIT_URL = "https://github.com/wix-private/himalaya";
2895
+ DIST_PACKAGE_NAME = "@himalaya/native-ci";
2896
+ DIST_PARAMS_FLAG = "--himi-dist";
2897
+ MAX_COMMAND_CHARS = 2e3;
2449
2898
  }
2450
2899
  });
2451
2900
 
@@ -9873,16 +10322,16 @@ function scanWidgetPayload(node, family, path2, bindings, disallowed, timeRefs,
9873
10322
  scanWidgetPayload(v2, family, `${path2}.${k2}`, bindings, disallowed, timeRefs, intents, actionSites2);
9874
10323
  }
9875
10324
  }
9876
- function validateWidget(snapshot2) {
10325
+ function validateWidget(snapshot3) {
9877
10326
  const validate5 = compileWidgetSchema();
9878
- const schemaOk = validate5(snapshot2);
10327
+ const schemaOk = validate5(snapshot3);
9879
10328
  const schemaErrors = schemaOk ? [] : validate5.errors ?? [];
9880
10329
  const bindingViolations = [];
9881
10330
  const disallowedComponents = [];
9882
10331
  const timeRefViolations = [];
9883
10332
  const intentViolations = [];
9884
10333
  const actionSites2 = [];
9885
- const snap = snapshot2 ?? {};
10334
+ const snap = snapshot3 ?? {};
9886
10335
  const family = snap.family;
9887
10336
  scanWidgetPayload(snap.payload, family, "$.payload", bindingViolations, disallowedComponents, timeRefViolations, intentViolations, actionSites2);
9888
10337
  if (snap.placeholderWhenStale !== void 0) {
@@ -13556,12 +14005,12 @@ function responseBytes(value2) {
13556
14005
  return 0;
13557
14006
  }
13558
14007
  }
13559
- function restoredState(def, snapshot2) {
14008
+ function restoredState(def, snapshot3) {
13560
14009
  const declared = def?.state ?? {};
13561
- if (!snapshot2 || typeof snapshot2 !== "object" || Array.isArray(snapshot2)) return declared;
14010
+ if (!snapshot3 || typeof snapshot3 !== "object" || Array.isArray(snapshot3)) return declared;
13562
14011
  const fresh = [def?.notHydrated, def?.perVisit].flatMap((keys) => Array.isArray(keys) ? keys : []);
13563
14012
  const merged = { ...declared };
13564
- for (const [key3, value2] of Object.entries(snapshot2)) {
14013
+ for (const [key3, value2] of Object.entries(snapshot3)) {
13565
14014
  if (key3 !== "_error" && !fresh.includes(key3)) merged[key3] = value2;
13566
14015
  }
13567
14016
  return merged;
@@ -22600,6 +23049,7 @@ function resolveIdiom(tokens2, ctx = {}) {
22600
23049
  var IDIOM_CONTEXTS, UNSAFE_KEYS;
22601
23050
  var init_idiom = __esm({
22602
23051
  "../../core/ts/src/idiom.ts"() {
23052
+ "use strict";
22603
23053
  IDIOM_CONTEXTS = {
22604
23054
  ios: { native: ["ios"] },
22605
23055
  android: { native: ["material"] },
@@ -28061,7 +28511,7 @@ function json(res, status, obj5) {
28061
28511
  res.setHeader("content-type", "application/json; charset=utf-8");
28062
28512
  res.end(body);
28063
28513
  }
28064
- function truncate(s2) {
28514
+ function truncate2(s2) {
28065
28515
  if (s2 === void 0) return void 0;
28066
28516
  return s2.length > BODY_CAP ? s2.slice(0, BODY_CAP) + `\u2026[+${s2.length - BODY_CAP}B]` : s2;
28067
28517
  }
@@ -28076,8 +28526,8 @@ function redactHeaders(h2) {
28076
28526
  }
28077
28527
  function recordWixTraffic(gw, app, adminUrl, ev) {
28078
28528
  const full = { ...ev, id: ++gw.seq, app };
28079
- full.reqBody = truncate(redactBodyString(full.reqBody));
28080
- full.respBody = truncate(redactBodyString(full.respBody));
28529
+ full.reqBody = truncate2(redactBodyString(full.reqBody));
28530
+ full.respBody = truncate2(redactBodyString(full.respBody));
28081
28531
  gw.traffic.push(full);
28082
28532
  if (gw.traffic.length > TRAFFIC_CAP) gw.traffic.splice(0, gw.traffic.length - TRAFFIC_CAP);
28083
28533
  const text11 = `event: wix.traffic
@@ -116688,7 +117138,7 @@ function ensureListAnimateRuntime() {
116688
117138
  listAnimateRuntimeInstalled = true;
116689
117139
  const prevRects = /* @__PURE__ */ new WeakMap();
116690
117140
  const rowKey = (c2) => c2.dataset?.componentId ?? c2.querySelector("[data-component-id]")?.dataset?.componentId;
116691
- const snapshot2 = (host) => {
117141
+ const snapshot3 = (host) => {
116692
117142
  const m2 = /* @__PURE__ */ new Map();
116693
117143
  Array.from(host.children).forEach((c2) => {
116694
117144
  const id = rowKey(c2);
@@ -116700,7 +117150,7 @@ function ensureListAnimateRuntime() {
116700
117150
  const flip = (host) => {
116701
117151
  const prev = prevRects.get(host);
116702
117152
  if (!prev) {
116703
- snapshot2(host);
117153
+ snapshot3(host);
116704
117154
  return;
116705
117155
  }
116706
117156
  const next = /* @__PURE__ */ new Map();
@@ -116743,7 +117193,7 @@ function ensureListAnimateRuntime() {
116743
117193
  const scan = () => {
116744
117194
  document.querySelectorAll(hostSel).forEach((h2) => {
116745
117195
  if (!prevRects.has(h2)) {
116746
- snapshot2(h2);
117196
+ snapshot3(h2);
116747
117197
  childObs.observe(h2, { childList: true });
116748
117198
  }
116749
117199
  });
@@ -121243,7 +121693,7 @@ var init_renderNode = __esm({
121243
121693
  }, [startPositionPath, boundStartPosition, status]);
121244
121694
  const emit = React12.useCallback((action, extra = {}) => {
121245
121695
  if (!action || !ctx.onAction) return;
121246
- const snapshot2 = {
121696
+ const snapshot3 = {
121247
121697
  sessionId,
121248
121698
  state: status,
121249
121699
  status,
@@ -121258,7 +121708,7 @@ var init_renderNode = __esm({
121258
121708
  artist: String(item?.artist ?? ""),
121259
121709
  error
121260
121710
  };
121261
- ctx.onAction({ ...action, args: { ...action.args ?? {}, ...snapshot2, ...extra }, params: { ...action.params ?? {}, sessionId, index: String(index), ...Object.fromEntries(Object.entries(extra).map(([k2, v2]) => [k2, String(v2)])) } });
121711
+ ctx.onAction({ ...action, args: { ...action.args ?? {}, ...snapshot3, ...extra }, params: { ...action.params ?? {}, sessionId, index: String(index), ...Object.fromEntries(Object.entries(extra).map(([k2, v2]) => [k2, String(v2)])) } });
121262
121712
  }, [ctx.onAction, sessionId, status, index, position, duration2, item, buffered, rate, props.muted, fullscreen, error]);
121263
121713
  React12.useEffect(() => {
121264
121714
  if (!isUnsupportedProvider || status === "error") return;
@@ -127551,15 +128001,15 @@ function interpretProduce(services, parsed) {
127551
128001
  }
127552
128002
  return { services, widgets, views };
127553
128003
  }
127554
- function contentEtag(snapshot2) {
128004
+ function contentEtag(snapshot3) {
127555
128005
  const canonical7 = JSON.stringify({
127556
- widgetId: snapshot2.widgetId,
127557
- family: snapshot2.family,
127558
- size: snapshot2.size,
127559
- payload: snapshot2.payload,
127560
- deepLink: snapshot2.deepLink,
128006
+ widgetId: snapshot3.widgetId,
128007
+ family: snapshot3.family,
128008
+ size: snapshot3.size,
128009
+ payload: snapshot3.payload,
128010
+ deepLink: snapshot3.deepLink,
127561
128011
  // Lever A — a timeline change must bump the etag (frame values/times are content).
127562
- frames: snapshot2.frames ?? null
128012
+ frames: snapshot3.frames ?? null
127563
128013
  });
127564
128014
  return "sha256-" + createHash15("sha256").update(canonical7).digest("base64");
127565
128015
  }
@@ -127602,8 +128052,8 @@ function freezeCaptured(def, widgetId, opts = {}) {
127602
128052
  generatedAt: opts.generatedAt ?? Math.floor(Date.now() / 1e3),
127603
128053
  ...frames ? { frames } : {}
127604
128054
  };
127605
- const snapshot2 = { ...base, contentEtag: contentEtag(base) };
127606
- const result = validateWidget(snapshot2);
128055
+ const snapshot3 = { ...base, contentEtag: contentEtag(base) };
128056
+ const result = validateWidget(snapshot3);
127607
128057
  if (!result.ok) {
127608
128058
  const reasons = [];
127609
128059
  if (result.bindingViolations.length) {
@@ -127625,7 +128075,7 @@ function freezeCaptured(def, widgetId, opts = {}) {
127625
128075
  }
127626
128076
  throw new Error(`widget "${widgetId}" produced an invalid snapshot \u2014 ${reasons.join(" | ")}`);
127627
128077
  }
127628
- return snapshot2;
128078
+ return snapshot3;
127629
128079
  }
127630
128080
  function viewWidgetId(viewId, family) {
127631
128081
  return `${viewId}.${family}`;
@@ -127725,8 +128175,8 @@ function freezeOverrideSnapshot(input, opts = {}) {
127725
128175
  ...input.placeholderWhenStale !== void 0 ? { placeholderWhenStale: input.placeholderWhenStale } : {},
127726
128176
  ...input.frames !== void 0 ? { frames: input.frames } : {}
127727
128177
  };
127728
- const snapshot2 = { ...base, contentEtag: contentEtag(base) };
127729
- const result = validateWidget(snapshot2);
128178
+ const snapshot3 = { ...base, contentEtag: contentEtag(base) };
128179
+ const result = validateWidget(snapshot3);
127730
128180
  if (!result.ok) {
127731
128181
  const reasons = [];
127732
128182
  if (result.bindingViolations.length) {
@@ -127748,7 +128198,7 @@ function freezeOverrideSnapshot(input, opts = {}) {
127748
128198
  }
127749
128199
  throw new Error(`override snapshot for "${input.widgetId}" is invalid \u2014 ${reasons.join(" | ")}`);
127750
128200
  }
127751
- return snapshot2;
128201
+ return snapshot3;
127752
128202
  }
127753
128203
  var MAX_TIMELINE_FRAMES, WIDGET_BUDGET_MS, realmPromise, WidgetRealmUnavailableError, PRODUCE_CACHE_TTL_MS, MAX_PRODUCE_CACHE_ENTRIES, MAX_PRODUCE_ENTRY_BYTES, MAX_PRODUCE_CACHE_BYTES, produceCache, produceCacheGeneration, CAPTURE_SLOT, READBACK_SERVICE;
127754
128204
  var init_widgets = __esm({
@@ -130386,9 +130836,9 @@ async function buildSnapshotForId(widgetId, dir) {
130386
130836
  try {
130387
130837
  const js2 = loaded.bytes.toString("utf8");
130388
130838
  if (entry.view) {
130389
- const snapshot2 = await buildViewSnapshot(js2, entry.view.viewId, entry.view.surfaceId);
130390
- if (!snapshot2) return { ok: false, status: 404, error: "widget_not_found" };
130391
- return { ok: true, snapshot: snapshot2 };
130839
+ const snapshot3 = await buildViewSnapshot(js2, entry.view.viewId, entry.view.surfaceId);
130840
+ if (!snapshot3) return { ok: false, status: 404, error: "widget_not_found" };
130841
+ return { ok: true, snapshot: snapshot3 };
130392
130842
  }
130393
130843
  return { ok: true, snapshot: await buildWidgetSnapshot(js2, widgetId) };
130394
130844
  } catch (err) {
@@ -133649,7 +134099,7 @@ __export(screen_crawl_exports, {
133649
134099
  recordScreenTraffic: () => recordScreenTraffic,
133650
134100
  renderedNameGaps: () => renderedNameGaps,
133651
134101
  rootOnAppear: () => rootOnAppear,
133652
- snapshot: () => snapshot,
134102
+ snapshot: () => snapshot2,
133653
134103
  structuralSignature: () => structuralSignature,
133654
134104
  unverifiableNodes: () => unverifiableNodes,
133655
134105
  workerArgs: () => workerArgs
@@ -134387,7 +134837,7 @@ function hasOwnError(state) {
134387
134837
  }
134388
134838
  return false;
134389
134839
  }
134390
- function snapshot(w2) {
134840
+ function snapshot2(w2) {
134391
134841
  const state = {};
134392
134842
  const defects = {};
134393
134843
  for (const [name, h2] of Object.entries(w2.handles)) {
@@ -134453,7 +134903,7 @@ async function bootScreenWorker(opts) {
134453
134903
  }
134454
134904
  }
134455
134905
  for (let i2 = 0; i2 < 20; i2++) await yieldMacrotask();
134456
- const { state, defects } = snapshot(w2);
134906
+ const { state, defects } = snapshot2(w2);
134457
134907
  return { ...w2, hostCalls, deniedCapabilities, state: Object.keys(state).length ? state : w2.services, defects, ...deadLifecycle ? { deadLifecycle } : {} };
134458
134908
  }
134459
134909
  function effectSince(w2, mark, action, componentId) {
@@ -134575,7 +135025,7 @@ async function crawlScreenJourney(opts) {
134575
135025
  fail6(`journey "${opts.journey.name}" cannot find ${step.component ? `${step.component} \u2192 ` : ""}${step.action} in this screen`, step);
134576
135026
  continue;
134577
135027
  }
134578
- const state = snapshot(worker).state;
135028
+ const state = snapshot2(worker).state;
134579
135029
  const site = effectiveSiteFor(declared, state);
134580
135030
  if (!site) {
134581
135031
  fail6(`journey "${opts.journey.name}" cannot dispatch ${step.action}: its list has no rendered row`, step);
@@ -134602,7 +135052,7 @@ async function crawlScreenJourney(opts) {
134602
135052
  const stepEffect = effectSince(worker, mark, site.actionId, site.componentId);
134603
135053
  if (stepEffect) effects.push(stepEffect);
134604
135054
  }
134605
- const settled = snapshot(worker);
135055
+ const settled = snapshot2(worker);
134606
135056
  if (worker.errors.length) {
134607
135057
  for (const error of worker.errors) fail6(`journey "${opts.journey.name}" worker error: ${error.service}: ${error.message}`);
134608
135058
  }
@@ -135023,7 +135473,7 @@ async function crawlScreenInner(opts) {
135023
135473
  continue;
135024
135474
  }
135025
135475
  const trigger = { tap: site.componentId, key: site.key, action: site.actionId };
135026
- const preState = snapshot(w2).state;
135476
+ const preState = snapshot2(w2).state;
135027
135477
  const effective = effectiveSiteFor(site, preState);
135028
135478
  if (!effective) {
135029
135479
  actionsSkipped.push({ action: site.actionId, why: "no-row-value" });
@@ -135042,7 +135492,7 @@ async function crawlScreenInner(opts) {
135042
135492
  errors.push({ service: parsed.service, message: `${parsed.action}: ${errText(e2)}`, ...e2.stack ? { stack: e2.stack } : {}, argless: workerArgs(dispatched).length === 0 });
135043
135493
  }
135044
135494
  exercised += 1;
135045
- const { state, defects } = snapshot(w2);
135495
+ const { state, defects } = snapshot2(w2);
135046
135496
  check(trigger, state, errors, defects);
135047
135497
  reportDenied(w2.deniedCapabilities.slice(deniedBefore), trigger);
135048
135498
  const navCountBefore = navEdges.length;
@@ -135301,7 +135751,7 @@ async function bootAndSnapshotState(opts) {
135301
135751
  }
135302
135752
  try {
135303
135753
  for (let i2 = 0; i2 < 20; i2++) await yieldMacrotask();
135304
- return snapshot(w2).state;
135754
+ return snapshot2(w2).state;
135305
135755
  } finally {
135306
135756
  w2.dispose();
135307
135757
  }
@@ -135376,7 +135826,7 @@ async function crawlScreenDeep(opts, deep) {
135376
135826
  threwAt = "unreachable";
135377
135827
  break;
135378
135828
  }
135379
- const effective = effectiveSiteFor(site, snapshot(w2).state);
135829
+ const effective = effectiveSiteFor(site, snapshot2(w2).state);
135380
135830
  if (!effective) {
135381
135831
  threwAt = "unreachable";
135382
135832
  break;
@@ -135415,7 +135865,7 @@ async function crawlScreenDeep(opts, deep) {
135415
135865
  }
135416
135866
  for (let i2 = 0; i2 < 20; i2++) await yieldMacrotask();
135417
135867
  flushStep();
135418
- const { state, defects } = snapshot(w2);
135868
+ const { state, defects } = snapshot2(w2);
135419
135869
  const hash2 = deepStateSignature(state, defects);
135420
135870
  if (visited.has(hash2)) continue;
135421
135871
  visited.add(hash2);
@@ -135549,7 +135999,7 @@ async function crawlScreenRelations(opts) {
135549
135999
  const w2 = await boot();
135550
136000
  try {
135551
136001
  if (w2.errors.length) return null;
135552
- let prev = snapshot(w2).state;
136002
+ let prev = snapshot2(w2).state;
135553
136003
  const bootState = prev;
135554
136004
  for (const [i2, site] of path2.entries()) {
135555
136005
  const parsed = parseWorkerAction(site.actionId);
@@ -135566,7 +136016,7 @@ async function crawlScreenRelations(opts) {
135566
136016
  }
135567
136017
  for (let k2 = 0; k2 < 20; k2++) await yieldMacrotask();
135568
136018
  if (w2.errors.length) return null;
135569
- prev = snapshot(w2).state;
136019
+ prev = snapshot2(w2).state;
135570
136020
  }
135571
136021
  return prev;
135572
136022
  } finally {
@@ -136773,7 +137223,7 @@ async function liveRenderServices(bundleJs, opts) {
136773
137223
  }
136774
137224
  }
136775
137225
  if (opts.host) await settleUnawaitedWork();
136776
- return { services: snapshot(w2).state, calls: w2.calls, errors: w2.errors };
137226
+ return { services: snapshot2(w2).state, calls: w2.calls, errors: w2.errors };
136777
137227
  } finally {
136778
137228
  w2.dispose();
136779
137229
  }
@@ -136788,7 +137238,7 @@ async function loadAndSettle(bundleJs, options) {
136788
137238
  const w2 = await bootBundleWorker(bundleJs, options);
136789
137239
  try {
136790
137240
  await settleUnawaitedWork();
136791
- return { services: snapshot(w2).state, calls: w2.calls, errors: w2.errors };
137241
+ return { services: snapshot2(w2).state, calls: w2.calls, errors: w2.errors };
136792
137242
  } finally {
136793
137243
  w2.dispose();
136794
137244
  }
@@ -137492,7 +137942,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
137492
137942
  }
137493
137943
  }
137494
137944
  }
137495
- const snapshot2 = await snapshotTokens(
137945
+ const snapshot3 = await snapshotTokens(
137496
137946
  app,
137497
137947
  base,
137498
137948
  async () => {
@@ -137522,10 +137972,10 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
137522
137972
  },
137523
137973
  (text11) => store.putTokens(app, text11)
137524
137974
  );
137525
- if (snapshot2.hash) tokensHash = snapshot2.hash;
137526
- if (snapshot2.failure) {
137527
- tokensSnapshotFailure = snapshot2.failure;
137528
- console.error(snapshot2.failure.message);
137975
+ if (snapshot3.hash) tokensHash = snapshot3.hash;
137976
+ if (snapshot3.failure) {
137977
+ tokensSnapshotFailure = snapshot3.failure;
137978
+ console.error(snapshot3.failure.message);
137529
137979
  }
137530
137980
  Object.assign(catalogs, await bakeCatalogs(
137531
137981
  app,
@@ -140159,13 +140609,237 @@ var init_wix_owner = __esm({
140159
140609
  }
140160
140610
  });
140161
140611
 
140612
+ // src/captcha-auth.ts
140613
+ function captchaAuthFinding(auth, siteId, platforms) {
140614
+ const login = auth;
140615
+ if (login?.providerId !== "wix-consumer") return void 0;
140616
+ const inline = login.config?.captcha;
140617
+ const hasInline = typeof inline === "object" && inline !== null;
140618
+ const inlineFor = (platform) => hasInline && !!inline[platform];
140619
+ const inlineKeys = platforms?.length ? platforms.every(inlineFor) : inlineFor("ios") || inlineFor("android");
140620
+ const site = siteId?.trim().toLowerCase();
140621
+ const bound2 = typeof login.config?.siteId === "string" ? login.config.siteId : "";
140622
+ const uncovered = hasInline ? (platforms ?? []).filter((platform) => !inlineFor(platform)) : [];
140623
+ if (bound2 && uncovered.length) return {
140624
+ code: "captcha_auth_inline_key_missing",
140625
+ keyless: true,
140626
+ message: `auth (WixConsumerAuthProvider) declares inline captcha keys but none for ${uncovered.join(" and ")}, and the inline block replaces the baked key file on device \u2014 a native ${uncovered.join("/")} build ships with no member CAPTCHA key. Add captcha.${uncovered[0]} (or remove the captcha block to use the baked key), push a new release, and build from it.`
140627
+ };
140628
+ if (!bound2) return {
140629
+ code: "captcha_auth_site_missing",
140630
+ keyless: true,
140631
+ message: `auth (WixConsumerAuthProvider) names no siteId, so a native build cannot carry a member CAPTCHA key: the build drops the key file and the device refuses the key \u2014 member sign-in on a CAPTCHA-protected site fails with "Complete verification to continue". Add siteId: "${site ?? "<the site's metaSiteId, lower case>"}" to WixConsumerAuthProvider in config.ts, push a new release, and build from it.`
140632
+ };
140633
+ if (bound2 !== bound2.trim().toLowerCase() && !inlineKeys) return {
140634
+ code: "captcha_auth_site_case",
140635
+ keyless: true,
140636
+ message: `auth (WixConsumerAuthProvider) names siteId "${bound2}", but member CAPTCHA keys are stored and matched under the lower-case id, exactly, so a native build would ship without one. Write siteId: "${bound2.trim().toLowerCase()}", push a new release, and build from it.`
140637
+ };
140638
+ if (site && bound2.trim().toLowerCase() !== site) return {
140639
+ code: "captcha_auth_site_mismatch",
140640
+ keyless: false,
140641
+ message: `auth (WixConsumerAuthProvider) names siteId "${bound2}", not ${site}, so a member CAPTCHA key for ${site} cannot bind to this app's login and native member sign-in on a CAPTCHA-protected site fails. Bind the login and the build to the same site.`
140642
+ };
140643
+ return void 0;
140644
+ }
140645
+ var init_captcha_auth = __esm({
140646
+ "src/captcha-auth.ts"() {
140647
+ }
140648
+ });
140649
+
140650
+ // ../../stdlib/auth-providers/wix/src/member-captcha-policy.ts
140651
+ function acceptsClientSiteKeyToken(option) {
140652
+ return CLIENT_SITE_KEY_OPTIONS.includes(option);
140653
+ }
140654
+ function asOption(value2) {
140655
+ return value2 === "ALWAYS" || value2 === "SUSPECTED_BOTS_ONLY" || value2 === "NEVER" ? value2 : "UNKNOWN";
140656
+ }
140657
+ function parseMemberCaptchaPolicy(raw) {
140658
+ const settings = raw?.settings ?? {};
140659
+ return {
140660
+ login: asOption(settings.loginRecaptchaOption),
140661
+ signup: asOption(settings.signupRecaptchaOption)
140662
+ };
140663
+ }
140664
+ var MEMBER_SITE_SETTINGS_URL, CLIENT_SITE_KEY_OPTIONS;
140665
+ var init_member_captcha_policy = __esm({
140666
+ "../../stdlib/auth-providers/wix/src/member-captcha-policy.ts"() {
140667
+ MEMBER_SITE_SETTINGS_URL = "https://manage.wix.com/_api/wix-sm-webapp/v1/collection/settings";
140668
+ CLIENT_SITE_KEY_OPTIONS = ["ALWAYS", "SUSPECTED_BOTS_ONLY"];
140669
+ }
140670
+ });
140671
+
140672
+ // src/site-analyze/rest.ts
140673
+ var rest_exports = {};
140674
+ __export(rest_exports, {
140675
+ WIX_API: () => WIX_API2,
140676
+ WixApiError: () => WixApiError2,
140677
+ classifyHttp: () => classifyHttp,
140678
+ compactWixstaticUrl: () => compactWixstaticUrl,
140679
+ isGenericWixFavicon: () => isGenericWixFavicon,
140680
+ makeTransport: () => makeTransport,
140681
+ mediaUrlFromLogo: () => mediaUrlFromLogo,
140682
+ ownerTransportFrom: () => ownerTransportFrom,
140683
+ probeCall: () => probeCall,
140684
+ resolveWixUrl: () => resolveWixUrl,
140685
+ uploadMediaBytes: () => uploadMediaBytes,
140686
+ visitorTransportFrom: () => visitorTransportFrom,
140687
+ wixMediaFileId: () => wixMediaFileId
140688
+ });
140689
+ function resolveWixUrl(path2) {
140690
+ if (/^https?:\/\//i.test(path2)) return path2;
140691
+ return `${WIX_API2}${path2}`;
140692
+ }
140693
+ function classifyHttp(status, count2) {
140694
+ if (status === 428) return "not-installed";
140695
+ if (status === 404) return "wrong-path";
140696
+ if (status && status >= 400) return "error";
140697
+ return count2 > 0 ? "ok" : "empty";
140698
+ }
140699
+ function makeTransport(fetchImpl, auth, timeoutMs = DEFAULT_TIMEOUT_MS2) {
140700
+ return async (method, path2, body) => {
140701
+ const headers2 = { Accept: "application/json" };
140702
+ if (auth?.token) headers2.Authorization = auth.token;
140703
+ if (auth?.msid) headers2["wix-site-id"] = auth.msid;
140704
+ if (body !== void 0) headers2["Content-Type"] = "application/json";
140705
+ const res = await fetchImpl(resolveWixUrl(path2), {
140706
+ method,
140707
+ headers: headers2,
140708
+ body: body === void 0 ? void 0 : JSON.stringify(body),
140709
+ signal: AbortSignal.timeout(timeoutMs)
140710
+ });
140711
+ const text11 = await res.text();
140712
+ let parsed = text11;
140713
+ try {
140714
+ parsed = text11 ? JSON.parse(text11) : null;
140715
+ } catch {
140716
+ }
140717
+ if (!res.ok) throw new WixApiError2(res.status, method, path2, parsed);
140718
+ return parsed;
140719
+ };
140720
+ }
140721
+ function ownerTransportFrom(fetchImpl, owner) {
140722
+ return makeTransport(fetchImpl, { token: owner.token, msid: owner.msid });
140723
+ }
140724
+ function visitorTransportFrom(fetchImpl, token, msid) {
140725
+ return makeTransport(fetchImpl, { token, msid });
140726
+ }
140727
+ async function probeCall(call, method, path2, body) {
140728
+ try {
140729
+ return { data: await call(method, path2, body), err: null };
140730
+ } catch (e2) {
140731
+ if (e2 instanceof WixApiError2) return { data: null, err: e2 };
140732
+ throw e2;
140733
+ }
140734
+ }
140735
+ function mediaUrlFromLogo(logo) {
140736
+ const s2 = (logo ?? "").trim();
140737
+ if (!s2) return null;
140738
+ if (/^https?:\/\//i.test(s2)) return s2;
140739
+ const id = s2.replace(/^wix:image:\/\/v1\//, "").split("/")[0].split("#")[0];
140740
+ if (!id) return null;
140741
+ if (/^https?:\/\//i.test(id)) return id;
140742
+ return `https://static.wixstatic.com/media/${id}`;
140743
+ }
140744
+ function isGenericWixFavicon(url) {
140745
+ try {
140746
+ const h2 = new URL(url).hostname.toLowerCase();
140747
+ return h2 === "wix.com" || h2.endsWith(".wix.com");
140748
+ } catch {
140749
+ return false;
140750
+ }
140751
+ }
140752
+ function wixMediaFileId(url) {
140753
+ const m2 = url.match(/static\.wixstatic\.com\/media\/([^/?#]+)/i);
140754
+ return m2?.[1] ? decodeURIComponent(m2[1]) : void 0;
140755
+ }
140756
+ function compactWixstaticUrl(url, maxEdge = 800) {
140757
+ if (!/static\.wixstatic\.com\/media\//i.test(url)) return url;
140758
+ if (/\.(svg|ico)(\?|#|$)/i.test(url.split("?")[0] ?? url)) return url;
140759
+ const w2 = url.match(/(?:\/|,)w_(\d+)/)?.[1];
140760
+ if (w2 && Number(w2) <= maxEdge) return url;
140761
+ const id = wixMediaFileId(url);
140762
+ if (!id) return url;
140763
+ return `https://static.wixstatic.com/media/${id}/v1/fill/w_${maxEdge},h_${maxEdge},al_c,q_80,enc_auto/file.jpg`;
140764
+ }
140765
+ async function uploadMediaBytes(call, fetchImpl, bytes2, fileName, mimeType) {
140766
+ const { uploadUrl } = await call(
140767
+ "POST",
140768
+ "/site-media/v1/files/generate-upload-url",
140769
+ { mimeType, fileName: `himi-business-book/${fileName}` }
140770
+ );
140771
+ const put = await fetchImpl(`${uploadUrl}?filename=${encodeURIComponent(fileName)}`, {
140772
+ method: "PUT",
140773
+ headers: { "Content-Type": mimeType },
140774
+ body: bytes2,
140775
+ signal: AbortSignal.timeout(12e4)
140776
+ });
140777
+ if (!put.ok) throw new Error(`media upload PUT failed: ${put.status}`);
140778
+ const payload = await put.json();
140779
+ const file2 = payload.file ?? payload;
140780
+ const id = file2?.id ?? file2?.fileId;
140781
+ const url = file2?.url ?? (id ? `https://static.wixstatic.com/media/${id}` : void 0);
140782
+ if (!id || !url) throw new Error("media upload returned no file id");
140783
+ return { id, url };
140784
+ }
140785
+ var WIX_API2, DEFAULT_TIMEOUT_MS2, WixApiError2;
140786
+ var init_rest = __esm({
140787
+ "src/site-analyze/rest.ts"() {
140788
+ WIX_API2 = "https://www.wixapis.com";
140789
+ DEFAULT_TIMEOUT_MS2 = 8e3;
140790
+ WixApiError2 = class extends Error {
140791
+ constructor(status, method, path2, body) {
140792
+ super(`${method} ${path2} \u2192 ${status}`);
140793
+ this.status = status;
140794
+ this.method = method;
140795
+ this.path = path2;
140796
+ this.body = body;
140797
+ this.name = "WixApiError";
140798
+ }
140799
+ };
140800
+ }
140801
+ });
140802
+
140803
+ // src/site-analyze/visitor-auth.ts
140804
+ var visitor_auth_exports = {};
140805
+ __export(visitor_auth_exports, {
140806
+ mintVisitorToken: () => mintVisitorToken
140807
+ });
140808
+ async function mintVisitorToken(fetchImpl, clientId) {
140809
+ const res = await fetchImpl(`${WIX_API2}/oauth2/token`, {
140810
+ method: "POST",
140811
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
140812
+ body: JSON.stringify({ clientId, grantType: "anonymous" }),
140813
+ signal: AbortSignal.timeout(8e3)
140814
+ });
140815
+ const text11 = await res.text();
140816
+ let parsed = text11;
140817
+ try {
140818
+ parsed = text11 ? JSON.parse(text11) : null;
140819
+ } catch {
140820
+ }
140821
+ if (!res.ok) throw new WixApiError2(res.status, "POST", "/oauth2/token", parsed);
140822
+ const token = parsed?.access_token ?? parsed?.accessToken;
140823
+ if (!token) throw new Error("visitor mint returned no access_token");
140824
+ return token;
140825
+ }
140826
+ var init_visitor_auth = __esm({
140827
+ "src/site-analyze/visitor-auth.ts"() {
140828
+ init_rest();
140829
+ }
140830
+ });
140831
+
140162
140832
  // src/captcha.ts
140163
140833
  var captcha_exports = {};
140164
140834
  __export(captcha_exports, {
140835
+ captchaAuthFinding: () => captchaAuthFinding,
140165
140836
  captchaContentInput: () => captchaContentInput,
140166
- runCaptcha: () => runCaptcha
140837
+ captchaDistGate: () => captchaDistGate,
140838
+ contentCaptchaAuthFinding: () => contentCaptchaAuthFinding,
140839
+ runCaptcha: () => runCaptcha,
140840
+ siteMemberCaptchaPolicy: () => siteMemberCaptchaPolicy
140167
140841
  });
140168
- import { readFileSync as readFileSync40 } from "node:fs";
140842
+ import { existsSync as existsSync36, readFileSync as readFileSync40 } from "node:fs";
140169
140843
  import { resolve as resolve34 } from "node:path";
140170
140844
  function readObject(path2) {
140171
140845
  try {
@@ -140184,6 +140858,64 @@ function captchaContentInput(dir) {
140184
140858
  androidApplicationId: text2(provisioning.native?.androidApplicationId)
140185
140859
  };
140186
140860
  }
140861
+ async function contentAuth(dir, app) {
140862
+ if (!existsSync36(resolve34(dir, "himalaya.content.json"))) return void 0;
140863
+ if (app !== void 0 && captchaContentInput(dir).app !== app) return void 0;
140864
+ try {
140865
+ const { loadAppConfigModule: loadAppConfigModule2 } = await Promise.resolve().then(() => (init_app_config_loader(), app_config_loader_exports));
140866
+ return (await loadAppConfigModule2(dir))?.auth;
140867
+ } catch {
140868
+ return void 0;
140869
+ }
140870
+ }
140871
+ async function contentCaptchaAuthFinding(dir, siteId, app, platforms) {
140872
+ const auth = await contentAuth(dir, app);
140873
+ return auth === void 0 ? void 0 : captchaAuthFinding(auth, siteId, platforms);
140874
+ }
140875
+ async function siteMemberCaptchaPolicy(clientId, fetchImpl = fetch) {
140876
+ const { mintVisitorToken: mintVisitorToken2 } = await Promise.resolve().then(() => (init_visitor_auth(), visitor_auth_exports));
140877
+ const token = await mintVisitorToken2(fetchImpl, clientId);
140878
+ const res = await fetchImpl(MEMBER_SITE_SETTINGS_URL, {
140879
+ headers: { Authorization: token, Accept: "application/json" },
140880
+ signal: AbortSignal.timeout(8e3)
140881
+ });
140882
+ if (!res.ok) throw new Error(`the member settings read answered HTTP ${res.status}`);
140883
+ return parseMemberCaptchaPolicy(await res.json());
140884
+ }
140885
+ async function releaseAuth(client2, app, release2) {
140886
+ try {
140887
+ let previewToken;
140888
+ if (release2) {
140889
+ const minted = await client2.previewToken(app, release2);
140890
+ previewToken = minted.json?.previewToken;
140891
+ if (!minted.ok || typeof previewToken !== "string") return { error: `no preview token for release ${release2} (HTTP ${minted.status})` };
140892
+ }
140893
+ const read = await client2.fetchAppConfig(app, release2, previewToken);
140894
+ if (!read.ok) return { error: `its app-config read answered HTTP ${read.status}` };
140895
+ return { auth: read.json?.authConfig ?? null };
140896
+ } catch (error) {
140897
+ return { error: error?.message ?? String(error) };
140898
+ }
140899
+ }
140900
+ async function captchaDistGate(options) {
140901
+ const { auth, error } = await releaseAuth(options.client, options.app, options.release);
140902
+ if (error) return { advisory: `member CAPTCHA check skipped: the release's login could not be read (${error}). If the login names no siteId and the site requires a member CAPTCHA, member sign-in in this build will fail.` };
140903
+ const finding2 = captchaAuthFinding(auth, options.requestedSite, options.platforms);
140904
+ if (!finding2) return {};
140905
+ if (!finding2.keyless) return { advisory: `${finding2.code}: ${finding2.message}` };
140906
+ const clientId = auth.config?.clientId;
140907
+ const unasked = (why) => ({ advisory: `${finding2.code}: ${finding2.message} Not refused: ${why}, so whether the site requires a member CAPTCHA is unknown \u2014 if it does, member sign-in in this build will fail.` });
140908
+ if (typeof clientId !== "string" || !clientId || clientId === PLACEHOLDER_CLIENT_ID) return unasked("the login has no Wix client to ask with");
140909
+ let policy;
140910
+ try {
140911
+ policy = await siteMemberCaptchaPolicy(clientId, options.fetchImpl ?? fetch);
140912
+ } catch (error2) {
140913
+ return unasked(`the site's member settings could not be read (${error2?.message ?? String(error2)})`);
140914
+ }
140915
+ const demands = ["login", "signup"].filter((source) => acceptsClientSiteKeyToken(policy[source]));
140916
+ if (demands.length === 0) return {};
140917
+ return { refuse: { code: finding2.code, captchaPolicy: policy, message: `${finding2.message} The site requires a CAPTCHA for member ${demands.map((source) => `${source} (${policy[source]})`).join(" and ")}. The key is baked only for a build bound to the site (--site, provisioning.json, or the app's own site binding).` } };
140918
+ }
140187
140919
  async function runCaptcha(pos, flags, io2, deps = {}) {
140188
140920
  const operation = pos[1];
140189
140921
  if (operation !== "setup" && operation !== "status") {
@@ -140213,6 +140945,14 @@ async function runCaptcha(pos, flags, io2, deps = {}) {
140213
140945
  fetchImpl: deps.fetchImpl
140214
140946
  });
140215
140947
  io2.log(JSON.stringify(result.body));
140948
+ const anyReady = (result.body.records ?? []).some((record2) => record2?.status === "ready");
140949
+ const finding2 = operation === "setup" && anyReady ? await contentCaptchaAuthFinding(
140950
+ text2(flags.content) ?? process.cwd(),
140951
+ input.siteId,
140952
+ input.app,
140953
+ [...input.iosBundleId ? ["ios"] : [], ...input.androidApplicationId ? ["android"] : []]
140954
+ ) : void 0;
140955
+ if (finding2) io2.error(`\u26A0 ${finding2.code}: ${finding2.message}`);
140216
140956
  return result.ok ? 0 : 1;
140217
140957
  } catch {
140218
140958
  io2.error(JSON.stringify({ error: "captcha_service_unavailable", complete: false }));
@@ -140226,6 +140966,9 @@ var init_captcha2 = __esm({
140226
140966
  init_captcha();
140227
140967
  init_captcha_service();
140228
140968
  init_wix_owner();
140969
+ init_captcha_auth();
140970
+ init_wix_placeholders();
140971
+ init_member_captcha_policy();
140229
140972
  text2 = (value2) => typeof value2 === "string" ? value2 : void 0;
140230
140973
  }
140231
140974
  });
@@ -140239,7 +140982,7 @@ __export(preflight_exports, {
140239
140982
  parseStoreConfig: () => parseStoreConfig,
140240
140983
  requireEnv: () => requireEnv
140241
140984
  });
140242
- import { existsSync as existsSync36, readFileSync as readFileSync41 } from "node:fs";
140985
+ import { existsSync as existsSync37, readFileSync as readFileSync41 } from "node:fs";
140243
140986
  import { isAbsolute as isAbsolute2, relative as relative7, resolve as resolve35 } from "node:path";
140244
140987
  function isInsideRepo(p2) {
140245
140988
  const rel = relative7(REPO_ROOT, resolve35(p2));
@@ -140296,7 +141039,7 @@ function parseStoreConfig(text11) {
140296
141039
  }
140297
141040
  function loadStoreConfig(app) {
140298
141041
  const p2 = storeConfigPath(app);
140299
- if (!existsSync36(p2)) {
141042
+ if (!existsSync37(p2)) {
140300
141043
  throw new Error(`No store config at ${p2}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
140301
141044
  }
140302
141045
  return parseStoreConfig(readFileSync41(p2, "utf8"));
@@ -143987,10 +144730,10 @@ var init_Matcher = __esm({
143987
144730
  * Restore state from snapshot.
143988
144731
  * @param {Object} snapshot
143989
144732
  */
143990
- restore(snapshot2) {
144733
+ restore(snapshot3) {
143991
144734
  this._pathStringCache = null;
143992
- this.path = snapshot2.path.map((node) => ({ ...node }));
143993
- this.siblingStacks = snapshot2.siblingStacks.map((map) => new Map(map));
144735
+ this.path = snapshot3.path.map((node) => ({ ...node }));
144736
+ this.siblingStacks = snapshot3.siblingStacks.map((map) => new Map(map));
143994
144737
  }
143995
144738
  /**
143996
144739
  * Return the read-only {@link MatcherView} for this matcher.
@@ -147028,7 +147771,7 @@ var init_customer_signing_inspect = __esm({
147028
147771
  });
147029
147772
 
147030
147773
  // src/push-onboarding.ts
147031
- import { existsSync as existsSync37, mkdirSync as mkdirSync19, readFileSync as readFileSync42, statSync as statSync9, writeFileSync as writeFileSync13, chmodSync as chmodSync2 } from "node:fs";
147774
+ import { existsSync as existsSync38, mkdirSync as mkdirSync19, readFileSync as readFileSync42, statSync as statSync9, writeFileSync as writeFileSync13, chmodSync as chmodSync2 } from "node:fs";
147032
147775
  import { basename as basename11, dirname as dirname24, join as join32, resolve as resolve36 } from "node:path";
147033
147776
  function readJson4(path2) {
147034
147777
  try {
@@ -147040,7 +147783,7 @@ function readJson4(path2) {
147040
147783
  }
147041
147784
  function pushInputsFromContent(dir) {
147042
147785
  const root = resolve36(dir);
147043
- if (!existsSync37(join32(root, "himalaya.content.json"))) return { initialized: false };
147786
+ if (!existsSync38(join32(root, "himalaya.content.json"))) return { initialized: false };
147044
147787
  const content = readJson4(join32(root, "himalaya.content.json"));
147045
147788
  const provisioning = readJson4(join32(root, "provisioning.json"));
147046
147789
  const native = provisioning.native ?? {};
@@ -147172,7 +147915,7 @@ function buildPushSetupBody(i2, keys) {
147172
147915
  }
147173
147916
  function readPushOnboarding(dir) {
147174
147917
  const path2 = join32(resolve36(dir), PUSH_ONBOARDING_PATH);
147175
- if (!existsSync37(path2)) return { version: 1 };
147918
+ if (!existsSync38(path2)) return { version: 1 };
147176
147919
  const parsed = readJson4(path2);
147177
147920
  return { version: 1, ...parsed };
147178
147921
  }
@@ -147207,7 +147950,7 @@ function recordPushDeferral(dir, d2) {
147207
147950
  }
147208
147951
  function clearPushDeferral(dir) {
147209
147952
  const path2 = join32(resolve36(dir), PUSH_ONBOARDING_PATH);
147210
- if (!existsSync37(path2)) return;
147953
+ if (!existsSync38(path2)) return;
147211
147954
  const { push: _dropped, ...rest } = readPushOnboarding(dir);
147212
147955
  writeFile(dir, { ...rest, version: 1 });
147213
147956
  }
@@ -147275,7 +148018,7 @@ var signing_inspect_exports = {};
147275
148018
  __export(signing_inspect_exports, {
147276
148019
  runSigningInspect: () => runSigningInspect
147277
148020
  });
147278
- import { existsSync as existsSync38, readFileSync as readFileSync43, statSync as statSync10 } from "node:fs";
148021
+ import { existsSync as existsSync39, readFileSync as readFileSync43, statSync as statSync10 } from "node:fs";
147279
148022
  import { isAbsolute as isAbsolute3, resolve as resolve37 } from "node:path";
147280
148023
  function fail4(io2, code, fields = {}) {
147281
148024
  io2.error(JSON.stringify({ ok: false, code, ...fields }));
@@ -147365,7 +148108,7 @@ async function runSigningInspect(pos, flags, io2, deps = {}) {
147365
148108
  } catch (error) {
147366
148109
  return fail4(io2, "signing_inspect_key_inside_repo", { error: error.message });
147367
148110
  }
147368
- if (!existsSync38(certPath) || !existsSync38(profilePath2)) {
148111
+ if (!existsSync39(certPath) || !existsSync39(profilePath2)) {
147369
148112
  return fail4(io2, "signing_inspect_local_assets_unreadable", { error: "cannot read --cert-file or --profile-file" });
147370
148113
  }
147371
148114
  if (statSync10(certPath).size > 1048576 || statSync10(profilePath2).size > 1048576) {
@@ -147899,7 +148642,7 @@ __export(template_trim_exports, {
147899
148642
  routeMatcher: () => routeMatcher,
147900
148643
  runTemplateTrim: () => runTemplateTrim
147901
148644
  });
147902
- import { cpSync, existsSync as existsSync39, mkdirSync as mkdirSync20, readdirSync as readdirSync21, readFileSync as readFileSync45, renameSync as renameSync8, rmSync as rmSync11, statSync as statSync11, writeFileSync as writeFileSync15 } from "node:fs";
148645
+ import { cpSync, existsSync as existsSync40, mkdirSync as mkdirSync20, readdirSync as readdirSync21, readFileSync as readFileSync45, renameSync as renameSync8, rmSync as rmSync11, statSync as statSync11, writeFileSync as writeFileSync15 } from "node:fs";
147903
148646
  import { dirname as dirname25, join as join34 } from "node:path";
147904
148647
  function routeMatcher(pattern) {
147905
148648
  const detail = /^\^([A-Za-z0-9_]+)\(\?:\[_\/\]\(\.\+\)\)\?\$$/.exec(pattern);
@@ -148644,7 +149387,7 @@ function homeModulesPass(app, kept, edited) {
148644
149387
  }
148645
149388
  function listWorkers(dir) {
148646
149389
  const root = join34(dir, "tier5-src");
148647
- if (!existsSync39(root)) return { dirs: [], files: {} };
149390
+ if (!existsSync40(root)) return { dirs: [], files: {} };
148648
149391
  const dirs = readdirSync21(root).filter((n2) => statSync11(join34(root, n2)).isDirectory()).sort();
148649
149392
  const files = {};
148650
149393
  const walk2 = (abs, rel) => {
@@ -148681,7 +149424,7 @@ async function runTemplateTrim(req, deps) {
148681
149424
  const dir = req.dir;
148682
149425
  const devPath = join34(dir, "dev/index.ts");
148683
149426
  const configPath = join34(dir, "config.ts");
148684
- if (!existsSync39(devPath) || !existsSync39(configPath)) {
149427
+ if (!existsSync40(devPath) || !existsSync40(configPath)) {
148685
149428
  return refuse3(new TrimRefusal(`${dir} has no dev/index.ts + config.ts \u2014 point --content at an app scaffolded with \`himi init --template\``));
148686
149429
  }
148687
149430
  if (isTemplateSourceTree(dir)) {
@@ -148728,7 +149471,7 @@ async function runTemplateTrim(req, deps) {
148728
149471
  }
148729
149472
  }
148730
149473
  if (req.dryRun) return { code: 0, result: { ok: true, dryRun: true, ...plan.report, validate: null } };
148731
- const hadHimi = existsSync39(join34(dir, ".himi"));
149474
+ const hadHimi = existsSync40(join34(dir, ".himi"));
148732
149475
  const backupRel = join34(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
148733
149476
  const backup = join34(dir, backupRel);
148734
149477
  const originals = [[devPath, "dev/index.ts"], [configPath, "config.ts"], ...trimmedContent !== void 0 ? [[contentPath, "himalaya.content.json"]] : []];
@@ -148737,7 +149480,7 @@ async function runTemplateTrim(req, deps) {
148737
149480
  for (const [path2, rel] of originals) {
148738
149481
  if (rel === "himalaya.content.json") contentManifestPath(dir);
148739
149482
  const original = readFileSync45(join34(backup, rel));
148740
- if (!existsSync39(path2) || !readFileSync45(path2).equals(original)) {
149483
+ if (!existsSync40(path2) || !readFileSync45(path2).equals(original)) {
148741
149484
  if (rel === "himalaya.content.json") writeContentManifest(dir, original);
148742
149485
  else writeFileSync15(path2, original);
148743
149486
  }
@@ -149094,17 +149837,17 @@ __export(template_adopt_exports, {
149094
149837
  workerReach: () => workerReach,
149095
149838
  writeInventory: () => writeInventory
149096
149839
  });
149097
- import { existsSync as existsSync40, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync22, readFileSync as readFileSync46, renameSync as renameSync9, statSync as statSync12, writeFileSync as writeFileSync16 } from "node:fs";
149840
+ import { existsSync as existsSync41, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync22, readFileSync as readFileSync46, renameSync as renameSync9, statSync as statSync12, writeFileSync as writeFileSync16 } from "node:fs";
149098
149841
  import { dirname as dirname26, join as join35, posix as posix2 } from "node:path";
149099
149842
  function readAppSources(dir) {
149100
149843
  const devPath = join35(dir, "dev", "index.ts");
149101
149844
  const configPath = join35(dir, "config.ts");
149102
- if (!existsSync40(devPath) || !existsSync40(configPath)) {
149845
+ if (!existsSync41(devPath) || !existsSync41(configPath)) {
149103
149846
  throw new TrimRefusal(`no dev/index.ts + config.ts under ${dir} \u2014 run this in (or point --content at) an app \`himi init --template\` scaffolded`);
149104
149847
  }
149105
149848
  const workers = /* @__PURE__ */ new Map();
149106
149849
  const root = join35(dir, "tier5-src");
149107
- if (existsSync40(root)) {
149850
+ if (existsSync41(root)) {
149108
149851
  for (const name of readdirSync22(root).sort()) {
149109
149852
  const abs = join35(root, name);
149110
149853
  if (!statSync12(abs).isDirectory()) continue;
@@ -149344,7 +150087,7 @@ function inventoryOf(dir, src, app) {
149344
150087
  sampleCopy: copyHits(dir, SAMPLE_COPY, 50),
149345
150088
  matrix,
149346
150089
  journeys,
149347
- netMocks: existsSync40(join35(dir, "dev", "net-mocks.json"))
150090
+ netMocks: existsSync41(join35(dir, "dev", "net-mocks.json"))
149348
150091
  };
149349
150092
  }
149350
150093
  function matrixMarkdown(rows) {
@@ -149380,9 +150123,9 @@ function inventorySummary(inv) {
149380
150123
  function writeNoFollow(dir, rel, body) {
149381
150124
  const p2 = join35(dir, rel);
149382
150125
  const parent = dirname26(p2);
149383
- if (existsSync40(parent) && lstatSync11(parent).isSymbolicLink()) throw new TrimRefusal(`${dirname26(rel)} is a symlink \u2014 refusing to write through it`);
150126
+ if (existsSync41(parent) && lstatSync11(parent).isSymbolicLink()) throw new TrimRefusal(`${dirname26(rel)} is a symlink \u2014 refusing to write through it`);
149384
150127
  mkdirSync21(parent, { recursive: true });
149385
- if (existsSync40(p2) && lstatSync11(p2).isSymbolicLink()) throw new TrimRefusal(`${rel} is a symlink \u2014 refusing to write through it`);
150128
+ if (existsSync41(p2) && lstatSync11(p2).isSymbolicLink()) throw new TrimRefusal(`${rel} is a symlink \u2014 refusing to write through it`);
149386
150129
  const tmp = `${p2}.${process.pid}.${Date.now()}.tmp`;
149387
150130
  writeFileSync16(tmp, body, { flag: "wx" });
149388
150131
  renameSync9(tmp, p2);
@@ -149391,7 +150134,7 @@ function writeInventory(dir, inv, opts = {}) {
149391
150134
  const written = [];
149392
150135
  const keep = opts.companions === "if-missing";
149393
150136
  const put = (rel, body, companion) => {
149394
- if (companion && keep && existsSync40(join35(dir, rel))) return;
150137
+ if (companion && keep && existsSync41(join35(dir, rel))) return;
149395
150138
  writeNoFollow(dir, rel, body);
149396
150139
  written.push(rel);
149397
150140
  };
@@ -149497,7 +150240,7 @@ function addScreen(dir, id, entry, opts = {}) {
149497
150240
  }
149498
150241
  for (const rel of ["dev", "dev/index.ts", ...config !== src.config ? ["config.ts"] : [], ...contentWithView !== void 0 ? ["himalaya.content.json"] : []]) {
149499
150242
  const p2 = join35(dir, rel);
149500
- if (existsSync40(p2) && lstatSync11(p2).isSymbolicLink()) throw new TrimRefusal(`${rel} is a symlink \u2014 refusing to write through it (nothing written)`);
150243
+ if (existsSync41(p2) && lstatSync11(p2).isSymbolicLink()) throw new TrimRefusal(`${rel} is a symlink \u2014 refusing to write through it (nothing written)`);
149501
150244
  }
149502
150245
  writeNoFollow(dir, "dev/index.ts", dev);
149503
150246
  if (config !== src.config) writeNoFollow(dir, "config.ts", config);
@@ -149520,7 +150263,7 @@ function specScreens(spec) {
149520
150263
  return [...new Set(out2)];
149521
150264
  }
149522
150265
  function keepFromSpec(dir, specPath) {
149523
- if (!existsSync40(specPath)) throw new TrimRefusal(`no ${specPath} \u2014 --from-spec reads the approved plan's \`## Screen catalogue\``);
150266
+ if (!existsSync41(specPath)) throw new TrimRefusal(`no ${specPath} \u2014 --from-spec reads the approved plan's \`## Screen catalogue\``);
149524
150267
  const ids = specScreens(readFileSync46(specPath, "utf8"));
149525
150268
  if (!ids.length) {
149526
150269
  throw new TrimRefusal("SPEC.md's `## Screen catalogue` names no screen ids \u2014 put each screen's id (`himi template screens` lists them) in the first column, in backticks");
@@ -150273,7 +151016,7 @@ __export(pack_exports, {
150273
151016
  });
150274
151017
  import { createHash as createHash24 } from "node:crypto";
150275
151018
  import { deflateRawSync, inflateRawSync } from "node:zlib";
150276
- import { existsSync as existsSync41, mkdirSync as mkdirSync22, readFileSync as readFileSync48, readdirSync as readdirSync23, rmSync as rmSync12, statSync as statSync13, writeFileSync as writeFileSync17 } from "node:fs";
151019
+ import { existsSync as existsSync42, mkdirSync as mkdirSync22, readFileSync as readFileSync48, readdirSync as readdirSync23, rmSync as rmSync12, statSync as statSync13, writeFileSync as writeFileSync17 } from "node:fs";
150277
151020
  import { dirname as dirname27, join as join37, resolve as resolve39 } from "node:path";
150278
151021
  function writeZip(entries) {
150279
151022
  const sorted2 = entries.slice().sort((a2, b2) => a2.path < b2.path ? -1 : a2.path > b2.path ? 1 : 0);
@@ -150368,10 +151111,10 @@ function inflateEntry(buf, entry) {
150368
151111
  return bytes2;
150369
151112
  }
150370
151113
  function readIf(path2) {
150371
- return existsSync41(path2) && statSync13(path2).isFile() ? readFileSync48(path2) : null;
151114
+ return existsSync42(path2) && statSync13(path2).isFile() ? readFileSync48(path2) : null;
150372
151115
  }
150373
151116
  function listDir(dir) {
150374
- return existsSync41(dir) ? readdirSync23(dir).filter((f2) => statSync13(join37(dir, f2)).isFile()) : [];
151117
+ return existsSync42(dir) ? readdirSync23(dir).filter((f2) => statSync13(join37(dir, f2)).isFile()) : [];
150375
151118
  }
150376
151119
  function gatherReleaseEntries(appDir3) {
150377
151120
  const manifestPath = join37(appDir3, HIMI_PACKAGE_MANIFEST_PATH);
@@ -150450,7 +151193,7 @@ function packRelease(releaseRoot, app, out2, opts = {}) {
150450
151193
  const appDir3 = join37(resolve39(releaseRoot), app);
150451
151194
  const { all, header, skipped } = buildEntries(appDir3, app, opts);
150452
151195
  const buf = writeZip(all);
150453
- const isDir2 = existsSync41(out2) && statSync13(out2).isDirectory();
151196
+ const isDir2 = existsSync42(out2) && statSync13(out2).isDirectory();
150454
151197
  const outFile = isDir2 ? join37(resolve39(out2), packageFileName(app, header.releaseId)) : resolve39(out2);
150455
151198
  mkdirSync22(dirname27(outFile), { recursive: true });
150456
151199
  writeFileSync17(outFile, buf);
@@ -150467,11 +151210,11 @@ function resolvePackageDirDest(outDir, app, releaseId) {
150467
151210
  }
150468
151211
  };
150469
151212
  const replaceable = (dir) => {
150470
- if (!existsSync41(dir)) return true;
151213
+ if (!existsSync42(dir)) return true;
150471
151214
  if (!statSync13(dir).isDirectory()) return false;
150472
151215
  return readdirSync23(dir).length === 0 || packagedApp(dir) === app;
150473
151216
  };
150474
- if (existsSync41(outDir) && !statSync13(outDir).isDirectory()) {
151217
+ if (existsSync42(outDir) && !statSync13(outDir).isDirectory()) {
150475
151218
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
150476
151219
  }
150477
151220
  if (replaceable(outDir)) return outDir;
@@ -150532,7 +151275,7 @@ function openPackageDir(dir) {
150532
151275
  else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync48(childAbs));
150533
151276
  }
150534
151277
  };
150535
- if (!existsSync41(root)) throw new Error(`no such package directory: ${root}`);
151278
+ if (!existsSync42(root)) throw new Error(`no such package directory: ${root}`);
150536
151279
  walk2("");
150537
151280
  const manifestBytes = entries.get(HIMI_PACKAGE_MANIFEST_PATH);
150538
151281
  if (manifestBytes) {
@@ -150617,7 +151360,7 @@ __export(app_source_exports, {
150617
151360
  zipAppSource: () => zipAppSource
150618
151361
  });
150619
151362
  import { createHash as createHash25 } from "node:crypto";
150620
- import { readdirSync as readdirSync24, lstatSync as lstatSync13, readFileSync as readFileSync49, mkdirSync as mkdirSync23, writeFileSync as writeFileSync18, existsSync as existsSync42, rmSync as rmSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
151363
+ import { readdirSync as readdirSync24, lstatSync as lstatSync13, readFileSync as readFileSync49, mkdirSync as mkdirSync23, writeFileSync as writeFileSync18, existsSync as existsSync43, rmSync as rmSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
150621
151364
  import { join as join38, resolve as resolve40, sep as sep13 } from "node:path";
150622
151365
  import { createRequire as createRequire3 } from "node:module";
150623
151366
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
@@ -150633,7 +151376,7 @@ function walkSourceTree(dir, file2, skip, enter = () => {
150633
151376
  continue;
150634
151377
  }
150635
151378
  if (st2.isDirectory()) {
150636
- if (isDeniedSourceDir(name, relDir) || existsSync42(join38(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
151379
+ if (isDeniedSourceDir(name, relDir) || existsSync43(join38(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
150637
151380
  else walk2(rel);
150638
151381
  continue;
150639
151382
  }
@@ -150801,7 +151544,7 @@ function writeAppSource(entries, destDir) {
150801
151544
  return written;
150802
151545
  }
150803
151546
  function staleSourcePaths(entries, destDir) {
150804
- if (!existsSync42(destDir)) return [];
151547
+ if (!existsSync43(destDir)) return [];
150805
151548
  const archived = new Set(entries.map((e2) => e2.path));
150806
151549
  const stale = [];
150807
151550
  walkSourceTree(destDir, (archivePath) => {
@@ -150857,7 +151600,7 @@ function removeStaleSource(entries, destDir) {
150857
151600
  const segments = entry.path.split("/");
150858
151601
  for (let i2 = 1; i2 < segments.length; i2++) needed.add(segments.slice(0, i2).join("/"));
150859
151602
  }
150860
- if (existsSync42(root)) pruneEmptyDirs(root, "", needed, removed);
151603
+ if (existsSync43(root)) pruneEmptyDirs(root, "", needed, removed);
150861
151604
  return removed.sort();
150862
151605
  }
150863
151606
  function pruneEmptyDirs(root, rel, needed, removed) {
@@ -150946,7 +151689,7 @@ __export(run_watch_exports, {
150946
151689
  watchedContentDir: () => watchedContentDir,
150947
151690
  writeRunRecord: () => writeRunRecord
150948
151691
  });
150949
- import { closeSync as closeSync4, constants as constants3, existsSync as existsSync43, fstatSync, lstatSync as lstatSync14, statSync as statSync14, readdirSync as readdirSync25, mkdirSync as mkdirSync24, openSync as openSync4, readFileSync as readFileSync50, realpathSync as realpathSync8, renameSync as renameSync10, rmSync as rmSync14, writeFileSync as writeFileSync19, watch as fsWatch } from "node:fs";
151692
+ import { closeSync as closeSync4, constants as constants3, existsSync as existsSync44, fstatSync, lstatSync as lstatSync14, statSync as statSync14, readdirSync as readdirSync25, mkdirSync as mkdirSync24, openSync as openSync4, readFileSync as readFileSync50, realpathSync as realpathSync8, renameSync as renameSync10, rmSync as rmSync14, writeFileSync as writeFileSync19, watch as fsWatch } from "node:fs";
150950
151693
  import { randomUUID as randomUUID7 } from "node:crypto";
150951
151694
  import { extname as extname4, join as join39, relative as relative8, resolve as resolve41, sep as sep14 } from "node:path";
150952
151695
  function diffReleases(prev, next) {
@@ -151014,7 +151757,7 @@ function watchContent(dir, onChange, opts = {}) {
151014
151757
  }, debounceMs);
151015
151758
  };
151016
151759
  let watcher2, polling;
151017
- const snapshot2 = () => {
151760
+ const snapshot3 = () => {
151018
151761
  const files = /* @__PURE__ */ new Map();
151019
151762
  const visit = (directory, prefix = "") => {
151020
151763
  let entries;
@@ -151037,12 +151780,12 @@ function watchContent(dir, onChange, opts = {}) {
151037
151780
  visit(dir);
151038
151781
  return files;
151039
151782
  };
151040
- let before = snapshot2();
151783
+ let before = snapshot3();
151041
151784
  const fallback = () => {
151042
151785
  if (polling) return;
151043
151786
  watcher2?.close();
151044
151787
  polling = setInterval(() => {
151045
- const after = snapshot2();
151788
+ const after = snapshot3();
151046
151789
  for (const path2 of /* @__PURE__ */ new Set([...before.keys(), ...after.keys()])) if (before.get(path2) !== after.get(path2)) changed("change", path2);
151047
151790
  before = after;
151048
151791
  }, opts.pollMs ?? 500);
@@ -151065,7 +151808,7 @@ function watchedContentDir(source) {
151065
151808
  if (!source || /^https?:\/\//i.test(source)) return null;
151066
151809
  const dir = resolve41(source);
151067
151810
  try {
151068
- return statSync14(dir).isDirectory() && existsSync43(join39(dir, "himalaya.content.json")) ? dir : null;
151811
+ return statSync14(dir).isDirectory() && existsSync44(join39(dir, "himalaya.content.json")) ? dir : null;
151069
151812
  } catch {
151070
151813
  return null;
151071
151814
  }
@@ -151721,7 +152464,7 @@ __export(milestones_exports, {
151721
152464
  });
151722
152465
  import { createHash as createHash28, randomUUID as randomUUID8 } from "node:crypto";
151723
152466
  import { spawn as spawn4 } from "node:child_process";
151724
- import { closeSync as closeSync5, existsSync as existsSync44, lstatSync as lstatSync17, readFileSync as readFileSync54, statSync as statSync15 } from "node:fs";
152467
+ import { closeSync as closeSync5, existsSync as existsSync45, lstatSync as lstatSync17, readFileSync as readFileSync54, statSync as statSync15 } from "node:fs";
151725
152468
  import { createServer as createServer3 } from "node:net";
151726
152469
  import { basename as basename14, dirname as dirname28, isAbsolute as isAbsolute4, join as join43, relative as relative9, resolve as resolve42, sep as sep15 } from "node:path";
151727
152470
  function homeShotPath(dir, home) {
@@ -151834,7 +152577,7 @@ function autoPictureGate(dir) {
151834
152577
  }
151835
152578
  async function homePictureAtValidate(dir, opts) {
151836
152579
  if (!opts.ok) return { skipped: "validate found errors, so the picture was left as it is" };
151837
- if (!existsSync44(join43(dir, "himalaya.content.json"))) return { skipped: "not a content package" };
152580
+ if (!existsSync45(join43(dir, "himalaya.content.json"))) return { skipped: "not a content package" };
151838
152581
  const env = opts.env ?? process.env;
151839
152582
  if (ciSet(env)) return { skipped: "the CI environment variable is set" };
151840
152583
  if (env.NODE_TEST_CONTEXT) return { skipped: "running under `node --test`" };
@@ -151904,7 +152647,7 @@ function stillBuilding(dir, image, now) {
151904
152647
  }
151905
152648
  }
151906
152649
  function firstLookBeforeOwnPostcard(dir, image, now) {
151907
- if (!image || !existsSync44(image) || initDivergence(dir).state !== "diverged") return void 0;
152650
+ if (!image || !existsSync45(image) || initDivergence(dir).state !== "diverged") return void 0;
151908
152651
  return journal(dir, "first-look", withHomeCaveat(yourFirstLookMessage(dir), homeCaveat(dir, image)), { image, ...now ? { now } : {} });
151909
152652
  }
151910
152653
  function afterRenderPng(dir, input) {
@@ -151969,13 +152712,13 @@ function testChangeNote(headline2, change) {
151969
152712
  }
151970
152713
  function afterTestRun(dir, input) {
151971
152714
  const firstLook = firstLookBeforeOwnPostcard(dir, input.homeShot, input.now);
151972
- const shot = input.homeShot && existsSync44(input.homeShot) ? input.homeShot : void 0;
152715
+ const shot = input.homeShot && existsSync45(input.homeShot) ? input.homeShot : void 0;
151973
152716
  const caveat = shot ? homeCaveat(dir, shot) : void 0;
151974
152717
  const counts = testPostcardMessage(input);
151975
152718
  const headline2 = caveat ? `${counts} But ${caveat}.` : counts;
151976
152719
  const postcard = journal(dir, "test", headline2, {
151977
152720
  ref: input.runId,
151978
- ...input.homeShot && existsSync44(input.homeShot) ? { image: input.homeShot } : {},
152721
+ ...input.homeShot && existsSync45(input.homeShot) ? { image: input.homeShot } : {},
151979
152722
  ...input.now ? { now: input.now } : {},
151980
152723
  unlessUnchanged: true,
151981
152724
  changeNote: (change) => testChangeNote(headline2, change)
@@ -152018,7 +152761,7 @@ function draftPictureGap(input) {
152018
152761
  if (!input.home) return { reason: "the app's dev/index.ts names no literal homeScreenId, so there is no home screen to picture" };
152019
152762
  if (!input.homeShot) return { reason: "the homeScreenId does not name a file under .himi/shots" };
152020
152763
  const render = `himi render ${shellQuote(input.home)} --png${contentArg(input.content)}`;
152021
- if (!existsSync44(input.homeShot)) return { reason: "there is no home shot yet", render };
152764
+ if (!existsSync45(input.homeShot)) return { reason: "there is no home shot yet", render };
152022
152765
  if (input.sourceChangedAtMs === void 0) return { reason: "the source's last change could not be read, so the home shot cannot be shown to be current", render };
152023
152766
  if (!shotIsCurrent(input.homeShot, input.sourceChangedAtMs)) return { reason: "the home shot is older than the last source edit, so it is not a picture of this draft", render };
152024
152767
  return null;
@@ -152096,7 +152839,7 @@ function draftChangeNote(dir, input) {
152096
152839
  if (!previous) return void 0;
152097
152840
  const shaOf = (i2) => i2 && typeof i2 === "object" && typeof i2.sha256 === "string" ? i2.sha256 : void 0;
152098
152841
  const had = Array.isArray(previous.images) ? previous.images.map(shaOf).find(Boolean) : void 0;
152099
- const has = input.picture && existsSync44(input.picture) ? sha256Of2(input.picture) : void 0;
152842
+ const has = input.picture && existsSync45(input.picture) ? sha256Of2(input.picture) : void 0;
152100
152843
  if (had && has) parts.push(had === has ? "the home screen looks the same" : "the home screen changed");
152101
152844
  else if (has) parts.push("a home picture this time");
152102
152845
  else return void 0;
@@ -152162,7 +152905,7 @@ function homeShotForPostcard(dir, opts = {}) {
152162
152905
  const shot = homeShotPath(dir, home);
152163
152906
  if (!shot) return { skipped: "the homeScreenId does not name a file under .himi/shots" };
152164
152907
  const render = `\`himi render ${shellQuote(home)} --png\``;
152165
- if (!existsSync44(shot)) return { skipped: `no home shot yet \u2014 ${render} makes one, and the next postcard carries it` };
152908
+ if (!existsSync45(shot)) return { skipped: `no home shot yet \u2014 ${render} makes one, and the next postcard carries it` };
152166
152909
  let changed;
152167
152910
  try {
152168
152911
  changed = newestSourceChangeMs(dir);
@@ -152242,7 +152985,7 @@ function liveWatchRunning(dir, isAlive = pidAlive, now) {
152242
152985
  }
152243
152986
  function watchReminder(dir, opts = {}) {
152244
152987
  try {
152245
- if (!existsSync44(join43(dir, "himalaya.content.json"))) return void 0;
152988
+ if (!existsSync45(join43(dir, "himalaya.content.json"))) return void 0;
152246
152989
  const nowDate = opts.now ?? /* @__PURE__ */ new Date();
152247
152990
  const live3 = liveWatch(dir, opts.isAlive, nowDate);
152248
152991
  const now = nowDate.getTime();
@@ -152917,12 +153660,12 @@ var init_brand_color_math = __esm({
152917
153660
  });
152918
153661
 
152919
153662
  // src/token-lint.ts
152920
- import { existsSync as existsSync47, readFileSync as readFileSync56 } from "node:fs";
153663
+ import { existsSync as existsSync48, readFileSync as readFileSync56 } from "node:fs";
152921
153664
  import { join as join46 } from "node:path";
152922
153665
  function configuredTokensPath(dir) {
152923
153666
  for (const name of ["config.ts", "config.js", "himi.config.ts"]) {
152924
153667
  const f2 = join46(dir, name);
152925
- if (!existsSync47(f2)) continue;
153668
+ if (!existsSync48(f2)) continue;
152926
153669
  const src = readFileSync56(f2, "utf8");
152927
153670
  const m2 = /designTokensPath\s*:\s*["'`]([^"'`]+)["'`]/.exec(src);
152928
153671
  if (m2) return { path: m2[1].replace(/^\.\//, "") };
@@ -153000,7 +153743,7 @@ function tokenColorIssues(dir, tokensPathOverride) {
153000
153743
  }
153001
153744
  const tokensPath = configured.path ?? "tokens.json";
153002
153745
  const p2 = join46(dir, tokensPath);
153003
- if (!existsSync47(p2)) {
153746
+ if (!existsSync48(p2)) {
153004
153747
  if (tokensPath !== "tokens.json") {
153005
153748
  warn3(`config designTokensPath points at ${tokensPath}, which does not exist -- colour tokens were not checked`);
153006
153749
  }
@@ -153322,7 +154065,7 @@ __export(fonts_exports, {
153322
154065
  listReport: () => listReport,
153323
154066
  resolveFamilyName: () => resolveFamilyName
153324
154067
  });
153325
- import { existsSync as existsSync48, readFileSync as readFileSync57 } from "node:fs";
154068
+ import { existsSync as existsSync49, readFileSync as readFileSync57 } from "node:fs";
153326
154069
  import { join as join47 } from "node:path";
153327
154070
  function readJson6(path2, fallback) {
153328
154071
  try {
@@ -153356,7 +154099,7 @@ function listReport(opts = {}) {
153356
154099
  }
153357
154100
  async function fontFindings(appDir3, opts = {}) {
153358
154101
  const tokensPath = join47(appDir3, configuredTokensPath(appDir3).path ?? "tokens.json");
153359
- if (!existsSync48(tokensPath)) return null;
154102
+ if (!existsSync49(tokensPath)) return null;
153360
154103
  const catalog4 = loadCatalog2();
153361
154104
  const tokens2 = readJson6(tokensPath, {});
153362
154105
  const policy = readJson6(join47(appDir3, "fonts.json"), {});
@@ -153864,17 +154607,17 @@ __export(template_recommend_exports, {
153864
154607
  recommendTemplates: () => recommendTemplates,
153865
154608
  verticalsFromBook: () => verticalsFromBook
153866
154609
  });
153867
- import { existsSync as existsSync50, readFileSync as readFileSync60 } from "node:fs";
154610
+ import { existsSync as existsSync51, readFileSync as readFileSync60 } from "node:fs";
153868
154611
  import { join as join50 } from "node:path";
153869
154612
  function verticalsFromBook(path2) {
153870
- const dir = existsSync50(join50(path2, "book.json")) || existsSync50(join50(path2, "provisioning.overlay.json")) ? path2 : join50(path2, ".himi", "business-book");
154613
+ const dir = existsSync51(join50(path2, "book.json")) || existsSync51(join50(path2, "provisioning.overlay.json")) ? path2 : join50(path2, ".himi", "business-book");
153871
154614
  const overlay = join50(dir, "provisioning.overlay.json");
153872
- if (existsSync50(overlay)) {
154615
+ if (existsSync51(overlay)) {
153873
154616
  const v2 = JSON.parse(readFileSync60(overlay, "utf8")).installedVerticals;
153874
154617
  if (Array.isArray(v2) && v2.length) return v2.filter((x2) => typeof x2 === "string");
153875
154618
  }
153876
154619
  const book = join50(dir, "book.json");
153877
- if (existsSync50(book)) {
154620
+ if (existsSync51(book)) {
153878
154621
  const probes = JSON.parse(readFileSync60(book, "utf8")).probes;
153879
154622
  if (Array.isArray(probes)) return installedFromProbes(probes);
153880
154623
  }
@@ -154489,7 +155232,7 @@ var init_glyphs = __esm({
154489
155232
  });
154490
155233
 
154491
155234
  // ../app-icons/plan.ts
154492
- import { existsSync as existsSync51, readFileSync as readFileSync61, readdirSync as readdirSync27 } from "node:fs";
155235
+ import { existsSync as existsSync52, readFileSync as readFileSync61, readdirSync as readdirSync27 } from "node:fs";
154493
155236
  import { createHash as createHash29 } from "node:crypto";
154494
155237
  import { dirname as dirname29, join as join51, resolve as resolve44 } from "node:path";
154495
155238
  function stripComments(src) {
@@ -154625,7 +155368,7 @@ function resolveTokensPath(app, configTs) {
154625
155368
  }
154626
155369
  function resolveToken(app, token, tokensPathOverride) {
154627
155370
  const tokensPath = tokensPathOverride ?? join51(appDir(app), "tokens.json");
154628
- if (!existsSync51(tokensPath)) {
155371
+ if (!existsSync52(tokensPath)) {
154629
155372
  throw new Error(`app-icons: ${app} has no tokens.json at ${tokensPath} (needed to resolve "${token}").`);
154630
155373
  }
154631
155374
  const tokens2 = JSON.parse(readFileSync61(tokensPath, "utf8"));
@@ -154670,7 +155413,7 @@ function iconSpec(app, configTs, id, tokensPathOverride) {
154670
155413
  const tokens2 = tokensPathOverride ?? resolveTokensPath(app, configTs).path;
154671
155414
  if (cfg.source) {
154672
155415
  const abs = resolve44(appDir(app), cfg.source);
154673
- if (!existsSync51(abs)) {
155416
+ if (!existsSync52(abs)) {
154674
155417
  throw new Error(`app-icons: ${app} declares icon.source "${cfg.source}" but ${abs} does not exist.`);
154675
155418
  }
154676
155419
  return {
@@ -154710,9 +155453,9 @@ function iosAssetCatalog2(app) {
154710
155453
  return assetCatalogIn(join51(appDir(app), "ios"));
154711
155454
  }
154712
155455
  function assetCatalogIn(platformDir) {
154713
- if (!existsSync51(platformDir)) return void 0;
155456
+ if (!existsSync52(platformDir)) return void 0;
154714
155457
  const children = readdirSync27(platformDir).filter((c2) => !c2.startsWith("build")).sort();
154715
- const catalogs = children.map((c2) => join51(platformDir, c2, "Assets.xcassets")).filter(existsSync51);
155458
+ const catalogs = children.map((c2) => join51(platformDir, c2, "Assets.xcassets")).filter(existsSync52);
154716
155459
  if (catalogs.length === 1) return catalogs[0];
154717
155460
  if (catalogs.length > 1) return void 0;
154718
155461
  const targets = [];
@@ -154735,14 +155478,14 @@ function androidResDir2(manifestPath) {
154735
155478
  }
154736
155479
  function androidManifestPath(app) {
154737
155480
  const androidDir = join51(appDir(app), "android");
154738
- if (!existsSync51(androidDir)) return void 0;
155481
+ if (!existsSync52(androidDir)) return void 0;
154739
155482
  const preferred = join51(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
154740
- if (existsSync51(preferred)) return preferred;
154741
- const candidates = readdirSync27(androidDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && d2.name.endsWith("-app")).map((d2) => join51(androidDir, d2.name, "src", "main", "AndroidManifest.xml")).filter(existsSync51);
155483
+ if (existsSync52(preferred)) return preferred;
155484
+ const candidates = readdirSync27(androidDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && d2.name.endsWith("-app")).map((d2) => join51(androidDir, d2.name, "src", "main", "AndroidManifest.xml")).filter(existsSync52);
154742
155485
  return candidates.length === 1 ? candidates[0] : void 0;
154743
155486
  }
154744
155487
  function androidOptedIn2(manifestPath) {
154745
- if (!manifestPath || !existsSync51(manifestPath)) return false;
155488
+ if (!manifestPath || !existsSync52(manifestPath)) return false;
154746
155489
  return readFileSync61(manifestPath, "utf8").includes("@mipmap/ic_launcher");
154747
155490
  }
154748
155491
  function templateIconSha256() {
@@ -154767,7 +155510,7 @@ function generationAllowed(app, configTs, opts = {}) {
154767
155510
  }
154768
155511
  const iosCat = iosAssetCatalog2(app);
154769
155512
  const iosIcon = iosCat ? join51(iosCat, "AppIcon.appiconset", "icon_1024.png") : void 0;
154770
- if (!iosIcon || !existsSync51(iosIcon)) return { allowed: true, reason: "no icon yet" };
155513
+ if (!iosIcon || !existsSync52(iosIcon)) return { allowed: true, reason: "no icon yet" };
154771
155514
  const hash2 = fileSha256(iosIcon);
154772
155515
  const placeholders = opts.placeholderHashes ?? placeholderHashes();
154773
155516
  if (app === "_template") return { allowed: true, reason: "the template itself" };
@@ -154791,7 +155534,7 @@ var init_plan2 = __esm({
154791
155534
  });
154792
155535
 
154793
155536
  // ../native-config/icon.ts
154794
- import { existsSync as existsSync52, readFileSync as readFileSync62 } from "node:fs";
155537
+ import { existsSync as existsSync53, readFileSync as readFileSync62 } from "node:fs";
154795
155538
  import { join as join52 } from "node:path";
154796
155539
  function androidAdaptiveIconXml() {
154797
155540
  return [
@@ -154820,7 +155563,7 @@ function androidIconArtifacts(app, configTs) {
154820
155563
  if (!generationAllowed(app, configTs).allowed) return [];
154821
155564
  const res = androidResDir2(manifest2);
154822
155565
  const foreground = join52(res, "mipmap-mdpi", "ic_launcher_foreground.png");
154823
- if (!existsSync52(foreground)) {
155566
+ if (!existsSync53(foreground)) {
154824
155567
  throw new Error(
154825
155568
  `native-config icon: ${app} opts into @mipmap/ic_launcher but has no mipmap-*/ic_launcher_foreground.png. Run \`npm run app-icons -- ${app}\` first \u2014 the XML emitted here points at those rasters.`
154826
155569
  );
@@ -155122,7 +155865,7 @@ var init_localweb = __esm({
155122
155865
  });
155123
155866
 
155124
155867
  // ../native-config/capability-registrations.ts
155125
- import { existsSync as existsSync53, readdirSync as readdirSync28, readFileSync as readFileSync63, statSync as statSync16 } from "node:fs";
155868
+ import { existsSync as existsSync54, readdirSync as readdirSync28, readFileSync as readFileSync63, statSync as statSync16 } from "node:fs";
155126
155869
  import { dirname as dirname30, join as join53, resolve as resolve45 } from "node:path";
155127
155870
  import { fileURLToPath as fileURLToPath22 } from "node:url";
155128
155871
  function registrarClasses(path2, re2) {
@@ -155166,7 +155909,7 @@ function renderKotlinRegistrations(ids, appIdExpr = "APP_NAME", classes = androi
155166
155909
  ];
155167
155910
  }
155168
155911
  function filesWithRegisterMarkers(root, ext) {
155169
- if (!existsSync53(root)) return [];
155912
+ if (!existsSync54(root)) return [];
155170
155913
  const out2 = [];
155171
155914
  const stack = [[root, 0, ext === ".kt" ? "project" : "main"]];
155172
155915
  while (stack.length) {
@@ -155358,7 +156101,7 @@ __export(gen_native_config_exports, {
155358
156101
  xmlAttr: () => xmlAttr,
155359
156102
  yamlScalar: () => yamlScalar
155360
156103
  });
155361
- import { existsSync as existsSync54, mkdirSync as mkdirSync25, readFileSync as readFileSync64, readdirSync as readdirSync29, rmSync as rmSync15, statSync as statSync17, writeFileSync as writeFileSync21 } from "node:fs";
156104
+ import { existsSync as existsSync55, mkdirSync as mkdirSync25, readFileSync as readFileSync64, readdirSync as readdirSync29, rmSync as rmSync15, statSync as statSync17, writeFileSync as writeFileSync21 } from "node:fs";
155362
156105
  import { dirname as dirname31, join as join54 } from "node:path";
155363
156106
  import { fileURLToPath as fileURLToPath23 } from "node:url";
155364
156107
  function parseStringArray(configTs, field) {
@@ -155436,7 +156179,7 @@ function buildSet(capabilities, envelope = []) {
155436
156179
  }
155437
156180
  function loadOverrides(app) {
155438
156181
  const p2 = join54(appDir(app), "native-config.overrides.json");
155439
- if (!existsSync54(p2)) return {};
156182
+ if (!existsSync55(p2)) return {};
155440
156183
  return JSON.parse(readFileSync64(p2, "utf8"));
155441
156184
  }
155442
156185
  function needsQuote(s2) {
@@ -155663,7 +156406,7 @@ function genCoreModuleInstallers(check) {
155663
156406
  }
155664
156407
  function swiftFilesWithModuleMarkers(app) {
155665
156408
  const root = join54(appDir(app), "ios");
155666
- if (!existsSync54(root)) return [];
156409
+ if (!existsSync55(root)) return [];
155667
156410
  const out2 = [];
155668
156411
  const stack = [root];
155669
156412
  while (stack.length) {
@@ -155685,7 +156428,7 @@ function kotlinFilesWithModuleInstall(app) {
155685
156428
  return kotlinBootSourcesUnder(join54(appDir(app), "android"));
155686
156429
  }
155687
156430
  function kotlinBootSourcesUnder(root) {
155688
- if (!existsSync54(root)) return [];
156431
+ if (!existsSync55(root)) return [];
155689
156432
  const out2 = [];
155690
156433
  const stack = [[root, 0, "project"]];
155691
156434
  while (stack.length) {
@@ -155756,7 +156499,7 @@ function endText(kind, label3, indent) {
155756
156499
  }
155757
156500
  function hostAppliesDeferredWidgetActions(app) {
155758
156501
  const root = join54(appDir(app), "ios");
155759
- if (!existsSync54(root)) return false;
156502
+ if (!existsSync55(root)) return false;
155760
156503
  const stack = [root];
155761
156504
  while (stack.length) {
155762
156505
  const dir = stack.pop();
@@ -155814,15 +156557,15 @@ function androidAppGradle(app) {
155814
156557
  function androidManifestPath2(app) {
155815
156558
  const androidDir = join54(appDir(app), "android");
155816
156559
  const preferred = join54(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
155817
- if (existsSync54(preferred)) return preferred;
155818
- const candidates = existsSync54(androidDir) ? readdirSync29(androidDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && d2.name.endsWith("-app")).map((d2) => join54(androidDir, d2.name, "src", "main", "AndroidManifest.xml")).filter(existsSync54) : [];
156560
+ if (existsSync55(preferred)) return preferred;
156561
+ const candidates = existsSync55(androidDir) ? readdirSync29(androidDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && d2.name.endsWith("-app")).map((d2) => join54(androidDir, d2.name, "src", "main", "AndroidManifest.xml")).filter(existsSync55) : [];
155819
156562
  return candidates.length === 1 ? candidates[0] : preferred;
155820
156563
  }
155821
156564
  function automotiveAppDescPath(app) {
155822
156565
  return join54(dirname31(androidManifestPath2(app)), "res", "xml", "automotive_app_desc.xml");
155823
156566
  }
155824
156567
  function applyToFile(path2, kind, label3, render, check) {
155825
- if (!existsSync54(path2)) return { path: path2, outcome: "skipped" };
156568
+ if (!existsSync55(path2)) return { path: path2, outcome: "skipped" };
155826
156569
  const before = readFileSync64(path2, "utf8");
155827
156570
  assertSingleMarkerPair(before, kind, label3, path2);
155828
156571
  if (!hasMarkers(before, kind, label3)) return { path: path2, outcome: "skipped" };
@@ -155834,7 +156577,7 @@ function applyToFile(path2, kind, label3, render, check) {
155834
156577
  }
155835
156578
  function assertModuleFlagsMarkers(paths2) {
155836
156579
  for (const path2 of paths2) {
155837
- if (!existsSync54(path2)) continue;
156580
+ if (!existsSync55(path2)) continue;
155838
156581
  const text11 = readFileSync64(path2, "utf8");
155839
156582
  if (hasMarkers(text11, "yaml", LABEL_MODULES_IOS_LINK) && !hasMarkers(text11, "yaml", LABEL_MODULES_IOS_FLAGS)) {
155840
156583
  throw new Error(`${path2} links modules (${LABEL_MODULES_IOS_LINK}) but has no ${LABEL_MODULES_IOS_FLAGS} block under its project-level settings:, so no module would install. Add the markers (see apps/_template).`);
@@ -155846,7 +156589,7 @@ function applyModuleFlags(path2, products, check) {
155846
156589
  return applyToFile(path2, "yaml", LABEL_MODULES_IOS_FLAGS, (i2) => renderModulesIosFlags(products, i2), check);
155847
156590
  }
155848
156591
  function applyWholeFile(path2, content, check) {
155849
- const current = existsSync54(path2) ? readFileSync64(path2, "utf8") : null;
156592
+ const current = existsSync55(path2) ? readFileSync64(path2, "utf8") : null;
155850
156593
  if (current === content) return { path: path2, outcome: "unchanged" };
155851
156594
  if (check) return { path: path2, outcome: "drift" };
155852
156595
  mkdirSync25(dirname31(path2), { recursive: true });
@@ -155854,7 +156597,7 @@ function applyWholeFile(path2, content, check) {
155854
156597
  return { path: path2, outcome: "written" };
155855
156598
  }
155856
156599
  function removeWholeFile(path2, check) {
155857
- if (!existsSync54(path2)) return { path: path2, outcome: "skipped" };
156600
+ if (!existsSync55(path2)) return { path: path2, outcome: "skipped" };
155858
156601
  if (check) return { path: path2, outcome: "drift" };
155859
156602
  rmSync15(path2);
155860
156603
  return { path: path2, outcome: "removed" };
@@ -155893,7 +156636,7 @@ function genCapabilityRegistrations(root, declared, check) {
155893
156636
  }
155894
156637
  function buildSetOfAppDir(root) {
155895
156638
  const configPath = join54(root, "config.ts");
155896
- const configTs = existsSync54(configPath) ? readFileSync64(configPath, "utf8") : "";
156639
+ const configTs = existsSync55(configPath) ? readFileSync64(configPath, "utf8") : "";
155897
156640
  return buildSet(parseStringArray(configTs, "capabilities"), parseStringArray(configTs, "capabilityEnvelope"));
155898
156641
  }
155899
156642
  function genNativeConfig(app, opts = {}) {
@@ -155902,7 +156645,7 @@ function genNativeConfig(app, opts = {}) {
155902
156645
  assertSingleModuleInstall(app, kotlinFilesWithModuleInstall(app));
155903
156646
  assertModuleFlagsMarkers([iosProjectYml(app), macosProjectYml(app)]);
155904
156647
  const configPath = appConfigTs(app);
155905
- const configTs = existsSync54(configPath) ? readFileSync64(configPath, "utf8") : "";
156648
+ const configTs = existsSync55(configPath) ? readFileSync64(configPath, "utf8") : "";
155906
156649
  const capabilities = parseStringArray(configTs, "capabilities");
155907
156650
  const envelope = parseStringArray(configTs, "capabilityEnvelope");
155908
156651
  const ownIds = buildSet(capabilities, envelope);
@@ -155911,7 +156654,7 @@ function genNativeConfig(app, opts = {}) {
155911
156654
  const ids = dist ? dist.build : ownIds;
155912
156655
  const healthLinked = ids.includes("health");
155913
156656
  const distJsonPath = join54(appDir(app), "distribution.json");
155914
- const distJsonText = existsSync54(distJsonPath) ? readFileSync64(distJsonPath, "utf8") : void 0;
156657
+ const distJsonText = existsSync55(distJsonPath) ? readFileSync64(distJsonPath, "utf8") : void 0;
155915
156658
  const optIns = distributionOptIns(distJsonText);
155916
156659
  const binaryCore = isBinaryCore(configTs, distJsonText);
155917
156660
  const modules = modulesFor(ids, optIns);
@@ -155927,7 +156670,7 @@ function genNativeConfig(app, opts = {}) {
155927
156670
  if (!v2) throw new Error(`${app}: a binary-core app that links native modules (${modules.gradle.join(", ")}) needs distribution.json coreVersion`);
155928
156671
  return v2;
155929
156672
  },
155930
- corePackage: () => existsSync54(iosProjectYml(app)) ? binaryCorePackage(readFileSync64(iosProjectYml(app), "utf8")) : void 0
156673
+ corePackage: () => existsSync55(iosProjectYml(app)) ? binaryCorePackage(readFileSync64(iosProjectYml(app), "utf8")) : void 0
155931
156674
  } : void 0
155932
156675
  );
155933
156676
  if (binaryCore) {
@@ -155946,9 +156689,9 @@ function genNativeConfig(app, opts = {}) {
155946
156689
  const iosBundleId = watchConfig.iosBundleId ?? parseIosBundleId(configTs);
155947
156690
  const watch2 = watchInfoPlan(watchConfig, iosBundleId);
155948
156691
  const watchYml = watchosProjectYml(app);
155949
- const watchYmlText = existsSync54(watchYml) ? readFileSync64(watchYml, "utf8") : "";
156692
+ const watchYmlText = existsSync55(watchYml) ? readFileSync64(watchYml, "utf8") : "";
155950
156693
  const watchSettings = watchSettingsPlan(watchMode(watchConfig.mode), iosBundleId, watchTargetBundleId(watchYmlText));
155951
- const watchName = existsSync54(watchYml) ? watchProjectName(readFileSync64(watchYml, "utf8")) : void 0;
156694
+ const watchName = existsSync55(watchYml) ? watchProjectName(readFileSync64(watchYml, "utf8")) : void 0;
155952
156695
  const watchEmbedFor = (lines) => (indent) => {
155953
156696
  const mode = watchMode(watchConfig.mode);
155954
156697
  if (mode === "standalone") return [];
@@ -155962,7 +156705,7 @@ function genNativeConfig(app, opts = {}) {
155962
156705
  const carBlock = applyToFile(carManifestPath, "xml", LABEL_CAR, (i2) => renderCarManifestXml(carCategory, i2), check);
155963
156706
  assertCarBlockLanded(app, carCategory, carManifestPath, carBlock.outcome, LABEL_CAR);
155964
156707
  const iosYml = iosProjectYml(app);
155965
- const project = existsSync54(iosYml) ? readFileSync64(iosYml, "utf8") : "";
156708
+ const project = existsSync55(iosYml) ? readFileSync64(iosYml, "utf8") : "";
155966
156709
  const targetManaged = hasMarkers(project, "yaml", LABEL_LIVE_TARGET);
155967
156710
  const embedManaged = hasMarkers(project, "yaml", LABEL_LIVE_EMBED);
155968
156711
  if (targetManaged !== embedManaged) throw new Error(`${app}: Live Activity target and embedding markers must be installed together`);
@@ -156046,7 +156789,7 @@ function genNativeConfig(app, opts = {}) {
156046
156789
  ];
156047
156790
  if (dist) {
156048
156791
  const manifestPath = androidManifestPath2(app);
156049
- const marked = (path2, kind, label3) => !existsSync54(path2) || hasMarkers(readFileSync64(path2, "utf8"), kind, label3);
156792
+ const marked = (path2, kind, label3) => !existsSync55(path2) || hasMarkers(readFileSync64(path2, "utf8"), kind, label3);
156050
156793
  if (!marked(iosYml, "yaml", LABEL_IOS_INFO)) throw new Error(`${app}: dist capabilities need the ${LABEL_IOS_INFO} markers in ${iosYml}`);
156051
156794
  if (!marked(manifestPath, "xml", LABEL_ANDROID_SERVICES)) {
156052
156795
  throw new Error(`${app}: dist capabilities need the ${LABEL_ANDROID_SERVICES} markers inside <application> in ${manifestPath}`);
@@ -156057,7 +156800,7 @@ function genNativeConfig(app, opts = {}) {
156057
156800
  const addsGradle = added.gradle.some((p2) => !ownModules.gradle.includes(p2));
156058
156801
  if (addsSpm || addsGradle) {
156059
156802
  const requireMarkers = (path2, kind, label3) => {
156060
- if (!existsSync54(path2) || !hasMarkers(readFileSync64(path2, "utf8"), kind, label3)) {
156803
+ if (!existsSync55(path2) || !hasMarkers(readFileSync64(path2, "utf8"), kind, label3)) {
156061
156804
  throw new Error(`${app}: dist added ${dist.added.join(", ")} but ${path2} has no ${label3} markers \u2014 the module would stay unlinked and every verb would answer module_not_linked`);
156062
156805
  }
156063
156806
  };
@@ -156082,9 +156825,9 @@ function genNativeConfig(app, opts = {}) {
156082
156825
  files.push(...genCapabilityRegistrations(appDir(app), ids, check));
156083
156826
  const rce = declaresRichContentEditor(configTs);
156084
156827
  for (const yml of [iosYml, macosProjectYml(app)]) {
156085
- const text11 = existsSync54(yml) ? readFileSync64(yml, "utf8") : "";
156828
+ const text11 = existsSync55(yml) ? readFileSync64(yml, "utf8") : "";
156086
156829
  const r3 = applyToFile(yml, "yaml", LABEL_LOCALWEB, renderLocalWebYaml(resourcesDirOf(text11), rce), check);
156087
- if (existsSync54(yml)) assertLocalWebBlockLanded(app, rce, yml, r3.outcome);
156830
+ if (existsSync55(yml)) assertLocalWebBlockLanded(app, rce, yml, r3.outcome);
156088
156831
  files.push(r3);
156089
156832
  }
156090
156833
  if (targetManaged) {
@@ -156096,7 +156839,7 @@ function genNativeConfig(app, opts = {}) {
156096
156839
  const entitlements = join54(dirname31(iosYml), LIVE_ENTITLEMENTS_PATH);
156097
156840
  files.push(plan.entitlements ? applyWholeFile(entitlements, plan.entitlements, check) : removeWholeFile(entitlements, check));
156098
156841
  }
156099
- const iosSplashManaged = existsSync54(iosYml) && hasMarkers(readFileSync64(iosYml, "utf8"), "yaml", LABEL_IOS_SPLASH);
156842
+ const iosSplashManaged = existsSync55(iosYml) && hasMarkers(readFileSync64(iosYml, "utf8"), "yaml", LABEL_IOS_SPLASH);
156100
156843
  const manifest2 = androidManifestPath2(app);
156101
156844
  const splash = splashPlan(app, configTs, manifest2, iosSplashManaged);
156102
156845
  for (const artifact of splash.artifacts) files.push(applyWholeFile(artifact.path, artifact.content, check));
@@ -156189,7 +156932,7 @@ __export(capabilities_exports, {
156189
156932
  shellSupport: () => shellSupport,
156190
156933
  writeLastDist: () => writeLastDist
156191
156934
  });
156192
- import { existsSync as existsSync55, lstatSync as lstatSync18, readFileSync as readFileSync65 } from "node:fs";
156935
+ import { existsSync as existsSync56, lstatSync as lstatSync18, readFileSync as readFileSync65 } from "node:fs";
156193
156936
  import { join as join55 } from "node:path";
156194
156937
  function declareHintFor(id) {
156195
156938
  return catalogKey(id) === "app_services" ? "each calling screen's `logicBundle.declaredCapabilities` (the runtime gate) \u2014 an app need not list app_services in config.ts `capabilities`, which only a shell that hosts app services must (an HHP app declares it there, since HHP copies that list onto every screen)" : DECLARE_HINT;
@@ -156271,7 +157014,7 @@ function allCapabilities(sdkRef = []) {
156271
157014
  function contentShell(dir, flag) {
156272
157015
  if (flag) return flag;
156273
157016
  const path2 = join55(dir, "himalaya.content.json");
156274
- if (!existsSync55(path2)) return null;
157017
+ if (!existsSync56(path2)) return null;
156275
157018
  try {
156276
157019
  const shell = JSON.parse(readFileSync65(path2, "utf8")).shell;
156277
157020
  return typeof shell === "string" && shell ? shell : null;
@@ -156569,7 +157312,7 @@ function envelopeSubsetIssues(app, envelope) {
156569
157312
  function capabilityConfigFindings(sourceDir, builtDir) {
156570
157313
  const out2 = [];
156571
157314
  const configPath = join55(sourceDir, "config.ts");
156572
- if (existsSync55(configPath)) {
157315
+ if (existsSync56(configPath)) {
156573
157316
  const configSrc = readFileSync65(configPath, "utf8");
156574
157317
  const declared = parseStringArray(configSrc, "capabilities");
156575
157318
  for (const id of new Set(declared)) {
@@ -156738,6 +157481,9 @@ async function navIssues(dir, strict) {
156738
157481
  pushScreenMapIssues(cfg.push, provisioning, warn3);
156739
157482
  pushAuthIssues(cfg.push, cfg.auth, provisioning, warn3);
156740
157483
  pushFamilyIssues(cfg.push, warn3);
157484
+ const captcha = provisioning.siteId && !placeholderLogin(cfg.auth) ? captchaAuthFinding(cfg.auth, provisioning.siteId) : void 0;
157485
+ if (captcha && captcha.code !== "captcha_auth_site_case" && !issues.some((i2) => i2.ruleCode === "push-auth-site"))
157486
+ push("warning", "auth-captcha-site", captcha.message);
156741
157487
  if (nav === void 0 || nav === null) return issues;
156742
157488
  if (!isPlainObject2(nav)) {
156743
157489
  error("nav-not-an-object", `navigation must be an object (got ${Array.isArray(nav) ? "array" : typeof nav}).`);
@@ -156875,6 +157621,9 @@ function pushAuthIssues(push, auth, provisioning, warn3) {
156875
157621
  `push.registrar is "wix-ping", which registers a signed-in member's device with Wix Ping, but ${problem}. Use auth: WixConsumerAuthProvider({ siteId: "${provisioning.siteId ?? "<metaSiteId>"}", \u2026 }).`
156876
157622
  );
156877
157623
  }
157624
+ function placeholderLogin(auth) {
157625
+ return isPlainObject2(auth) && isPlainObject2(auth.config) && auth.config.clientId === PLACEHOLDER_CLIENT_ID;
157626
+ }
156878
157627
  function pushFamilyIssues(push, warn3) {
156879
157628
  if (!isPlainObject2(push) || push.registrar !== "wix-ping" || typeof push.pingAppName !== "string") return;
156880
157629
  const replacement = replacementPingFamily(push.pingAppName);
@@ -156937,6 +157686,8 @@ var init_nav_lint = __esm({
156937
157686
  init_nav_slots();
156938
157687
  init_home_screen_id();
156939
157688
  init_wix_push_links();
157689
+ init_captcha_auth();
157690
+ init_wix_placeholders();
156940
157691
  NAVIGATION_KEYS = /* @__PURE__ */ new Set([
156941
157692
  "tabBar",
156942
157693
  "drawer",
@@ -157017,7 +157768,7 @@ __export(base44_types_exports, {
157017
157768
  renderBase44Types: () => renderBase44Types,
157018
157769
  writeBase44Types: () => writeBase44Types
157019
157770
  });
157020
- import { existsSync as existsSync56, mkdirSync as mkdirSync26, readFileSync as readFileSync66, writeFileSync as writeFileSync22 } from "node:fs";
157771
+ import { existsSync as existsSync57, mkdirSync as mkdirSync26, readFileSync as readFileSync66, writeFileSync as writeFileSync22 } from "node:fs";
157021
157772
  import { dirname as dirname32, join as join56 } from "node:path";
157022
157773
  function logicalName(spec) {
157023
157774
  return (isAdoptedEntity(spec) ? spec.base44Name : spec.name) ?? "";
@@ -157097,7 +157848,7 @@ async function writeBase44Types(dir, app) {
157097
157848
  if (!list2?.length) throw new Base44TypesError("no_base44_entities", "config.ts declares no base44.entities \u2014 nothing to type. Declare them (or run `himi base44 adopt`) first.");
157098
157849
  const text11 = renderBase44Types(list2, app);
157099
157850
  const path2 = join56(dir, BASE44_TYPES_PATH);
157100
- const changed = !existsSync56(path2) || readFileSync66(path2, "utf8") !== text11;
157851
+ const changed = !existsSync57(path2) || readFileSync66(path2, "utf8") !== text11;
157101
157852
  if (changed) {
157102
157853
  mkdirSync26(dirname32(path2), { recursive: true });
157103
157854
  writeFileSync22(path2, text11);
@@ -157106,7 +157857,7 @@ async function writeBase44Types(dir, app) {
157106
157857
  }
157107
157858
  async function base44TypesIssues(dir, app, strict) {
157108
157859
  const path2 = join56(dir, BASE44_TYPES_PATH);
157109
- if (!existsSync56(path2)) return [];
157860
+ if (!existsSync57(path2)) return [];
157110
157861
  let list2;
157111
157862
  try {
157112
157863
  list2 = await declaredEntities(dir);
@@ -157249,13 +158000,13 @@ __export(eject_exports, {
157249
158000
  EJECTABLE: () => EJECTABLE,
157250
158001
  ejectModule: () => ejectModule
157251
158002
  });
157252
- import { existsSync as existsSync58, mkdirSync as mkdirSync27, readFileSync as readFileSync68, writeFileSync as writeFileSync23 } from "node:fs";
158003
+ import { existsSync as existsSync59, mkdirSync as mkdirSync27, readFileSync as readFileSync68, writeFileSync as writeFileSync23 } from "node:fs";
157253
158004
  import { dirname as dirname33, join as join58, relative as relative10, resolve as resolve47, sep as sep16 } from "node:path";
157254
158005
  function locate(repoRootOrCwd, mod) {
157255
158006
  const inRepo = resolve47(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
157256
- if (existsSync58(inRepo)) return { path: inRepo, form: "source" };
158007
+ if (existsSync59(inRepo)) return { path: inRepo, form: "source" };
157257
158008
  const dist = resolve47(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
157258
- if (existsSync58(dist)) return { path: dist, form: "bundle" };
158009
+ if (existsSync59(dist)) return { path: dist, form: "bundle" };
157259
158010
  return null;
157260
158011
  }
157261
158012
  function specifiersFor(mod) {
@@ -157300,7 +158051,7 @@ function ejectModule(opts) {
157300
158051
  writeFileSync23(target, banner + normalized);
157301
158052
  const rewired = [];
157302
158053
  for (const f2 of files) {
157303
- if (!existsSync58(f2)) continue;
158054
+ if (!existsSync59(f2)) continue;
157304
158055
  const before = readFileSync68(f2, "utf8");
157305
158056
  let after = before;
157306
158057
  let rel = relative10(dirname33(f2), target).split(sep16).join("/").replace(/\.tsx?$/, ".js");
@@ -157479,7 +158230,7 @@ __export(icon_exports, {
157479
158230
  summarize: () => summarize3
157480
158231
  });
157481
158232
  import { createHash as createHash31 } from "node:crypto";
157482
- import { existsSync as existsSync59, mkdirSync as mkdirSync28, readFileSync as readFileSync69, statSync as statSync18, writeFileSync as writeFileSync24 } from "node:fs";
158233
+ import { existsSync as existsSync60, mkdirSync as mkdirSync28, readFileSync as readFileSync69, statSync as statSync18, writeFileSync as writeFileSync24 } from "node:fs";
157483
158234
  import { basename as basename16, dirname as dirname34, join as join59, resolve as resolve48, sep as sep17 } from "node:path";
157484
158235
  function readManifest(dir) {
157485
158236
  try {
@@ -157518,7 +158269,7 @@ function templateIconMatch(png) {
157518
158269
  }
157519
158270
  function playState(dir) {
157520
158271
  const p2 = join59(resolve48(dir), PLAY_LISTING_ICON);
157521
- if (!existsSync59(p2)) return null;
158272
+ if (!existsSync60(p2)) return null;
157522
158273
  try {
157523
158274
  if (!statSync18(p2).isFile()) return { path: p2, issues: ["is a directory, not a PNG file"] };
157524
158275
  return { path: p2, issues: playListingIconIssues(readFileSync69(p2)) };
@@ -157544,7 +158295,7 @@ async function iconStatus(dir) {
157544
158295
  verdict: "missing",
157545
158296
  policyChecklist: POLICY_CHECKLIST
157546
158297
  };
157547
- if (!path2 || !existsSync59(path2)) return base;
158298
+ if (!path2 || !existsSync60(path2)) return base;
157548
158299
  let png;
157549
158300
  try {
157550
158301
  if (!statSync18(path2).isFile()) {
@@ -157634,7 +158385,7 @@ function backdropFromTokens(dir, fallback = "#1F2933") {
157634
158385
  }
157635
158386
  async function installIcon(dir, sourcePath, opts = {}) {
157636
158387
  const src = resolve48(sourcePath);
157637
- if (!existsSync59(src)) throw new Error(`himi icon: no such file: ${src}`);
158388
+ if (!existsSync60(src)) throw new Error(`himi icon: no such file: ${src}`);
157638
158389
  const name = declaredIconName(dir) ?? DEFAULT_CONTENT_ICON;
157639
158390
  const dest = iconDest(dir, name);
157640
158391
  const wrote = [];
@@ -157688,11 +158439,11 @@ function declareIcon(dir, name) {
157688
158439
  }
157689
158440
  function siteBookIcon(bookDir) {
157690
158441
  const p2 = join59(resolve48(bookDir), SITE_BOOK_ICON_REL);
157691
- return existsSync59(p2) ? p2 : null;
158442
+ return existsSync60(p2) ? p2 : null;
157692
158443
  }
157693
158444
  function siteLogoAbsence(bookDir) {
157694
158445
  const bookJson = join59(resolve48(bookDir), "book.json");
157695
- if (!existsSync59(bookJson)) {
158446
+ if (!existsSync60(bookJson)) {
157696
158447
  return { reason: "no-book", hint: `no business book at ${bookDir} \u2014 run \`himi site analyze --out <dir>\` first, or pass --book <dir>` };
157697
158448
  }
157698
158449
  let logoUrl;
@@ -157976,7 +158727,7 @@ var init_crawl_layers = __esm({
157976
158727
  });
157977
158728
 
157978
158729
  // src/wix-writes.ts
157979
- import { existsSync as existsSync61, readFileSync as readFileSync71 } from "node:fs";
158730
+ import { existsSync as existsSync62, readFileSync as readFileSync71 } from "node:fs";
157980
158731
  import { join as join62 } from "node:path";
157981
158732
  function isProvenRead(method, path2) {
157982
158733
  if (/[#\x00-\x1F\x7F]/.test(path2)) return false;
@@ -158096,7 +158847,7 @@ function parseDataAccess(text11, from) {
158096
158847
  function readDataAccess(dir) {
158097
158848
  for (const name of ["SPEC.md", "MOBILE-UX.md"]) {
158098
158849
  const p2 = join62(dir, name);
158099
- if (!existsSync61(p2)) continue;
158850
+ if (!existsSync62(p2)) continue;
158100
158851
  let text11;
158101
158852
  try {
158102
158853
  text11 = readFileSync71(p2, "utf8");
@@ -158215,11 +158966,11 @@ __export(test_exports, {
158215
158966
  scopeDescriptors: () => scopeDescriptors,
158216
158967
  writeRecordedMocks: () => writeRecordedMocks
158217
158968
  });
158218
- import { existsSync as existsSync62, mkdirSync as mkdirSync29, readFileSync as readFileSync72, writeFileSync as writeFileSync25 } from "node:fs";
158969
+ import { existsSync as existsSync63, mkdirSync as mkdirSync29, readFileSync as readFileSync72, writeFileSync as writeFileSync25 } from "node:fs";
158219
158970
  import { join as join63, resolve as resolve49 } from "node:path";
158220
158971
  function readTestConfig(dir) {
158221
158972
  const p2 = resolve49(dir, TEST_CONFIG_FILE);
158222
- if (!existsSync62(p2)) return {};
158973
+ if (!existsSync63(p2)) return {};
158223
158974
  let parsed;
158224
158975
  try {
158225
158976
  parsed = JSON.parse(readFileSync72(p2, "utf8"));
@@ -158290,7 +159041,7 @@ function readNetRules(path2) {
158290
159041
  function defaultNetRules(dir) {
158291
159042
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
158292
159043
  const p2 = resolve49(dir, rel);
158293
- if (existsSync62(p2)) {
159044
+ if (existsSync63(p2)) {
158294
159045
  const rules = readNetRules(p2);
158295
159046
  if (rules.length) return { rules, from: rel };
158296
159047
  }
@@ -159382,7 +160133,7 @@ var init_release_payload_hash = __esm({
159382
160133
  import { execFileSync as execFileSync5 } from "node:child_process";
159383
160134
  import { createHash as createHash35, randomBytes as randomBytes2 } from "node:crypto";
159384
160135
  import {
159385
- existsSync as existsSync63,
160136
+ existsSync as existsSync64,
159386
160137
  lstatSync as lstatSync20,
159387
160138
  mkdirSync as mkdirSync31,
159388
160139
  realpathSync as realpathSync9,
@@ -159512,7 +160263,7 @@ function readLatestAttestation(contentDir2) {
159512
160263
  }
159513
160264
  function readRetainedAttestations(contentDir2) {
159514
160265
  const dir = join66(resolve53(contentDir2), ".himi", "attestations");
159515
- if (!existsSync63(dir)) return { runs: [], errors: [] };
160266
+ if (!existsSync64(dir)) return { runs: [], errors: [] };
159516
160267
  try {
159517
160268
  const names2 = readdirSync30(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
159518
160269
  const parsedRuns = [];
@@ -159529,7 +160280,7 @@ function readRetainedAttestations(contentDir2) {
159529
160280
  } else errors.push(`attestation history contains malformed retained run ${name}`);
159530
160281
  }
159531
160282
  const latest = readLatestAttestation(contentDir2);
159532
- if (existsSync63(join66(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
160283
+ if (existsSync64(join66(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
159533
160284
  else if (names2.length > 0 && !latest) errors.push("attestation history has no latest.json");
159534
160285
  const byHash = new Map(parsedRuns.map((run2) => [run2.selfHash, run2]));
159535
160286
  if (byHash.size !== parsedRuns.length) errors.push("attestation history contains duplicate self hashes");
@@ -159537,7 +160288,7 @@ function readRetainedAttestations(contentDir2) {
159537
160288
  if (selfHashFor(run2) !== run2.selfHash) errors.push(`selfHash does not match retained run ${run2.runId}`);
159538
160289
  for (const expected of Object.values(run2.screenshots)) {
159539
160290
  const path2 = screenshotArtifactPath(contentDir2, expected);
159540
- if (!existsSync63(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename18(path2)}`);
160291
+ if (!existsSync64(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename18(path2)}`);
159541
160292
  else if (sha2565(readFileSync74(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename18(path2)}`);
159542
160293
  }
159543
160294
  }
@@ -159608,7 +160359,7 @@ function writeAttestation(input, opts = {}) {
159608
160359
  const descriptorHashes = Object.fromEntries(
159609
160360
  Object.entries(input.descriptors).sort(([a2], [b2]) => a2.localeCompare(b2)).map(([screen, bytes2]) => [screen, sha2565(bytes2)])
159610
160361
  );
159611
- const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a2], [b2]) => a2.localeCompare(b2)).filter(([, path2]) => existsSync63(path2)).map(([screen, path2]) => {
160362
+ const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a2], [b2]) => a2.localeCompare(b2)).filter(([, path2]) => existsSync64(path2)).map(([screen, path2]) => {
159612
160363
  const bytes2 = readFileSync74(path2);
159613
160364
  return { screen, bytes: bytes2, hash: sha2565(bytes2) };
159614
160365
  });
@@ -159616,7 +160367,7 @@ function writeAttestation(input, opts = {}) {
159616
160367
  for (const { bytes: bytes2, hash: hash2 } of screenshotEntries) {
159617
160368
  const artifact = screenshotArtifactPath(contentDir2, hash2);
159618
160369
  mkdirSync31(join66(dir, "artifacts"), { recursive: true });
159619
- if (!existsSync63(artifact)) writeFileSync27(artifact, bytes2);
160370
+ if (!existsSync64(artifact)) writeFileSync27(artifact, bytes2);
159620
160371
  }
159621
160372
  const unsigned = {
159622
160373
  attestation: 2,
@@ -159661,7 +160412,7 @@ function writeAttestation(input, opts = {}) {
159661
160412
  retainedRuns.flatMap((run2) => Object.values(run2.screenshots))
159662
160413
  );
159663
160414
  const artifactDir = join66(dir, "artifacts");
159664
- if (existsSync63(artifactDir)) {
160415
+ if (existsSync64(artifactDir)) {
159665
160416
  for (const name of readdirSync30(artifactDir).filter((candidate) => candidate.endsWith(".png"))) {
159666
160417
  if (!retainedScreenshotHashes.has(name.slice(0, -4))) unlinkSync4(join66(artifactDir, name));
159667
160418
  }
@@ -159737,7 +160488,7 @@ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, r
159737
160488
  }
159738
160489
  for (const [screen, expected] of Object.entries(attestation.screenshots)) {
159739
160490
  const path2 = screenshotArtifactPath(contentDir2, expected);
159740
- if (!existsSync63(path2)) {
160491
+ if (!existsSync64(path2)) {
159741
160492
  errors.push(`missing screenshot for ${screen}: ${basename18(path2)}`);
159742
160493
  continue;
159743
160494
  }
@@ -159761,7 +160512,7 @@ var init_attestation = __esm({
159761
160512
  });
159762
160513
 
159763
160514
  // src/ux-evidence.ts
159764
- import { existsSync as existsSync64, mkdirSync as mkdirSync32, readFileSync as readFileSync75, writeFileSync as writeFileSync28 } from "node:fs";
160515
+ import { existsSync as existsSync65, mkdirSync as mkdirSync32, readFileSync as readFileSync75, writeFileSync as writeFileSync28 } from "node:fs";
159765
160516
  import { dirname as dirname38, isAbsolute as isAbsolute6, join as join67, relative as relative12, resolve as resolve54 } from "node:path";
159766
160517
  function isNativePlatform(value2) {
159767
160518
  return typeof value2 === "string" && NATIVE_PLATFORMS.includes(value2);
@@ -159771,7 +160522,7 @@ function isNativeAuditStatus(value2) {
159771
160522
  }
159772
160523
  function readText(path2) {
159773
160524
  try {
159774
- return existsSync64(path2) ? readFileSync75(path2, "utf8") : null;
160525
+ return existsSync65(path2) ? readFileSync75(path2, "utf8") : null;
159775
160526
  } catch {
159776
160527
  return null;
159777
160528
  }
@@ -159873,7 +160624,7 @@ function uxEvidenceForRelease(content, release2) {
159873
160624
  inheritedScreens: [],
159874
160625
  malformed: []
159875
160626
  };
159876
- if (!existsSync64(path2)) return { ...base, state: "absent" };
160627
+ if (!existsSync65(path2)) return { ...base, state: "absent" };
159877
160628
  const parsed = parseEvidence(path2);
159878
160629
  if (parsed.error) return { ...base, state: "unreadable", error: parsed.error };
159879
160630
  if (!parsed.evidence) return { ...base, state: "absent" };
@@ -159944,11 +160695,11 @@ var mobile_ux_lint_exports = {};
159944
160695
  __export(mobile_ux_lint_exports, {
159945
160696
  mobileUxIssues: () => mobileUxIssues
159946
160697
  });
159947
- import { existsSync as existsSync65, readFileSync as readFileSync76 } from "node:fs";
160698
+ import { existsSync as existsSync66, readFileSync as readFileSync76 } from "node:fs";
159948
160699
  import { join as join68 } from "node:path";
159949
160700
  function mobileUxIssues(dir, strict) {
159950
160701
  const file2 = join68(dir, "MOBILE-UX.md");
159951
- if (!existsSync65(file2)) return [];
160702
+ if (!existsSync66(file2)) return [];
159952
160703
  let text11;
159953
160704
  try {
159954
160705
  text11 = readFileSync76(file2, "utf8");
@@ -161632,7 +162383,7 @@ __export(functions_exports, {
161632
162383
  resolveFunctionsTarget: () => resolveFunctionsTarget,
161633
162384
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
161634
162385
  });
161635
- import { readFileSync as readFileSync79, existsSync as existsSync68 } from "node:fs";
162386
+ import { readFileSync as readFileSync79, existsSync as existsSync69 } from "node:fs";
161636
162387
  import { join as join71, resolve as resolve55 } from "node:path";
161637
162388
  function resolveFunctionsTarget(input) {
161638
162389
  const explicitUrl = input.functionsUrl?.trim();
@@ -161652,7 +162403,7 @@ async function loadFunctionsApp(dir) {
161652
162403
  }
161653
162404
  const sources = {};
161654
162405
  for (const definition of config.functions.functions) {
161655
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join71(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync68);
162406
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join71(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync69);
161656
162407
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
161657
162408
  sources[definition.name] = readFileSync79(source, "utf8");
161658
162409
  }
@@ -161842,7 +162593,7 @@ var init_cli_bridge = __esm({
161842
162593
  });
161843
162594
 
161844
162595
  // src/mcp/file-tools.ts
161845
- import { closeSync as closeSync6, constants as constants4, lstatSync as lstatSync21, mkdirSync as mkdirSync33, openSync as openSync5, readdirSync as readdirSync31, readFileSync as readFileSync81, realpathSync as realpathSync10, renameSync as renameSync11, rmSync as rmSync18, statSync as statSync19, writeSync, existsSync as existsSync69 } from "node:fs";
162596
+ import { closeSync as closeSync6, constants as constants4, lstatSync as lstatSync21, mkdirSync as mkdirSync33, openSync as openSync5, readdirSync as readdirSync31, readFileSync as readFileSync81, realpathSync as realpathSync10, renameSync as renameSync11, rmSync as rmSync18, statSync as statSync19, writeSync, existsSync as existsSync70 } from "node:fs";
161846
162597
  import { randomBytes as randomBytes3 } from "node:crypto";
161847
162598
  import { dirname as dirname39, isAbsolute as isAbsolute7, join as join72, relative as relative13, resolve as resolve56, sep as sep18 } from "node:path";
161848
162599
  import * as z2 from "zod";
@@ -161852,7 +162603,7 @@ function within(root, p2) {
161852
162603
  function realish(p2) {
161853
162604
  let cur = p2;
161854
162605
  const tail = [];
161855
- while (!existsSync69(cur)) {
162606
+ while (!existsSync70(cur)) {
161856
162607
  const up = dirname39(cur);
161857
162608
  if (up === cur) break;
161858
162609
  tail.unshift(cur.slice(up.length + (up.endsWith(sep18) ? 0 : 1)));
@@ -161875,7 +162626,7 @@ function symlinkComponent(base, abs) {
161875
162626
  function workspaceRefusal(root, target) {
161876
162627
  const r3 = resolve56(root);
161877
162628
  const t2 = resolve56(r3, target);
161878
- if (!existsSync69(r3)) return `the workspace folder ${r3} does not exist`;
162629
+ if (!existsSync70(r3)) return `the workspace folder ${r3} does not exist`;
161879
162630
  if (!within(r3, t2) || !within(realpathSync10(r3), realish(t2))) return `"${target}" is outside the workspace folder ${r3}`;
161880
162631
  const link = symlinkComponent(r3, t2);
161881
162632
  if (link) return `"${link}" is a symlink \u2014 paths must stay in real folders inside the workspace`;
@@ -161884,13 +162635,13 @@ function workspaceRefusal(root, target) {
161884
162635
  async function confinedAppDir(args, deps) {
161885
162636
  const root = resolve56(await deps.defaultDir());
161886
162637
  const dir = typeof args.dir === "string" && args.dir ? resolve56(root, args.dir) : root;
161887
- if (!existsSync69(root)) return refuse5("no_workspace", `the workspace folder ${root} does not exist`);
162638
+ if (!existsSync70(root)) return refuse5("no_workspace", `the workspace folder ${root} does not exist`);
161888
162639
  if (!within(root, dir) || !within(realpathSync10(root), realish(dir))) return refuse5("outside_workspace", `"${String(args.dir)}" is outside the workspace folder`);
161889
162640
  const link = symlinkComponent(root, dir);
161890
162641
  if (link) return refuse5("outside_workspace", `"${link}" is a symlink \u2014 the app folder must be a real folder in the workspace`);
161891
162642
  const rel = relative13(root, dir);
161892
162643
  if (rel && (isDeniedSourcePath(`${rel}/x`) || isDeniedSourcePath(`${rel.toLowerCase()}/x`))) return refuse5("denied", `"${rel}" is a folder this tool does not touch`);
161893
- if (!existsSync69(join72(dir, CONTENT_MARKER))) {
162644
+ if (!existsSync70(join72(dir, CONTENT_MARKER))) {
161894
162645
  return refuse5("not_an_app", `"${rel || "."}" is not an app folder (no ${CONTENT_MARKER}) \u2014 see himi_list_apps, or create one with himi_init`);
161895
162646
  }
161896
162647
  return dir;
@@ -161898,7 +162649,7 @@ async function confinedAppDir(args, deps) {
161898
162649
  function onDiskSpelling(abs) {
161899
162650
  let cur = abs;
161900
162651
  const tail = [];
161901
- while (!existsSync69(cur)) {
162652
+ while (!existsSync70(cur)) {
161902
162653
  const up = dirname39(cur);
161903
162654
  if (up === cur) break;
161904
162655
  tail.unshift(cur.slice(up.length + (up.endsWith(sep18) ? 0 : 1)));
@@ -161922,7 +162673,7 @@ function confinedPath(appDir3, rel) {
161922
162673
  const abs = resolve56(appDir3, rel);
161923
162674
  if (!within(appDir3, abs) || abs === appDir3) return { error: `"${rel}" is outside the app folder` };
161924
162675
  const real = realish(abs);
161925
- const base = existsSync69(appDir3) ? realpathSync10(appDir3) : realish(appDir3);
162676
+ const base = existsSync70(appDir3) ? realpathSync10(appDir3) : realish(appDir3);
161926
162677
  if (!within(base, real) || real === base) return { error: `"${rel}" is outside the app folder` };
161927
162678
  const link = symlinkComponent(appDir3, abs);
161928
162679
  if (link) return { error: `"${link}" is a symlink, which this tool does not follow` };
@@ -161979,11 +162730,11 @@ function fileTools() {
161979
162730
  inputSchema: z2.object({}),
161980
162731
  handler: async (_a3, d2) => {
161981
162732
  const root = resolve56(await d2.defaultDir());
161982
- if (!existsSync69(root)) return ok({ ok: true, workspace: root, apps: [] });
162733
+ if (!existsSync70(root)) return ok({ ok: true, workspace: root, apps: [] });
161983
162734
  const apps = [];
161984
- if (existsSync69(join72(root, CONTENT_MARKER))) apps.push(".");
162735
+ if (existsSync70(join72(root, CONTENT_MARKER))) apps.push(".");
161985
162736
  for (const e2 of readdirSync31(root, { withFileTypes: true })) {
161986
- if (e2.isDirectory() && !e2.name.startsWith(".") && existsSync69(join72(root, e2.name, CONTENT_MARKER))) apps.push(e2.name);
162737
+ if (e2.isDirectory() && !e2.name.startsWith(".") && existsSync70(join72(root, e2.name, CONTENT_MARKER))) apps.push(e2.name);
161987
162738
  }
161988
162739
  return ok({ ok: true, workspace: root, apps: apps.sort() });
161989
162740
  }
@@ -161997,7 +162748,7 @@ function fileTools() {
161997
162748
  handler: async (a2, d2) => {
161998
162749
  const app = await confinedAppDir(a2, d2);
161999
162750
  if (typeof app !== "string") return app;
162000
- if (!existsSync69(app) || !statSync19(app).isDirectory()) return refuse5("no_app", `no folder at ${app}`);
162751
+ if (!existsSync70(app) || !statSync19(app).isDirectory()) return refuse5("no_app", `no folder at ${app}`);
162001
162752
  return ok({ ok: true, dir: app, ...listFiles(app) });
162002
162753
  }
162003
162754
  },
@@ -162012,7 +162763,7 @@ function fileTools() {
162012
162763
  if (typeof app !== "string") return app;
162013
162764
  const p2 = confinedPath(app, a2.path);
162014
162765
  if (typeof p2 !== "string") return refuse5("bad_path", p2.error);
162015
- if (!existsSync69(p2) || !lstatSync21(p2).isFile()) return refuse5("not_found", `no file "${String(a2.path)}"`);
162766
+ if (!existsSync70(p2) || !lstatSync21(p2).isFile()) return refuse5("not_found", `no file "${String(a2.path)}"`);
162016
162767
  const size = statSync19(p2).size;
162017
162768
  if (size > MAX_FILE_BYTES) return refuse5("too_large", `"${String(a2.path)}" is ${size} bytes \u2014 over the ${MAX_FILE_BYTES}-byte read limit`);
162018
162769
  return ok({ ok: true, path: a2.path, bytes: size, content: readFileSync81(p2, "utf8") });
@@ -162039,7 +162790,7 @@ function fileTools() {
162039
162790
  if (typeof p2 !== "string") refused2.push({ path: f2.path, error: p2.error });
162040
162791
  else if (typeof f2.content !== "string") refused2.push({ path: f2.path, error: "content must be text" });
162041
162792
  else if (Buffer.byteLength(f2.content) > MAX_FILE_BYTES) refused2.push({ path: f2.path, error: `over the ${MAX_FILE_BYTES}-byte limit` });
162042
- else if (existsSync69(p2) && !lstatSync21(p2).isFile()) refused2.push({ path: f2.path, error: "not a regular file" });
162793
+ else if (existsSync70(p2) && !lstatSync21(p2).isFile()) refused2.push({ path: f2.path, error: "not a regular file" });
162043
162794
  else if (planned2.some((q2) => q2.abs === p2)) refused2.push({ path: f2.path, error: "the same file twice in one call" });
162044
162795
  else planned2.push({ abs: p2, rel: String(f2.path), content: f2.content });
162045
162796
  }
@@ -162072,7 +162823,7 @@ function fileTools() {
162072
162823
  for (const rel of paths2) {
162073
162824
  const p2 = confinedPath(app, rel);
162074
162825
  if (typeof p2 !== "string") refused2.push({ path: rel, error: p2.error });
162075
- else if (!existsSync69(p2)) refused2.push({ path: rel, error: "no such file" });
162826
+ else if (!existsSync70(p2)) refused2.push({ path: rel, error: "no such file" });
162076
162827
  else if (!lstatSync21(p2).isFile()) refused2.push({ path: rel, error: "not a regular file" });
162077
162828
  else if (!planned2.includes(p2)) {
162078
162829
  planned2.push(p2);
@@ -162867,14 +163618,14 @@ var init_open_url = __esm({
162867
163618
  });
162868
163619
 
162869
163620
  // src/ux-audit.ts
162870
- import { existsSync as existsSync72, readFileSync as readFileSync83, readdirSync as readdirSync32 } from "node:fs";
163621
+ import { existsSync as existsSync73, readFileSync as readFileSync83, readdirSync as readdirSync32 } from "node:fs";
162871
163622
  import { join as join76, relative as relative15, resolve as resolve61 } from "node:path";
162872
163623
  function issue(issues, strict, code, message2, path2) {
162873
163624
  issues.push({ code, message: message2, ...path2 ? { path: path2 } : {}, severity: strict ? "error" : "warning" });
162874
163625
  }
162875
163626
  function readText2(path2) {
162876
163627
  try {
162877
- return existsSync72(path2) ? readFileSync83(path2, "utf8") : null;
163628
+ return existsSync73(path2) ? readFileSync83(path2, "utf8") : null;
162878
163629
  } catch {
162879
163630
  return null;
162880
163631
  }
@@ -162974,7 +163725,7 @@ function auditUx(content, strict = false) {
162974
163725
  }
162975
163726
  if (usesAppNetwork(source)) {
162976
163727
  const mocksPath = join76(dir, "dev", "net-mocks.json");
162977
- if (!existsSync72(mocksPath)) {
163728
+ if (!existsSync73(mocksPath)) {
162978
163729
  issue(issues, strict, "missing-app-mocks", "This package uses a Wix/network client but has no app-specific dev/net-mocks.json. `himi test --record` writes one from the live responses it sees; then add fixtures for dynamic, entitlement, or recovery flows.", "dev/net-mocks.json");
162979
163730
  } else {
162980
163731
  try {
@@ -163022,7 +163773,7 @@ __export(native_decode_exports, {
163022
163773
  runNativeDecode: () => runNativeDecode
163023
163774
  });
163024
163775
  import { execFileSync as execFileSync6, spawnSync as spawnSync4 } from "node:child_process";
163025
- import { existsSync as existsSync73, mkdirSync as mkdirSync37, mkdtempSync as mkdtempSync5, readFileSync as readFileSync84, rmSync as rmSync20, writeFileSync as writeFileSync31 } from "node:fs";
163776
+ import { existsSync as existsSync74, mkdirSync as mkdirSync37, mkdtempSync as mkdtempSync5, readFileSync as readFileSync84, rmSync as rmSync20, writeFileSync as writeFileSync31 } from "node:fs";
163026
163777
  import { homedir as homedir10, tmpdir as tmpdir6 } from "node:os";
163027
163778
  import { join as join78 } from "node:path";
163028
163779
  import { fileURLToPath as fileURLToPath27 } from "node:url";
@@ -163034,8 +163785,8 @@ function nativeDecodeAndroidHarnessDir() {
163034
163785
  }
163035
163786
  function nativeOracleShipped() {
163036
163787
  return {
163037
- ios: existsSync73(join78(nativeDecodePackageDir(), "Package.swift")),
163038
- android: existsSync73(join78(nativeDecodeAndroidHarnessDir(), "gradlew"))
163788
+ ios: existsSync74(join78(nativeDecodePackageDir(), "Package.swift")),
163789
+ android: existsSync74(join78(nativeDecodeAndroidHarnessDir(), "gradlew"))
163039
163790
  };
163040
163791
  }
163041
163792
  function detectNativeDecode(options = {}) {
@@ -163046,7 +163797,7 @@ function detectNativeDecode(options = {}) {
163046
163797
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
163047
163798
  }
163048
163799
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
163049
- const packageAvailable = options.packageAvailable ?? existsSync73(join78(packageDir, "Package.swift"));
163800
+ const packageAvailable = options.packageAvailable ?? existsSync74(join78(packageDir, "Package.swift"));
163050
163801
  if (!packageAvailable) {
163051
163802
  return {
163052
163803
  platform: "ios",
@@ -163072,7 +163823,7 @@ function defaultAndroidSdkDir(hostPlatform, home) {
163072
163823
  function detectAndroidNativeDecode(options = {}) {
163073
163824
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
163074
163825
  const gradlew = join78(harnessDir, "gradlew");
163075
- const gradlewAvailable = options.gradlewAvailable ?? existsSync73(gradlew);
163826
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync74(gradlew);
163076
163827
  if (!gradlewAvailable) {
163077
163828
  return {
163078
163829
  platform: "android",
@@ -163080,7 +163831,7 @@ function detectAndroidNativeDecode(options = {}) {
163080
163831
  reason: "the in-repo Android Gradle harness is unavailable (off-repo installs do not ship the decoder oracle)"
163081
163832
  };
163082
163833
  }
163083
- const exists = typeof options.sdkAvailable === "function" ? options.sdkAvailable : typeof options.sdkAvailable === "boolean" ? /* @__PURE__ */ ((answer) => () => answer)(options.sdkAvailable) : existsSync73;
163834
+ const exists = typeof options.sdkAvailable === "function" ? options.sdkAvailable : typeof options.sdkAvailable === "boolean" ? /* @__PURE__ */ ((answer) => () => answer)(options.sdkAvailable) : existsSync74;
163084
163835
  const defaultDir = defaultAndroidSdkDir(options.hostPlatform ?? process.platform, options.homeDir ?? homedir10());
163085
163836
  const candidates = [
163086
163837
  options.androidHome ?? process.env.ANDROID_HOME,
@@ -163342,7 +164093,7 @@ __export(browser_authoring_exports, {
163342
164093
  });
163343
164094
  import { createServer as createServer4 } from "node:http";
163344
164095
  import { createHash as createHash38, randomBytes as randomBytes4 } from "node:crypto";
163345
- import { existsSync as existsSync74, readdirSync as readdirSync33, readFileSync as readFileSync85, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
164096
+ import { existsSync as existsSync75, readdirSync as readdirSync33, readFileSync as readFileSync85, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
163346
164097
  import { join as join79, posix as posix3, relative as relative16, sep as sep19 } from "node:path";
163347
164098
  function humanMs(ms2) {
163348
164099
  return ms2 < 1e3 ? `${ms2}ms` : `${Math.round(ms2 / 1e3)}s`;
@@ -163365,7 +164116,7 @@ function collectWorkerSources(tier5SrcDir, bundleName) {
163365
164116
  }
163366
164117
  }
163367
164118
  };
163368
- if (!existsSync74(root)) return null;
164119
+ if (!existsSync75(root)) return null;
163369
164120
  walk2(root, `/tier5-src/${bundleName}`);
163370
164121
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p2) => p2 in files);
163371
164122
  if (!entry) return null;
@@ -163392,7 +164143,7 @@ function collectRelativeSiblings(files, tier5SrcDir) {
163392
164143
  join79(onDisk, "index.js")
163393
164144
  ];
163394
164145
  for (const abs of candidates) {
163395
- if (!existsSync74(abs) || statSync20(abs).isDirectory()) continue;
164146
+ if (!existsSync75(abs) || statSync20(abs).isDirectory()) continue;
163396
164147
  const key3 = `/tier5-src/${relative16(tier5SrcDir, abs).split(sep19).join(posix3.sep)}`;
163397
164148
  if (key3 in files) break;
163398
164149
  files[key3] = readFileSync85(abs, "utf8");
@@ -163503,7 +164254,7 @@ async function readBody3(req) {
163503
164254
  async function runBrowserAuthoring(opts) {
163504
164255
  const log = opts.log ?? (() => {
163505
164256
  });
163506
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
164257
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
163507
164258
  const payload = encodeJob(opts.job);
163508
164259
  const token = mintToken2(payload);
163509
164260
  const unusable = await probeAuthoringPage(opts.authoringBase, opts.fetchImpl, opts.probeTimeoutMs);
@@ -163579,11 +164330,11 @@ async function runBrowserAuthoring(opts) {
163579
164330
  });
163580
164331
  });
163581
164332
  }
163582
- var DEFAULT_TIMEOUT_MS2, PROBE_TIMEOUT_MS, POINT_ELSEWHERE, NO_SUCH_ROUTE, NOT_STAGED, MAX_CALLBACK_BYTES;
164333
+ var DEFAULT_TIMEOUT_MS3, PROBE_TIMEOUT_MS, POINT_ELSEWHERE, NO_SUCH_ROUTE, NOT_STAGED, MAX_CALLBACK_BYTES;
163583
164334
  var init_browser_authoring = __esm({
163584
164335
  "src/browser-authoring.ts"() {
163585
164336
  init_open_url();
163586
- DEFAULT_TIMEOUT_MS2 = 5 * 60 * 1e3;
164337
+ DEFAULT_TIMEOUT_MS3 = 5 * 60 * 1e3;
163587
164338
  PROBE_TIMEOUT_MS = 5e3;
163588
164339
  POINT_ELSEWHERE = "Point --serve-url at a plane that mounts /v1/authoring/* (the local serve stack does NOT: core/serve-authoring is a separate service), or pass --target to select a deployed one.";
163589
164340
  NO_SUCH_ROUTE = /* @__PURE__ */ new Set([404, 410]);
@@ -163602,7 +164353,7 @@ __export(coverage_exports, {
163602
164353
  suppressedNativeKeys: () => suppressedNativeKeys,
163603
164354
  trendAndStore: () => trendAndStore
163604
164355
  });
163605
- import { existsSync as existsSync75, mkdirSync as mkdirSync38, readFileSync as readFileSync86, writeFileSync as writeFileSync32 } from "node:fs";
164356
+ import { existsSync as existsSync76, mkdirSync as mkdirSync38, readFileSync as readFileSync86, writeFileSync as writeFileSync32 } from "node:fs";
163606
164357
  import { dirname as dirname43, join as join80 } from "node:path";
163607
164358
  function suppressedNativeKeys(oracle) {
163608
164359
  return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
@@ -163786,7 +164537,7 @@ function trendAndStore(dir, entries, suppressed) {
163786
164537
  const p2 = join80(dir, LEDGER_FILE);
163787
164538
  let previous = null;
163788
164539
  try {
163789
- if (existsSync75(p2)) {
164540
+ if (existsSync76(p2)) {
163790
164541
  const parsed = JSON.parse(readFileSync86(p2, "utf8"));
163791
164542
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k2) => typeof k2 === "string");
163792
164543
  }
@@ -163829,7 +164580,7 @@ function diffAndStoreObservations(dir, current) {
163829
164580
  const p2 = join80(dir, OBS_FILE);
163830
164581
  let previous = null;
163831
164582
  try {
163832
- if (existsSync75(p2)) {
164583
+ if (existsSync76(p2)) {
163833
164584
  const parsed = JSON.parse(readFileSync86(p2, "utf8"));
163834
164585
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
163835
164586
  (v2) => Array.isArray(v2) && v2.every((l2) => typeof l2 === "string")
@@ -164202,11 +164953,11 @@ __export(design_kits_exports, {
164202
164953
  kitsDir: () => kitsDir,
164203
164954
  listKits: () => listKits2
164204
164955
  });
164205
- import { existsSync as existsSync77, readFileSync as readFileSync88, readdirSync as readdirSync34 } from "node:fs";
164956
+ import { existsSync as existsSync78, readFileSync as readFileSync88, readdirSync as readdirSync34 } from "node:fs";
164206
164957
  import { join as join83, basename as basename20 } from "node:path";
164207
164958
  import { fileURLToPath as fileURLToPath28 } from "node:url";
164208
164959
  function kitsDir() {
164209
- return KIT_DIR_CANDIDATES.find((p2) => existsSync77(p2));
164960
+ return KIT_DIR_CANDIDATES.find((p2) => existsSync78(p2));
164210
164961
  }
164211
164962
  function listKits2() {
164212
164963
  const dir = kitsDir();
@@ -164701,7 +165452,7 @@ __export(coverage_exports2, {
164701
165452
  verifyNativeProof: () => verifyNativeProof
164702
165453
  });
164703
165454
  import { createHash as createHash39 } from "node:crypto";
164704
- import { existsSync as existsSync79, readFileSync as readFileSync90, writeFileSync as writeFileSync33 } from "node:fs";
165455
+ import { existsSync as existsSync80, readFileSync as readFileSync90, writeFileSync as writeFileSync33 } from "node:fs";
164705
165456
  import { isAbsolute as isAbsolute8, join as join85, resolve as resolve64 } from "node:path";
164706
165457
  function verifiedEvidence(evidenceDir) {
164707
165458
  const manifest2 = obj3(JSON.parse(readFileSync90(join85(evidenceDir, "manifest.json"), "utf8")));
@@ -164797,7 +165548,7 @@ function deriveRequirements(evidenceDir) {
164797
165548
  function verifyNativeProof(contentDir2, releaseHash, requirementIds) {
164798
165549
  const root = resolve64(contentDir2);
164799
165550
  const path2 = join85(root, NATIVE_PROOF_FILE);
164800
- if (!existsSync79(path2)) return { ok: false, issues: [`${NATIVE_PROOF_FILE} is absent.`] };
165551
+ if (!existsSync80(path2)) return { ok: false, issues: [`${NATIVE_PROOF_FILE} is absent.`] };
164801
165552
  let proof;
164802
165553
  try {
164803
165554
  proof = obj3(JSON.parse(readFileSync90(path2, "utf8")));
@@ -164819,7 +165570,7 @@ function verifyNativeProof(contentDir2, releaseHash, requirementIds) {
164819
165570
  continue;
164820
165571
  }
164821
165572
  const artifact = record2.artifact;
164822
- if (typeof artifact !== "string" || !artifact.trim() || isAbsolute8(artifact) || artifact.split(/[\\/]/).includes("..") || !existsSync79(join85(root, artifact))) {
165573
+ if (typeof artifact !== "string" || !artifact.trim() || isAbsolute8(artifact) || artifact.split(/[\\/]/).includes("..") || !existsSync80(join85(root, artifact))) {
164823
165574
  issues.push(`Native walkthrough check ${id} needs a reviewable artifact file.`);
164824
165575
  }
164825
165576
  }
@@ -164828,7 +165579,7 @@ function verifyNativeProof(contentDir2, releaseHash, requirementIds) {
164828
165579
  function syncCoverage(evidenceDir, contentDir2, write2 = true) {
164829
165580
  const current = deriveRequirements(evidenceDir);
164830
165581
  const path2 = join85(resolve64(contentDir2), COVERAGE_FILE);
164831
- const existed = existsSync79(path2);
165582
+ const existed = existsSync80(path2);
164832
165583
  const old = existed ? obj3(JSON.parse(readFileSync90(path2, "utf8"))) : {};
164833
165584
  const hashesChanged = existed && JSON.stringify(old.sourceHashes) !== JSON.stringify(current.sourceHashes);
164834
165585
  const prior = Array.isArray(old.requirements) ? old.requirements : [];
@@ -164869,7 +165620,7 @@ function verifyCoverage(evidenceDir, contentDir2, releaseHash, builtScreenIds) {
164869
165620
  const saved = result.existed ? obj3(JSON.parse(readFileSync90(join85(resolve64(contentDir2), COVERAGE_FILE), "utf8"))) : {};
164870
165621
  const rows = Array.isArray(saved.requirements) ? saved.requirements.map(obj3) : [];
164871
165622
  const contentManifest = join85(resolve64(contentDir2), "himalaya.content.json");
164872
- const declaredScreens = existsSync79(contentManifest) ? obj3(obj3(JSON.parse(readFileSync90(contentManifest, "utf8"))).screens) : void 0;
165623
+ const declaredScreens = existsSync80(contentManifest) ? obj3(obj3(JSON.parse(readFileSync90(contentManifest, "utf8"))).screens) : void 0;
164873
165624
  const availableScreens = builtScreenIds ? new Set(builtScreenIds) : declaredScreens && Object.keys(declaredScreens).length ? new Set(Object.keys(declaredScreens)) : void 0;
164874
165625
  for (const row2 of rows) {
164875
165626
  const id = typeof row2.id === "string" ? row2.id : void 0;
@@ -164905,7 +165656,7 @@ function verifyCoverage(evidenceDir, contentDir2, releaseHash, builtScreenIds) {
164905
165656
  const path2 = check.evidence;
164906
165657
  if (typeof path2 !== "string" || !path2.trim() || isAbsolute8(path2) || path2.split(/[\\/]/).includes("..")) {
164907
165658
  issues.push({ id, message: `Check ${required3} needs a safe relative evidence path.` });
164908
- } else if (!existsSync79(join85(resolve64(contentDir2), path2))) {
165659
+ } else if (!existsSync80(join85(resolve64(contentDir2), path2))) {
164909
165660
  issues.push({ id, message: `Check ${required3} evidence file is absent: ${path2}.` });
164910
165661
  } else {
164911
165662
  try {
@@ -164916,7 +165667,7 @@ function verifyCoverage(evidenceDir, contentDir2, releaseHash, builtScreenIds) {
164916
165667
  issues.push({ id, message: `Check ${required3} has no latest passed record for this requirement and target.` });
164917
165668
  } else {
164918
165669
  const artifact = record2.artifact;
164919
- if (typeof artifact !== "string" || !artifact.trim() || isAbsolute8(artifact) || artifact.split(/[\\/]/).includes("..") || !existsSync79(join85(resolve64(contentDir2), artifact))) {
165670
+ if (typeof artifact !== "string" || !artifact.trim() || isAbsolute8(artifact) || artifact.split(/[\\/]/).includes("..") || !existsSync80(join85(resolve64(contentDir2), artifact))) {
164920
165671
  issues.push({ id, message: `Check ${required3} needs a reviewable artifact file.` });
164921
165672
  }
164922
165673
  }
@@ -164956,7 +165707,7 @@ __export(validate_topbar_exports, {
164956
165707
  runOneAppTopBarValidation: () => runOneAppTopBarValidation
164957
165708
  });
164958
165709
  import { createHash as createHash40 } from "node:crypto";
164959
- import { existsSync as existsSync80, readFileSync as readFileSync91, statSync as statSync22 } from "node:fs";
165710
+ import { existsSync as existsSync81, readFileSync as readFileSync91, statSync as statSync22 } from "node:fs";
164960
165711
  import { resolve as resolve65, join as join86 } from "node:path";
164961
165712
  import ts8 from "typescript";
164962
165713
  function enrichedControls(raw, views) {
@@ -165165,7 +165916,7 @@ function configActions(configPath) {
165165
165916
  }
165166
165917
  function screenActions2(contentDir2) {
165167
165918
  const path2 = join86(contentDir2, "dev", "index.ts");
165168
- if (!existsSync80(path2)) return /* @__PURE__ */ new Map();
165919
+ if (!existsSync81(path2)) return /* @__PURE__ */ new Map();
165169
165920
  const source = ts8.createSourceFile(path2, readFileSync91(path2, "utf8"), ts8.ScriptTarget.Latest, true, ts8.ScriptKind.TS);
165170
165921
  if (source.parseDiagnostics.length) throw Error("dev/index.ts could not be parsed.");
165171
165922
  const declaration = source.statements.filter(ts8.isVariableStatement).flatMap((statement) => [...statement.declarationList.declarations]).find((item) => ts8.isIdentifier(item.name) && item.name.text === "SCREENS");
@@ -165266,7 +166017,7 @@ function validate3(evidenceDir, contentDir2) {
165266
166017
  }
165267
166018
  const mappings = /* @__PURE__ */ new Map();
165268
166019
  const coveragePath = join86(contentDir2, "ONE-APP-COVERAGE.json");
165269
- if (existsSync80(coveragePath)) {
166020
+ if (existsSync81(coveragePath)) {
165270
166021
  try {
165271
166022
  const coverage = readJson7(coveragePath);
165272
166023
  for (const row2 of Array.isArray(coverage.requirements) ? coverage.requirements : []) {
@@ -166132,7 +166883,7 @@ async function generatePkce() {
166132
166883
  return { verifier, challenge: base64url(new Uint8Array(digest2)) };
166133
166884
  }
166134
166885
  async function hostedAuthUrl(req, visitorToken, fetchImpl = fetch) {
166135
- const res = await fetchImpl(`${WIX_API2}${REDIRECT_SESSION_PATH}`, {
166886
+ const res = await fetchImpl(`${WIX_API3}${REDIRECT_SESSION_PATH}`, {
166136
166887
  method: "POST",
166137
166888
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${visitorToken}` },
166138
166889
  body: JSON.stringify({
@@ -166175,7 +166926,7 @@ async function hostedAuthUrl(req, visitorToken, fetchImpl = fetch) {
166175
166926
  return fullUrl;
166176
166927
  }
166177
166928
  async function exchangeAuthCode(cfg, code, verifier, fetchImpl = fetch) {
166178
- const res = await fetchImpl(`${WIX_API2}${TOKEN_PATH}`, {
166929
+ const res = await fetchImpl(`${WIX_API3}${TOKEN_PATH}`, {
166179
166930
  method: "POST",
166180
166931
  headers: { "Content-Type": "application/json" },
166181
166932
  body: JSON.stringify({
@@ -166224,10 +166975,10 @@ function safeReturnPath(value2, fallback) {
166224
166975
  if (/[-]/.test(value2)) return fallback;
166225
166976
  return value2;
166226
166977
  }
166227
- var WIX_API2, REDIRECT_SESSION_PATH, TOKEN_PATH, MEMBER_SESSION_COOKIE, MEMBER_SESSION_TTL_MS, PENDING_LOGIN_TTL_MS, MEMBER_CALLBACK_PATH;
166978
+ var WIX_API3, REDIRECT_SESSION_PATH, TOKEN_PATH, MEMBER_SESSION_COOKIE, MEMBER_SESSION_TTL_MS, PENDING_LOGIN_TTL_MS, MEMBER_CALLBACK_PATH;
166228
166979
  var init_wix_member_auth = __esm({
166229
166980
  "../../core/serve-runtime/src/wix-member-auth.ts"() {
166230
- WIX_API2 = "https://www.wixapis.com";
166981
+ WIX_API3 = "https://www.wixapis.com";
166231
166982
  REDIRECT_SESSION_PATH = "/_api/redirects-api/v1/redirect-session";
166232
166983
  TOKEN_PATH = "/oauth2/token";
166233
166984
  MEMBER_SESSION_COOKIE = "himi_wm_sid";
@@ -168099,7 +168850,7 @@ __export(run_exports, {
168099
168850
  runPackage: () => runPackage,
168100
168851
  withoutProdServe: () => withoutProdServe
168101
168852
  });
168102
- import { cpSync as cpSync2, existsSync as existsSync83, mkdirSync as mkdirSync39, mkdtempSync as mkdtempSync7, readFileSync as readFileSync93, rmSync as rmSync23, statSync as statSync24 } from "node:fs";
168853
+ import { cpSync as cpSync2, existsSync as existsSync84, mkdirSync as mkdirSync39, mkdtempSync as mkdtempSync7, readFileSync as readFileSync93, rmSync as rmSync23, statSync as statSync24 } from "node:fs";
168103
168854
  import { networkInterfaces as osNetworkInterfaces, tmpdir as tmpdir8 } from "node:os";
168104
168855
  import { dirname as dirname44, join as join89 } from "node:path";
168105
168856
  import { fileURLToPath as fileURLToPath29 } from "node:url";
@@ -168129,9 +168880,9 @@ async function fetchBytes(url) {
168129
168880
  }
168130
168881
  async function resolvePackage(source, workDir) {
168131
168882
  const isUrl2 = /^https?:\/\//i.test(source);
168132
- if (!isUrl2 && existsSync83(source) && statSync24(source).isDirectory()) {
168883
+ if (!isUrl2 && existsSync84(source) && statSync24(source).isDirectory()) {
168133
168884
  const appDirManifest = join89(source, "release-manifest.json");
168134
- if (existsSync83(appDirManifest)) {
168885
+ if (existsSync84(appDirManifest)) {
168135
168886
  const opened2 = openPackageDir(source);
168136
168887
  if (!opened2.validation.ok) {
168137
168888
  throw new Error(`package is not valid:
@@ -168189,7 +168940,7 @@ async function loadPreviewAssets() {
168189
168940
  // in-repo checkout
168190
168941
  ];
168191
168942
  for (const path2 of candidates) {
168192
- if (!existsSync83(path2)) continue;
168943
+ if (!existsSync84(path2)) continue;
168193
168944
  const blob = readFileSync93(path2);
168194
168945
  return mod.assemblePreviewAssetsFromBlob(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
168195
168946
  }
@@ -168328,7 +169079,7 @@ __export(run_browser_exports, {
168328
169079
  runInBrowser: () => runInBrowser
168329
169080
  });
168330
169081
  import { createServer as createServer6 } from "node:http";
168331
- import { existsSync as existsSync84, readFileSync as readFileSync94 } from "node:fs";
169082
+ import { existsSync as existsSync85, readFileSync as readFileSync94 } from "node:fs";
168332
169083
  import { spawnSync as spawnSync5 } from "node:child_process";
168333
169084
  import { dirname as dirname45, resolve as resolve69 } from "node:path";
168334
169085
  import { fileURLToPath as fileURLToPath30 } from "node:url";
@@ -168336,7 +169087,7 @@ function stagedDir() {
168336
169087
  return process.env.HIMI_BROWSER_PLANE_OUT ? resolve69(process.env.HIMI_BROWSER_PLANE_OUT) : resolve69(REPO2, "packages", "serve", "dist", "browser-plane");
168337
169088
  }
168338
169089
  function missingStaged() {
168339
- return [...SERVED].filter((f2) => !existsSync84(resolve69(stagedDir(), f2))).sort();
169090
+ return [...SERVED].filter((f2) => !existsSync85(resolve69(stagedDir(), f2))).sort();
168340
169091
  }
168341
169092
  function ensureBrowserPlaneStaged() {
168342
169093
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
@@ -168426,7 +169177,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
168426
169177
  }
168427
169178
  const name = path2 === "/" || path2 === "/index.html" ? "host.html" : path2.slice(1);
168428
169179
  if (SERVED.has(name)) {
168429
- if (!existsSync84(resolve69(stagedDir(), name))) {
169180
+ if (!existsSync85(resolve69(stagedDir(), name))) {
168430
169181
  const restaged = ensureBrowserPlaneStaged();
168431
169182
  if (!restaged.ok) {
168432
169183
  res.writeHead(503, { "content-type": "application/json" });
@@ -168489,14 +169240,14 @@ var hhp_preview_exports = {};
168489
169240
  __export(hhp_preview_exports, {
168490
169241
  hhpPreviewResponse: () => hhpPreviewResponse
168491
169242
  });
168492
- import { existsSync as existsSync85, readFileSync as readFileSync95 } from "node:fs";
169243
+ import { existsSync as existsSync86, readFileSync as readFileSync95 } from "node:fs";
168493
169244
  import { join as join90 } from "node:path";
168494
169245
  function hhpPreviewResponse(root, request) {
168495
- if (new URL(request.url).pathname !== "/__himi/hhp-preview" || !existsSync85(join90(root, "hhp.json"))) return null;
169246
+ if (new URL(request.url).pathname !== "/__himi/hhp-preview" || !existsSync86(join90(root, "hhp.json"))) return null;
168496
169247
  if (request.method !== "GET") return new Response(null, { status: 405 });
168497
169248
  try {
168498
169249
  const file2 = localPath(root, ".himi/hhp-preview.json");
168499
- const authored2 = existsSync85(file2) ? JSON.parse(readFileSync95(file2, "utf8")) : {};
169250
+ const authored2 = existsSync86(file2) ? JSON.parse(readFileSync95(file2, "utf8")) : {};
168500
169251
  if (authored2.mockCapabilities && (!Array.isArray(authored2.mockCapabilities) || authored2.mockCapabilities.some((v2) => typeof v2 !== "string"))) throw new Error("mockCapabilities must be a string array.");
168501
169252
  const fixtures = authored2.fixtures ?? {};
168502
169253
  if (typeof fixtures !== "object" || Array.isArray(fixtures) || fixtures === null) throw new Error("fixtures must be an object.");
@@ -171193,7 +171944,7 @@ __export(credential_profile_exports, {
171193
171944
  profilePath: () => profilePath,
171194
171945
  resolveStoreCredentials: () => resolveStoreCredentials
171195
171946
  });
171196
- import { existsSync as existsSync87, readFileSync as readFileSync101 } from "node:fs";
171947
+ import { existsSync as existsSync88, readFileSync as readFileSync101 } from "node:fs";
171197
171948
  import { homedir as homedir11 } from "node:os";
171198
171949
  import { join as join93 } from "node:path";
171199
171950
  function credentialsDir(home = homedir11()) {
@@ -171237,7 +171988,7 @@ function resolveStoreCredentials(opts) {
171237
171988
  const env = opts.env ?? process.env;
171238
171989
  const home = opts.home ?? homedir11();
171239
171990
  const read = opts.readProfile ?? ((p2) => readFileSync101(p2, "utf8"));
171240
- const exists = opts.profileExists ?? existsSync87;
171991
+ const exists = opts.profileExists ?? existsSync88;
171241
171992
  const ios = cfg.ios ?? {};
171242
171993
  const flagName = opts.profileFlag?.trim();
171243
171994
  const named = namedProfile(cfg, opts.profileFlag);
@@ -171303,7 +172054,7 @@ var init_credential_profile = __esm({
171303
172054
 
171304
172055
  // ../store-release/iap-credentials.ts
171305
172056
  import { createHash as createHash45 } from "node:crypto";
171306
- import { existsSync as existsSync88, readFileSync as readFileSync102, realpathSync as realpathSync12 } from "node:fs";
172057
+ import { existsSync as existsSync89, readFileSync as readFileSync102, realpathSync as realpathSync12 } from "node:fs";
171307
172058
  import { homedir as homedir12 } from "node:os";
171308
172059
  function accountFingerprint(keyId, issuerId) {
171309
172060
  return `sha256:${createHash45("sha256").update(`${issuerId}:${keyId}`).digest("hex").slice(0, 16)}`;
@@ -171350,7 +172101,7 @@ function resolveIapCredentials(options) {
171350
172101
  env: {},
171351
172102
  home,
171352
172103
  readProfile: readFile3,
171353
- profileExists: options.exists ?? existsSync88
172104
+ profileExists: options.exists ?? existsSync89
171354
172105
  });
171355
172106
  assertCredentialOutsideRepo(`credential profile "${profile}"`, realpath(profilePath(profile, home)));
171356
172107
  assertCredentialOutsideRepo(`credential profile "${profile}" ascKeyPath`, canonicalKeyPath(creds.ascKeyPath, profile, realpath));
@@ -171855,7 +172606,7 @@ var init_store_spec = __esm({
171855
172606
  });
171856
172607
 
171857
172608
  // src/iap-config-edit.ts
171858
- import { existsSync as existsSync89, readFileSync as readFileSync104 } from "node:fs";
172609
+ import { existsSync as existsSync90, readFileSync as readFileSync104 } from "node:fs";
171859
172610
  import { basename as basename24, join as join94 } from "node:path";
171860
172611
  import ts9 from "typescript";
171861
172612
  function bindPurchasesAppInConfig(source, options) {
@@ -171936,7 +172687,7 @@ function capabilityFinding(app, fileName) {
171936
172687
  function withStoreBundle(dir, options) {
171937
172688
  if (options.storeBundleId) return options;
171938
172689
  const path2 = join94(dir, "store", "store.config.yaml");
171939
- if (!existsSync89(path2)) return options;
172690
+ if (!existsSync90(path2)) return options;
171940
172691
  const storeBundleId = parseStoreConfig(readFileSync104(path2, "utf8")).ios?.bundleId;
171941
172692
  return storeBundleId ? { ...options, storeBundleId } : options;
171942
172693
  }
@@ -172676,7 +173427,7 @@ var init_store_iap_screenshots = __esm({
172676
173427
  });
172677
173428
 
172678
173429
  // src/store-iap-screenshot-render.ts
172679
- import { existsSync as existsSync90, mkdtempSync as mkdtempSync9, readFileSync as readFileSync105, rmSync as rmSync25 } from "node:fs";
173430
+ import { existsSync as existsSync91, mkdtempSync as mkdtempSync9, readFileSync as readFileSync105, rmSync as rmSync25 } from "node:fs";
172680
173431
  import { tmpdir as tmpdir10 } from "node:os";
172681
173432
  import { join as join96 } from "node:path";
172682
173433
  function renderProblems(render, entityId) {
@@ -172739,7 +173490,7 @@ function createReviewScreenRenderer() {
172739
173490
  async capture(html, contentDir2) {
172740
173491
  const { pngPageHtml: pngPageHtml2, stabilizePngPage: stabilizePngPage2 } = await Promise.resolve().then(() => (init_render_descriptor(), render_descriptor_exports));
172741
173492
  const tokensPath = join96(contentDir2, "tokens.json");
172742
- const tokens2 = existsSync90(tokensPath) ? JSON.parse(readFileSync105(tokensPath, "utf8")) : null;
173493
+ const tokens2 = existsSync91(tokensPath) ? JSON.parse(readFileSync105(tokensPath, "utf8")) : null;
172743
173494
  const { width, height, scale } = REVIEW_SCREENSHOT_FRAME;
172744
173495
  const page = await (await (browser ??= launch())).newPage({ viewport: { width, height }, deviceScaleFactor: scale, colorScheme: "light" });
172745
173496
  try {
@@ -172810,166 +173561,6 @@ var init_serve_preview = __esm({
172810
173561
  }
172811
173562
  });
172812
173563
 
172813
- // src/site-analyze/rest.ts
172814
- var rest_exports = {};
172815
- __export(rest_exports, {
172816
- WIX_API: () => WIX_API3,
172817
- WixApiError: () => WixApiError2,
172818
- classifyHttp: () => classifyHttp,
172819
- compactWixstaticUrl: () => compactWixstaticUrl,
172820
- isGenericWixFavicon: () => isGenericWixFavicon,
172821
- makeTransport: () => makeTransport,
172822
- mediaUrlFromLogo: () => mediaUrlFromLogo,
172823
- ownerTransportFrom: () => ownerTransportFrom,
172824
- probeCall: () => probeCall,
172825
- resolveWixUrl: () => resolveWixUrl,
172826
- uploadMediaBytes: () => uploadMediaBytes,
172827
- visitorTransportFrom: () => visitorTransportFrom,
172828
- wixMediaFileId: () => wixMediaFileId
172829
- });
172830
- function resolveWixUrl(path2) {
172831
- if (/^https?:\/\//i.test(path2)) return path2;
172832
- return `${WIX_API3}${path2}`;
172833
- }
172834
- function classifyHttp(status, count2) {
172835
- if (status === 428) return "not-installed";
172836
- if (status === 404) return "wrong-path";
172837
- if (status && status >= 400) return "error";
172838
- return count2 > 0 ? "ok" : "empty";
172839
- }
172840
- function makeTransport(fetchImpl, auth, timeoutMs = DEFAULT_TIMEOUT_MS3) {
172841
- return async (method, path2, body) => {
172842
- const headers2 = { Accept: "application/json" };
172843
- if (auth?.token) headers2.Authorization = auth.token;
172844
- if (auth?.msid) headers2["wix-site-id"] = auth.msid;
172845
- if (body !== void 0) headers2["Content-Type"] = "application/json";
172846
- const res = await fetchImpl(resolveWixUrl(path2), {
172847
- method,
172848
- headers: headers2,
172849
- body: body === void 0 ? void 0 : JSON.stringify(body),
172850
- signal: AbortSignal.timeout(timeoutMs)
172851
- });
172852
- const text11 = await res.text();
172853
- let parsed = text11;
172854
- try {
172855
- parsed = text11 ? JSON.parse(text11) : null;
172856
- } catch {
172857
- }
172858
- if (!res.ok) throw new WixApiError2(res.status, method, path2, parsed);
172859
- return parsed;
172860
- };
172861
- }
172862
- function ownerTransportFrom(fetchImpl, owner) {
172863
- return makeTransport(fetchImpl, { token: owner.token, msid: owner.msid });
172864
- }
172865
- function visitorTransportFrom(fetchImpl, token, msid) {
172866
- return makeTransport(fetchImpl, { token, msid });
172867
- }
172868
- async function probeCall(call, method, path2, body) {
172869
- try {
172870
- return { data: await call(method, path2, body), err: null };
172871
- } catch (e2) {
172872
- if (e2 instanceof WixApiError2) return { data: null, err: e2 };
172873
- throw e2;
172874
- }
172875
- }
172876
- function mediaUrlFromLogo(logo) {
172877
- const s2 = (logo ?? "").trim();
172878
- if (!s2) return null;
172879
- if (/^https?:\/\//i.test(s2)) return s2;
172880
- const id = s2.replace(/^wix:image:\/\/v1\//, "").split("/")[0].split("#")[0];
172881
- if (!id) return null;
172882
- if (/^https?:\/\//i.test(id)) return id;
172883
- return `https://static.wixstatic.com/media/${id}`;
172884
- }
172885
- function isGenericWixFavicon(url) {
172886
- try {
172887
- const h2 = new URL(url).hostname.toLowerCase();
172888
- return h2 === "wix.com" || h2.endsWith(".wix.com");
172889
- } catch {
172890
- return false;
172891
- }
172892
- }
172893
- function wixMediaFileId(url) {
172894
- const m2 = url.match(/static\.wixstatic\.com\/media\/([^/?#]+)/i);
172895
- return m2?.[1] ? decodeURIComponent(m2[1]) : void 0;
172896
- }
172897
- function compactWixstaticUrl(url, maxEdge = 800) {
172898
- if (!/static\.wixstatic\.com\/media\//i.test(url)) return url;
172899
- if (/\.(svg|ico)(\?|#|$)/i.test(url.split("?")[0] ?? url)) return url;
172900
- const w2 = url.match(/(?:\/|,)w_(\d+)/)?.[1];
172901
- if (w2 && Number(w2) <= maxEdge) return url;
172902
- const id = wixMediaFileId(url);
172903
- if (!id) return url;
172904
- return `https://static.wixstatic.com/media/${id}/v1/fill/w_${maxEdge},h_${maxEdge},al_c,q_80,enc_auto/file.jpg`;
172905
- }
172906
- async function uploadMediaBytes(call, fetchImpl, bytes2, fileName, mimeType) {
172907
- const { uploadUrl } = await call(
172908
- "POST",
172909
- "/site-media/v1/files/generate-upload-url",
172910
- { mimeType, fileName: `himi-business-book/${fileName}` }
172911
- );
172912
- const put = await fetchImpl(`${uploadUrl}?filename=${encodeURIComponent(fileName)}`, {
172913
- method: "PUT",
172914
- headers: { "Content-Type": mimeType },
172915
- body: bytes2,
172916
- signal: AbortSignal.timeout(12e4)
172917
- });
172918
- if (!put.ok) throw new Error(`media upload PUT failed: ${put.status}`);
172919
- const payload = await put.json();
172920
- const file2 = payload.file ?? payload;
172921
- const id = file2?.id ?? file2?.fileId;
172922
- const url = file2?.url ?? (id ? `https://static.wixstatic.com/media/${id}` : void 0);
172923
- if (!id || !url) throw new Error("media upload returned no file id");
172924
- return { id, url };
172925
- }
172926
- var WIX_API3, DEFAULT_TIMEOUT_MS3, WixApiError2;
172927
- var init_rest = __esm({
172928
- "src/site-analyze/rest.ts"() {
172929
- WIX_API3 = "https://www.wixapis.com";
172930
- DEFAULT_TIMEOUT_MS3 = 8e3;
172931
- WixApiError2 = class extends Error {
172932
- constructor(status, method, path2, body) {
172933
- super(`${method} ${path2} \u2192 ${status}`);
172934
- this.status = status;
172935
- this.method = method;
172936
- this.path = path2;
172937
- this.body = body;
172938
- this.name = "WixApiError";
172939
- }
172940
- };
172941
- }
172942
- });
172943
-
172944
- // src/site-analyze/visitor-auth.ts
172945
- var visitor_auth_exports = {};
172946
- __export(visitor_auth_exports, {
172947
- mintVisitorToken: () => mintVisitorToken
172948
- });
172949
- async function mintVisitorToken(fetchImpl, clientId) {
172950
- const res = await fetchImpl(`${WIX_API3}/oauth2/token`, {
172951
- method: "POST",
172952
- headers: { "Content-Type": "application/json", Accept: "application/json" },
172953
- body: JSON.stringify({ clientId, grantType: "anonymous" }),
172954
- signal: AbortSignal.timeout(8e3)
172955
- });
172956
- const text11 = await res.text();
172957
- let parsed = text11;
172958
- try {
172959
- parsed = text11 ? JSON.parse(text11) : null;
172960
- } catch {
172961
- }
172962
- if (!res.ok) throw new WixApiError2(res.status, "POST", "/oauth2/token", parsed);
172963
- const token = parsed?.access_token ?? parsed?.accessToken;
172964
- if (!token) throw new Error("visitor mint returned no access_token");
172965
- return token;
172966
- }
172967
- var init_visitor_auth = __esm({
172968
- "src/site-analyze/visitor-auth.ts"() {
172969
- init_rest();
172970
- }
172971
- });
172972
-
172973
173564
  // src/store-iap.ts
172974
173565
  var store_iap_exports = {};
172975
173566
  __export(store_iap_exports, {
@@ -174181,7 +174772,7 @@ __export(preflight_app_exports, {
174181
174772
  profileAccount: () => profileAccount,
174182
174773
  readXcconfigVars: () => readXcconfigVars
174183
174774
  });
174184
- import { readFileSync as readFileSync107, existsSync as existsSync91, readdirSync as readdirSync38, statSync as statSync25 } from "node:fs";
174775
+ import { readFileSync as readFileSync107, existsSync as existsSync92, readdirSync as readdirSync38, statSync as statSync25 } from "node:fs";
174185
174776
  import { createHash as createHash47 } from "node:crypto";
174186
174777
  import { dirname as dirname48, join as join98 } from "node:path";
174187
174778
  function analyze(projectYml, storeConfig, icon, account) {
@@ -174308,7 +174899,7 @@ function readXcconfigVars(iosDir, projectYml) {
174308
174899
  const vars = {};
174309
174900
  const seen = /* @__PURE__ */ new Set();
174310
174901
  const read = (path2) => {
174311
- if (seen.has(path2) || !existsSync91(path2)) return;
174902
+ if (seen.has(path2) || !existsSync92(path2)) return;
174312
174903
  seen.add(path2);
174313
174904
  for (const raw of readFileSync107(path2, "utf8").split("\n")) {
174314
174905
  const inc = /^\s*#include\??\s+"([^"]+)"/.exec(raw);
@@ -174337,8 +174928,8 @@ function profileAccount(app, storeConfig, opts = {}) {
174337
174928
  }
174338
174929
  }
174339
174930
  async function preflightApp(app, opts = {}) {
174340
- const projectYml = existsSync91(iosProjectYml(app)) ? readFileSync107(iosProjectYml(app), "utf8") : "";
174341
- const storeConfig = existsSync91(storeConfigPath(app)) ? readFileSync107(storeConfigPath(app), "utf8") : "";
174931
+ const projectYml = existsSync92(iosProjectYml(app)) ? readFileSync107(iosProjectYml(app), "utf8") : "";
174932
+ const storeConfig = existsSync92(storeConfigPath(app)) ? readFileSync107(storeConfigPath(app), "utf8") : "";
174342
174933
  const identity = projectYml ? analyzeStoreIdentity(projectYml, storeConfig, readXcconfigVars(join98(appDir(app), "ios"), projectYml)) : [];
174343
174934
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }, ...identity];
174344
174935
  const findings = analyze(
@@ -174376,15 +174967,15 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
174376
174967
  function appLevelMetadataFiles(app, locale = "en-US") {
174377
174968
  const root = join98(storeDir(app), "metadata");
174378
174969
  const out2 = /* @__PURE__ */ new Set();
174379
- if (existsSync91(root)) {
174970
+ if (existsSync92(root)) {
174380
174971
  for (const n2 of readdirSync38(root)) if (n2.endsWith(".txt")) out2.add(n2);
174381
174972
  }
174382
- if (existsSync91(join98(root, locale, "privacy_url.txt"))) out2.add("privacy_url.txt");
174973
+ if (existsSync92(join98(root, locale, "privacy_url.txt"))) out2.add("privacy_url.txt");
174383
174974
  return out2;
174384
174975
  }
174385
174976
  function reviewInfoFiles(app) {
174386
174977
  const dir = join98(storeDir(app), "review_information");
174387
- if (!existsSync91(dir)) return /* @__PURE__ */ new Set();
174978
+ if (!existsSync92(dir)) return /* @__PURE__ */ new Set();
174388
174979
  return new Set(readdirSync38(dir).filter((n2) => n2.endsWith(".txt")));
174389
174980
  }
174390
174981
  function isValidAppStoreSize(w2, h2) {
@@ -174531,20 +175122,20 @@ function analyzeListing(metaFiles, shots) {
174531
175122
  }
174532
175123
  function metadataFilesPresent(app, locale = "en-US") {
174533
175124
  const dir = join98(storeDir(app), "metadata", locale);
174534
- if (!existsSync91(dir)) return /* @__PURE__ */ new Set();
175125
+ if (!existsSync92(dir)) return /* @__PURE__ */ new Set();
174535
175126
  return new Set(readdirSync38(dir).filter((n2) => n2.endsWith(".txt")));
174536
175127
  }
174537
175128
  function listScreenshots(app, locale = "en-US") {
174538
175129
  const out2 = [];
174539
175130
  const flat = join98(storeDir(app), "screenshots", locale);
174540
- if (existsSync91(flat)) {
175131
+ if (existsSync92(flat)) {
174541
175132
  for (const png of readdirSync38(flat).filter((n2) => n2.endsWith(".png"))) {
174542
175133
  const sz = pngSize2(readFileSync107(join98(flat, png)));
174543
175134
  if (sz) out2.push({ name: png, w: sz.w, h: sz.h });
174544
175135
  }
174545
175136
  }
174546
175137
  const legacy = join98(storeDir(app), "metadata", locale, "screenshots");
174547
- if (existsSync91(legacy)) {
175138
+ if (existsSync92(legacy)) {
174548
175139
  for (const deviceDir of readdirSync38(legacy)) {
174549
175140
  const d2 = join98(legacy, deviceDir);
174550
175141
  if (!statSync25(d2).isDirectory()) continue;
@@ -174558,7 +175149,7 @@ function listScreenshots(app, locale = "en-US") {
174558
175149
  }
174559
175150
  function placeholderHashes2() {
174560
175151
  const templateIos = join98(appDir("_template"), "ios");
174561
- if (!existsSync91(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
175152
+ if (!existsSync92(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
174562
175153
  for (const target of readdirSync38(templateIos)) {
174563
175154
  const p2 = join98(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
174564
175155
  try {
@@ -174594,7 +175185,7 @@ async function pixelIssues(bytes2) {
174594
175185
  }
174595
175186
  }
174596
175187
  async function appIconState(iosDir) {
174597
- if (!existsSync91(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
175188
+ if (!existsSync92(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
174598
175189
  for (const target of readdirSync38(iosDir)) {
174599
175190
  const p2 = join98(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
174600
175191
  try {
@@ -174730,6 +175321,19 @@ var init_asc_status = __esm({
174730
175321
  }
174731
175322
  });
174732
175323
 
175324
+ // ../../core/serve-control/src/captcha-bindable.ts
175325
+ var captcha_bindable_exports = {};
175326
+ __export(captcha_bindable_exports, {
175327
+ captchaBindable: () => captchaBindable
175328
+ });
175329
+ function captchaBindable(shell, externalContract) {
175330
+ return !externalContract && ["branded-lite", "owner-lite"].includes(shell);
175331
+ }
175332
+ var init_captcha_bindable = __esm({
175333
+ "../../core/serve-control/src/captcha-bindable.ts"() {
175334
+ }
175335
+ });
175336
+
174733
175337
  // src/browser-login.ts
174734
175338
  import { createServer as createServer7 } from "node:http";
174735
175339
  import { randomBytes as randomBytes6 } from "node:crypto";
@@ -175661,7 +176265,7 @@ function headers(session) {
175661
176265
  ...session ? { "X-Admin-Session": session } : {}
175662
176266
  };
175663
176267
  }
175664
- function truncate2(token) {
176268
+ function truncate3(token) {
175665
176269
  return token.length <= 20 ? token : `${token.slice(0, 8)}\u2026${token.slice(-8)}`;
175666
176270
  }
175667
176271
  function parseDataFlag(raw) {
@@ -175787,7 +176391,7 @@ async function runNotify(pos, flags, io2, deps = {}) {
175787
176391
  const bits = [
175788
176392
  String(t2.id).padStart(4),
175789
176393
  t2.platform.padEnd(8),
175790
- truncate2(t2.token).padEnd(19),
176394
+ truncate3(t2.token).padEnd(19),
175791
176395
  (t2.environment ?? "-").padEnd(11),
175792
176396
  (t2.appVersion ?? "-").padEnd(10),
175793
176397
  t2.lastSeen,
@@ -175999,7 +176603,7 @@ __export(base44_adopt_exports, {
175999
176603
  renderAdoptModule: () => renderAdoptModule,
176000
176604
  runBase44Adopt: () => runBase44Adopt
176001
176605
  });
176002
- import { existsSync as existsSync95, readFileSync as readFileSync114, writeFileSync as writeFileSync38 } from "node:fs";
176606
+ import { existsSync as existsSync96, readFileSync as readFileSync114, writeFileSync as writeFileSync38 } from "node:fs";
176003
176607
  import { join as join104, resolve as resolve74 } from "node:path";
176004
176608
  function prop(entity, key3) {
176005
176609
  const props = entity.schema?.properties;
@@ -176108,7 +176712,7 @@ function runBase44Adopt(opts) {
176108
176712
  const issues = validateBase44Entities({ entities: result.entities, functions: result.functions }, opts.app);
176109
176713
  if (opts.write === void 0 || opts.write === false || issues.some((i2) => i2.severity === "error")) return { result, module, issues };
176110
176714
  const target = typeof opts.write === "string" ? resolve74(opts.cwd, opts.write) : join104(opts.cwd, ADOPT_MODULE_FILENAME);
176111
- if (existsSync95(target)) {
176715
+ if (existsSync96(target)) {
176112
176716
  throw new AdoptError("adopt_target_exists", `${target} already exists \u2014 adopt never overwrites. Delete or rename it, or pass --write <other path>.`, 1, { path: target });
176113
176717
  }
176114
176718
  writeFileSync38(target, module);
@@ -176357,7 +176961,7 @@ __export(generate_exports, {
176357
176961
  writeGenerated: () => writeGenerated
176358
176962
  });
176359
176963
  import { createHash as createHash50 } from "node:crypto";
176360
- import { existsSync as existsSync96, mkdirSync as mkdirSync42, readdirSync as readdirSync39, readFileSync as readFileSync115, writeFileSync as writeFileSync39 } from "node:fs";
176964
+ import { existsSync as existsSync97, mkdirSync as mkdirSync42, readdirSync as readdirSync39, readFileSync as readFileSync115, writeFileSync as writeFileSync39 } from "node:fs";
176361
176965
  import { dirname as dirname51, join as join105 } from "node:path";
176362
176966
  function withHeader(body) {
176363
176967
  return `${GENERATED_HEADER}${sha2566(body)}
@@ -176484,7 +177088,7 @@ function writeGenerated(dir, files, opts = {}) {
176484
177088
  const abs = join105(dir, f2.path);
176485
177089
  const next = withHeader(f2.body);
176486
177090
  let outcome;
176487
- if (!existsSync96(abs)) outcome = "written";
177091
+ if (!existsSync97(abs)) outcome = "written";
176488
177092
  else {
176489
177093
  const cur = readFileSync115(abs, "utf8");
176490
177094
  if (cur === next) outcome = "unchanged";
@@ -176503,14 +177107,14 @@ function orphanedFiles(dir, current) {
176503
177107
  const keep = new Set(current.map((f2) => f2.path));
176504
177108
  const found = [];
176505
177109
  const t5 = join105(dir, "tier5-src");
176506
- if (!existsSync96(t5)) return found;
177110
+ if (!existsSync97(t5)) return found;
176507
177111
  const candidates = readdirSync39(t5).filter((d2) => d2.startsWith("b44-")).map((d2) => `tier5-src/${d2}/index.ts`);
176508
177112
  const shared = join105(t5, "_shared");
176509
- if (existsSync96(shared)) {
177113
+ if (existsSync97(shared)) {
176510
177114
  for (const f2 of readdirSync39(shared)) if (/^b44-.*-plan\.ts$/.test(f2)) candidates.push(`tier5-src/_shared/${f2}`);
176511
177115
  }
176512
177116
  for (const rel of candidates.sort()) {
176513
- if (keep.has(rel) || !existsSync96(join105(dir, rel))) continue;
177117
+ if (keep.has(rel) || !existsSync97(join105(dir, rel))) continue;
176514
177118
  if (readFileSync115(join105(dir, rel), "utf8").startsWith(GENERATED_HEADER)) found.push(rel);
176515
177119
  }
176516
177120
  return found;
@@ -176549,7 +177153,7 @@ __export(base44_auth_exports, {
176549
177153
  usableBase44Auth: () => usableBase44Auth,
176550
177154
  writeBase44Session: () => writeBase44Session
176551
177155
  });
176552
- import { chmodSync as chmodSync4, existsSync as existsSync97, mkdirSync as mkdirSync43, readFileSync as readFileSync116, rmSync as rmSync27, writeFileSync as writeFileSync40 } from "node:fs";
177156
+ import { chmodSync as chmodSync4, existsSync as existsSync98, mkdirSync as mkdirSync43, readFileSync as readFileSync116, rmSync as rmSync27, writeFileSync as writeFileSync40 } from "node:fs";
176553
177157
  import { homedir as homedir15 } from "node:os";
176554
177158
  import { dirname as dirname52, join as join106 } from "node:path";
176555
177159
  function base44ApiBase(env = process.env) {
@@ -176560,7 +177164,7 @@ function base44SessionPath(home = homedir15()) {
176560
177164
  }
176561
177165
  function readBase44Session(home) {
176562
177166
  const p2 = base44SessionPath(home);
176563
- if (!existsSync97(p2)) return void 0;
177167
+ if (!existsSync98(p2)) return void 0;
176564
177168
  try {
176565
177169
  const s2 = JSON.parse(readFileSync116(p2, "utf8"));
176566
177170
  return typeof s2.accessToken === "string" && s2.accessToken ? s2 : void 0;
@@ -176578,7 +177182,7 @@ function writeBase44Session(session, home) {
176578
177182
  }
176579
177183
  function deleteBase44Session(home) {
176580
177184
  const p2 = base44SessionPath(home);
176581
- if (!existsSync97(p2)) return false;
177185
+ if (!existsSync98(p2)) return false;
176582
177186
  rmSync27(p2, { force: true });
176583
177187
  return true;
176584
177188
  }
@@ -176681,7 +177285,7 @@ function hasBase44WriteScope(auth) {
176681
177285
  }
176682
177286
  function importBase44CliSession(home = homedir15(), env = process.env) {
176683
177287
  const src = join106(home, ".base44", "auth", "auth.json");
176684
- if (!existsSync97(src)) throw new Error(`no Base44 CLI session at ${src} \u2014 run \`npx base44 login\` first, or use \`himi base44 login\``);
177288
+ if (!existsSync98(src)) throw new Error(`no Base44 CLI session at ${src} \u2014 run \`npx base44 login\` first, or use \`himi base44 login\``);
176685
177289
  const a2 = JSON.parse(readFileSync116(src, "utf8"));
176686
177290
  if (typeof a2.accessToken !== "string" || !a2.accessToken) throw new Error(`${src} holds no access token`);
176687
177291
  const session = {
@@ -176779,11 +177383,11 @@ __export(seed_exports, {
176779
177383
  seedBase44Entities: () => seedBase44Entities,
176780
177384
  writeBase44SeedRows: () => writeBase44SeedRows
176781
177385
  });
176782
- import { existsSync as existsSync98, readFileSync as readFileSync117, writeFileSync as writeFileSync41 } from "node:fs";
177386
+ import { existsSync as existsSync99, readFileSync as readFileSync117, writeFileSync as writeFileSync41 } from "node:fs";
176783
177387
  import { join as join107 } from "node:path";
176784
177388
  function readBase44SeedFile(dir) {
176785
177389
  const file2 = join107(dir, BASE44_SEED_FILE);
176786
- if (!existsSync98(file2)) return {};
177390
+ if (!existsSync99(file2)) return {};
176787
177391
  let parsed;
176788
177392
  try {
176789
177393
  parsed = JSON.parse(readFileSync117(file2, "utf8"));
@@ -176943,7 +177547,7 @@ function writeBase44SeedRows(opts) {
176943
177547
  return { ok: false, path: path2, entities: {}, agentPrompt: "", next: [], note: opts.only ? `--only ${opts.only.join(",")} matched no owned entity` : `no owned Base44 entities declared \u2014 add one with \`name\` (physically ${base44Prefix(app)}<Name>). Adopted entities are never seeded.` };
176944
177548
  }
176945
177549
  let existing = {};
176946
- if (existsSync98(path2)) {
177550
+ if (existsSync99(path2)) {
176947
177551
  let parsed;
176948
177552
  try {
176949
177553
  parsed = JSON.parse(readFileSync117(path2, "utf8"));
@@ -177298,7 +177902,7 @@ __export(functions_exports2, {
177298
177902
  markerContent: () => markerContent,
177299
177903
  readFunctionSource: () => readFunctionSource
177300
177904
  });
177301
- import { existsSync as existsSync99, lstatSync as lstatSync22 } from "node:fs";
177905
+ import { existsSync as existsSync100, lstatSync as lstatSync22 } from "node:fs";
177302
177906
  import { join as join108 } from "node:path";
177303
177907
  function markerContent(app) {
177304
177908
  return `${JSON.stringify({ app, by: "himi" })}
@@ -177307,7 +177911,7 @@ function markerContent(app) {
177307
177911
  function readFunctionSource(appDir3, name, app) {
177308
177912
  const rel = `base44/functions/${name}/`;
177309
177913
  const folder = join108(appDir3, "base44", "functions", name);
177310
- if (!existsSync99(folder) || !lstatSync22(folder).isDirectory()) throw new FunctionSourceError("function_source_missing", `no source folder at ${rel}`);
177914
+ if (!existsSync100(folder) || !lstatSync22(folder).isDirectory()) throw new FunctionSourceError("function_source_missing", `no source folder at ${rel}`);
177311
177915
  const { entries, skipped } = collectAppSource(folder);
177312
177916
  if (skipped.length) throw new FunctionSourceError("function_source_unsafe", `${rel} holds paths himi will not upload (symlinks or denied names): ${skipped.join(", ")} \u2014 remove them`);
177313
177917
  const secrets = secretFindings(entries);
@@ -177462,9 +178066,9 @@ function propsOf2(schema) {
177462
178066
  function isOwnedPhysical(name, app) {
177463
178067
  return name.startsWith(base44Prefix(app));
177464
178068
  }
177465
- function foreignGuard(snapshot2, next, owned) {
178069
+ function foreignGuard(snapshot3, next, owned) {
177466
178070
  const out2 = [];
177467
- for (const e2 of snapshot2) {
178071
+ for (const e2 of snapshot3) {
177468
178072
  if (owned.has(e2.name)) continue;
177469
178073
  if (!Object.prototype.hasOwnProperty.call(next, e2.name)) {
177470
178074
  out2.push({ code: "would_delete_foreign_entity", entity: e2.name, detail: `"${e2.name}" is not himi's and would be left out of the full-set sync \u2014 Base44 would delete it and its rows` });
@@ -177501,20 +178105,20 @@ function inheritUndeclaredPermissions(desired, remote, spec) {
177501
178105
  if (f2.rls === void 0 && remoteProps[f2.key]?.rls !== void 0) desiredProps[f2.key].rls = remoteProps[f2.key].rls;
177502
178106
  }
177503
178107
  }
177504
- function planEntitySync(declared, snapshot2, opts) {
178108
+ function planEntitySync(declared, snapshot3, opts) {
177505
178109
  const { app, allowDestructive = false, applyPermissions = false, emptyAppOk = false } = opts;
177506
178110
  const plan = { next: {}, actions: [], refusals: [], warnings: [], owned: [] };
177507
- if (snapshot2.length === 0 && !emptyAppOk) {
178111
+ if (snapshot3.length === 0 && !emptyAppOk) {
177508
178112
  plan.refusals.push({ code: "schema_snapshot_empty", detail: "the project download holds no entity schemas \u2014 if the Base44 app really has no entities, pass --empty-app-ok" });
177509
178113
  }
177510
178114
  const specs = declared.filter((s2) => !!s2 && typeof s2 === "object");
177511
178115
  const owned = new Map(specs.filter(isOwnedEntity).map((s2) => [physicalEntityName(s2, app), s2]));
177512
178116
  const adopted = new Set(specs.filter(isAdoptedEntity).map((s2) => physicalEntityName(s2, app)));
177513
- const live3 = new Map(snapshot2.map((e2) => [e2.name, e2]));
178117
+ const live3 = new Map(snapshot3.map((e2) => [e2.name, e2]));
177514
178118
  const provisioned = new Set((opts.provisioned ?? []).filter((n2) => isOwnedPhysical(n2, app)));
177515
- const ownedHere = /* @__PURE__ */ new Set([...snapshot2.map((e2) => e2.name).filter((n2) => provisioned.has(n2)), ...[...owned.keys()].filter((n2) => !live3.has(n2))]);
178119
+ const ownedHere = /* @__PURE__ */ new Set([...snapshot3.map((e2) => e2.name).filter((n2) => provisioned.has(n2)), ...[...owned.keys()].filter((n2) => !live3.has(n2))]);
177516
178120
  plan.owned = [...ownedHere].sort();
177517
- for (const e2 of snapshot2) {
178121
+ for (const e2 of snapshot3) {
177518
178122
  if (!ownedHere.has(e2.name)) {
177519
178123
  plan.next[e2.name] = e2.schema;
177520
178124
  if (owned.has(e2.name)) {
@@ -177578,7 +178182,7 @@ function planEntitySync(declared, snapshot2, opts) {
177578
178182
  for (const name of adopted) {
177579
178183
  if (!live3.has(name)) plan.warnings.push(`adopted entity "${name}" is not on the live app \u2014 himi never creates an entity it does not own`);
177580
178184
  }
177581
- plan.refusals.push(...foreignGuard(snapshot2, plan.next, ownedHere));
178185
+ plan.refusals.push(...foreignGuard(snapshot3, plan.next, ownedHere));
177582
178186
  return plan;
177583
178187
  }
177584
178188
  var BUILTINS2;
@@ -177645,12 +178249,12 @@ __export(apply_exports, {
177645
178249
  createBase44Entities: () => createBase44Entities,
177646
178250
  fileLedger: () => fileLedger
177647
178251
  });
177648
- import { existsSync as existsSync100, readFileSync as readFileSync118, writeFileSync as writeFileSync42 } from "node:fs";
178252
+ import { existsSync as existsSync101, readFileSync as readFileSync118, writeFileSync as writeFileSync42 } from "node:fs";
177649
178253
  import { join as join109 } from "node:path";
177650
178254
  function fileLedger(dir, appId2) {
177651
178255
  const path2 = join109(dir, LEDGER_FILE2);
177652
178256
  const load3 = () => {
177653
- if (!existsSync100(path2)) return {};
178257
+ if (!existsSync101(path2)) return {};
177654
178258
  let v2;
177655
178259
  try {
177656
178260
  v2 = JSON.parse(readFileSync118(path2, "utf8"));
@@ -180695,7 +181299,7 @@ var init_analyze = __esm({
180695
181299
  });
180696
181300
 
180697
181301
  // src/base44-analyze/write-artifacts.ts
180698
- import { existsSync as existsSync101, mkdirSync as mkdirSync45, readFileSync as readFileSync119, rmSync as rmSync29, writeFileSync as writeFileSync44 } from "node:fs";
181302
+ import { existsSync as existsSync102, mkdirSync as mkdirSync45, readFileSync as readFileSync119, rmSync as rmSync29, writeFileSync as writeFileSync44 } from "node:fs";
180699
181303
  import { dirname as dirname53, join as join111, resolve as resolve75, sep as sep20 } from "node:path";
180700
181304
  function isBookFolder(dir) {
180701
181305
  try {
@@ -180786,7 +181390,7 @@ function writeBase44Book(result, outDir, opts = {}) {
180786
181390
  const md = renderBookMd(book);
180787
181391
  assertNoSecrets(json8);
180788
181392
  assertNoSecrets(md);
180789
- const owned = ["book.json", "BOOK.md", "source", "screenshots"].filter((n2) => existsSync101(join111(out2, n2)));
181393
+ const owned = ["book.json", "BOOK.md", "source", "screenshots"].filter((n2) => existsSync102(join111(out2, n2)));
180790
181394
  if (owned.length) {
180791
181395
  if (!isBookFolder(out2)) throw new Error(`refusing to write the app book into ${out2}: it already has ${owned.join(", ")} that ${owned.length === 1 ? "is" : "are"} not from an app book \u2014 pick an empty --out`);
180792
181396
  for (const d2 of ["source", "screenshots"]) rmSync29(join111(out2, d2), { recursive: true, force: true });
@@ -184675,12 +185279,19 @@ async function collectOneApp(input, deps = {}) {
184675
185279
  const fetchImpl = deps.fetchImpl ?? fetch;
184676
185280
  const maxBytes = boundedSetting(deps.maxResponseBytes, MAX_RESPONSE_BYTES);
184677
185281
  const timeoutMs = boundedSetting(deps.timeoutMs, TIMEOUT_MS3);
185282
+ const credentials = /* @__PURE__ */ new Set([input.authorization]);
185283
+ let internalBusinessRead = false;
184678
185284
  function redact(value2) {
185285
+ const secrets = [...credentials].sort((a2, b2) => b2.length - a2.length);
185286
+ const redactString = (value3) => {
185287
+ for (const credential of secrets) value3 = value3.split(credential).join("[REDACTED]");
185288
+ return value3;
185289
+ };
184679
185290
  const visit = (item) => {
184680
- if (typeof item === "string") return item.split(input.authorization).join("[REDACTED]");
185291
+ if (typeof item === "string") return redactString(item);
184681
185292
  if (Array.isArray(item)) return item.map(visit);
184682
185293
  if (isObject2(item)) return Object.fromEntries(Object.entries(item).map(([key3, child]) => [
184683
- key3.split(input.authorization).join("[REDACTED]"),
185294
+ redactString(key3),
184684
185295
  visit(child)
184685
185296
  ]));
184686
185297
  return item;
@@ -184695,19 +185306,23 @@ async function collectOneApp(input, deps = {}) {
184695
185306
  throw new OneAppError("artifact_write_failed", "Could not save the collected source evidence.", { stage });
184696
185307
  }
184697
185308
  }
184698
- async function request(url, stage, body, platform, instance) {
185309
+ async function request(url, stage, body, context = {}) {
185310
+ const { platform, instance } = context;
184699
185311
  const controller = new AbortController();
184700
185312
  let timedOut = false;
184701
185313
  let timer;
184702
185314
  const headers2 = {
184703
- Authorization: input.authorization,
184704
- "wix-site-id": input.metaSiteId,
185315
+ Authorization: context.authorization ?? input.authorization,
184705
185316
  Accept: "application/json"
184706
185317
  };
185318
+ if (context.siteScoped !== false) headers2["wix-site-id"] = input.metaSiteId;
184707
185319
  if (body !== void 0) headers2["Content-Type"] = "application/json";
185320
+ if (instance && !platform) {
185321
+ headers2["x-wix-mobile-app-version"] = `${sourceApp(instance)}/1.0.0`;
185322
+ }
184708
185323
  if (platform) {
184709
185324
  headers2.isAndroid = String(platform === "android");
184710
- headers2.isVisitor = "false";
185325
+ headers2.isVisitor = String(context.visitor ?? false);
184711
185326
  headers2.os = platform;
184712
185327
  headers2.mobile_app_type = instance === "branded" ? "branded" : "places";
184713
185328
  }
@@ -184780,6 +185395,38 @@ async function collectOneApp(input, deps = {}) {
184780
185395
  clearTimeout(timer);
184781
185396
  }
184782
185397
  }
185398
+ async function readBusiness(url, instance) {
185399
+ try {
185400
+ return await request(url.href, "business");
185401
+ } catch (error) {
185402
+ if (!input.allowInternalBusinessRead || !(error instanceof OneAppError) || error.code !== "source_forbidden") throw error;
185403
+ try {
185404
+ const info4 = await request(TOKEN_INFO, "business_authorization", { token: input.authorization });
185405
+ if (!isObject2(info4) || info4.active !== true || info4.subjectType !== "USER" || typeof info4.subjectId !== "string" || !info4.subjectId.trim() || typeof info4.siteId !== "string" || info4.siteId.toLowerCase() !== input.metaSiteId.toLowerCase()) throw error;
185406
+ const contexts = [
185407
+ { organizationContext: {} },
185408
+ { siteContext: { metasiteId: input.metaSiteId } }
185409
+ ];
185410
+ for (const resourceContext of contexts) {
185411
+ const permission = await request(IS_PERMITTED, "business_authorization", { permission: INTERNAL_PERMISSION, resourceContext });
185412
+ if (!isObject2(permission) || permission.isPermitted !== true) throw error;
185413
+ }
185414
+ } catch {
185415
+ throw error;
185416
+ }
185417
+ const visitorUrl = new URL(url);
185418
+ visitorUrl.pathname = visitorUrl.pathname.replace("/user/businesses/", "/visitor/businesses/");
185419
+ internalBusinessRead = true;
185420
+ return request(visitorUrl.href, "business", void 0, { instance });
185421
+ }
185422
+ }
185423
+ async function enrichmentAuthorization() {
185424
+ if (!internalBusinessRead) return input.authorization;
185425
+ const session = await request(`${VISITOR_SESSION}${input.metaSiteId}/signed-instance?signed_instance_type=Visitor`, "enrichment_session", void 0, { siteScoped: false });
185426
+ if (!isObject2(session) || session.signedInstanceType !== "Visitor" || typeof session.signedInstance !== "string" || !session.signedInstance.trim() || /[\r\n]/.test(session.signedInstance) || typeof session.validUntil !== "string" || !Number.isFinite(Date.parse(session.validUntil)) || Date.parse(session.validUntil) <= (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime()) throw invalidResponse("enrichment_session");
185427
+ credentials.add(session.signedInstance);
185428
+ return session.signedInstance;
185429
+ }
184783
185430
  const rawUrl = structureUrl("published-structure", input.metaSiteId, requested);
184784
185431
  const originalRaw = validateStructure(await request(rawUrl, "published", {}), "published");
184785
185432
  const baseline = semanticFingerprint(originalRaw);
@@ -184791,7 +185438,7 @@ async function collectOneApp(input, deps = {}) {
184791
185438
  await progress("raw-structure", rawStructure);
184792
185439
  const businessUrl = new URL(`${BUSINESS}${input.metaSiteId}`);
184793
185440
  businessUrl.searchParams.set("forApp", sourceApp(resolved2));
184794
- const projected = businessProjection(await request(businessUrl.href, "business"), input.metaSiteId);
185441
+ const projected = businessProjection(await readBusiness(businessUrl, resolved2), input.metaSiteId);
184795
185442
  const businessContext = redact(projected.context);
184796
185443
  await progress("business-context", businessContext);
184797
185444
  const enrichBody = {
@@ -184800,8 +185447,14 @@ async function collectOneApp(input, deps = {}) {
184800
185447
  ...projected.title ? { businessTitle: redact(projected.title) } : {}
184801
185448
  };
184802
185449
  const enrichUrl = structureUrl("v2/structure-enriched", input.metaSiteId, resolved2);
185450
+ const authorization = await enrichmentAuthorization();
184803
185451
  const enrich = async (platform) => {
184804
- const structure = validateStructure(await request(enrichUrl, `enriched_${platform}`, enrichBody, platform, resolved2), `enriched_${platform}`, true);
185452
+ const structure = validateStructure(await request(enrichUrl, `enriched_${platform}`, enrichBody, {
185453
+ platform,
185454
+ instance: resolved2,
185455
+ authorization,
185456
+ visitor: internalBusinessRead
185457
+ }), `enriched_${platform}`, true);
184805
185458
  if (structure.appInstance !== rawStructure.appInstance) {
184806
185459
  throw new OneAppError("source_changed", "The source app changed during collection. Run the command again.", { stage: `enriched_${platform}` });
184807
185460
  }
@@ -184813,11 +185466,11 @@ async function collectOneApp(input, deps = {}) {
184813
185466
  const enriched = {};
184814
185467
  const contextWarnings = [];
184815
185468
  for (const [index, platform] of ["ios", "android"].entries()) {
184816
- const snapshot2 = snapshots[index];
184817
- if (snapshot2.status === "fulfilled") {
184818
- enriched[platform] = snapshot2.value;
185469
+ const snapshot3 = snapshots[index];
185470
+ if (snapshot3.status === "fulfilled") {
185471
+ enriched[platform] = snapshot3.value;
184819
185472
  } else {
184820
- const error = snapshot2.reason;
185473
+ const error = snapshot3.reason;
184821
185474
  if (!(error instanceof OneAppError) || ![
184822
185475
  "source_not_found",
184823
185476
  "source_request_failed",
@@ -184845,24 +185498,28 @@ async function collectOneApp(input, deps = {}) {
184845
185498
  sourceApp: sourceApp(resolved2),
184846
185499
  fallback: requested !== resolved2,
184847
185500
  fetchedAt: (deps.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
184848
- context: { locale: "en", shell: resolved2 === "branded" ? "branded" : "places", platforms: ["ios", "android"], identity: "user", productionRules: "not-collected" }
185501
+ context: { locale: "en", shell: resolved2 === "branded" ? "branded" : "places", platforms: ["ios", "android"], identity: internalBusinessRead ? "visitor" : "user", productionRules: "not-collected" }
184849
185502
  },
184850
185503
  rawStructure,
184851
185504
  enriched,
184852
185505
  businessContext,
184853
185506
  warnings: [
184854
185507
  { code: "runtime_rules_unavailable", message: "Engine production rules were not collected. Empty rule input does not establish that the source has no restrictions." },
184855
- { code: "viewer_context", message: "Enrichment shows English iOS and Android views for a Wix user. Preserve authored raw access rules; this view does not establish visitor or member access." },
185508
+ { code: "viewer_context", message: internalBusinessRead ? "Enrichment shows English iOS and Android visitor views. Preserve authored raw access rules; this view does not establish member or manager access." : "Enrichment shows English iOS and Android views for a Wix user. Preserve authored raw access rules; this view does not establish visitor or member access." },
184856
185509
  ...contextWarnings
184857
185510
  ]
184858
185511
  };
184859
185512
  }
184860
- var MIDDLEWARE, BUSINESS, MAX_RESPONSE_BYTES, TIMEOUT_MS3, UUID, LIFECYCLE_FIELDS;
185513
+ var MIDDLEWARE, BUSINESS, TOKEN_INFO, IS_PERMITTED, INTERNAL_PERMISSION, VISITOR_SESSION, MAX_RESPONSE_BYTES, TIMEOUT_MS3, UUID, LIFECYCLE_FIELDS;
184861
185514
  var init_collect = __esm({
184862
185515
  "src/one-app/collect.ts"() {
184863
185516
  init_errors2();
184864
185517
  MIDDLEWARE = "https://manage.wix.com/_serverless/mobile-apps-editor-middleware";
184865
185518
  BUSINESS = "https://apps.wix.com/_api/app-state/v1/user/businesses/";
185519
+ TOKEN_INFO = "https://www.wixapis.com/oauth2/token-info";
185520
+ IS_PERMITTED = "https://www.wixapis.com/authorization-server/v1/authorization/is_permitted";
185521
+ INTERNAL_PERMISSION = "BRANDED_APPS.MANAGE_INTERNAL";
185522
+ VISITOR_SESSION = "https://apps.wix.com/_api/one-app-session-web/v3/businesses/";
184866
185523
  MAX_RESPONSE_BYTES = 8e6;
184867
185524
  TIMEOUT_MS3 = 3e4;
184868
185525
  UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -185222,7 +185879,8 @@ validate-topbar checks authored source top-bar controls against the migrated con
185222
185879
  const collected = await (deps.collect ?? collectOneApp)({
185223
185880
  metaSiteId: options.metaSiteId,
185224
185881
  appInstance: options.appInstance,
185225
- authorization: token
185882
+ authorization: token,
185883
+ allowInternalBusinessRead: true
185226
185884
  }, {
185227
185885
  fetchImpl: deps.fetchImpl,
185228
185886
  onProgress: (completedStage, value2) => {
@@ -187293,7 +187951,7 @@ var init_customer_build = __esm({
187293
187951
  });
187294
187952
 
187295
187953
  // src/site-analyze/apply.ts
187296
- import { existsSync as existsSync103, readFileSync as readFileSync121, writeFileSync as writeFileSync47, copyFileSync as copyFileSync2, mkdirSync as mkdirSync48, realpathSync as realpathSync15, statSync as statSync27 } from "node:fs";
187954
+ import { existsSync as existsSync104, readFileSync as readFileSync121, writeFileSync as writeFileSync47, copyFileSync as copyFileSync2, mkdirSync as mkdirSync48, realpathSync as realpathSync15, statSync as statSync27 } from "node:fs";
187297
187955
  import { dirname as dirname55, isAbsolute as isAbsolute9, join as join115, resolve as resolve79, sep as sep21 } from "node:path";
187298
187956
  import { spawnSync as spawnSync6 } from "node:child_process";
187299
187957
  function unpairedRedirectNote(overlay) {
@@ -187309,7 +187967,7 @@ function committedShellProvisioning(path2) {
187309
187967
  function findRepoRoot(cwd) {
187310
187968
  let dir = cwd;
187311
187969
  for (let i2 = 0; i2 < 8; i2++) {
187312
- if (SHELL_NAMES.some((name) => existsSync103(join115(dir, shellAppDir(name), "package.json")))) return dir;
187970
+ if (SHELL_NAMES.some((name) => existsSync104(join115(dir, shellAppDir(name), "package.json")))) return dir;
187313
187971
  const next = dirname55(dir);
187314
187972
  if (next === dir) break;
187315
187973
  dir = next;
@@ -187413,7 +188071,7 @@ function sameKnownSite(previous, next) {
187413
188071
  function buildMergedContract(options) {
187414
188072
  const { appDir: appDir3, packageDir, overlay } = options;
187415
188073
  const committedPath = join115(appDir3, "provisioning.json");
187416
- if (!existsSync103(committedPath)) {
188074
+ if (!existsSync104(committedPath)) {
187417
188075
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
187418
188076
  }
187419
188077
  const committed = JSON.parse(readFileSync121(committedPath, "utf8"));
@@ -187429,7 +188087,7 @@ function buildMergedContract(options) {
187429
188087
  const stage = (fromRel, toRel) => {
187430
188088
  const from = contained(packageDir, fromRel);
187431
188089
  const to2 = contained(join115(appDir3, STAGED), toRel.slice(STAGED.length + 1));
187432
- if (!from || !to2 || !existsSync103(from)) return void 0;
188090
+ if (!from || !to2 || !existsSync104(from)) return void 0;
187433
188091
  let real;
187434
188092
  let realRoot;
187435
188093
  try {
@@ -187452,7 +188110,7 @@ function buildMergedContract(options) {
187452
188110
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
187453
188111
  if (icon) branding.appIconPath = icon;
187454
188112
  const facesPath = join115(packageDir, "media/fonts/faces.json");
187455
- if (existsSync103(facesPath)) {
188113
+ if (existsSync104(facesPath)) {
187456
188114
  let faces = [];
187457
188115
  try {
187458
188116
  const parsed = JSON.parse(readFileSync121(facesPath, "utf8"));
@@ -187514,7 +188172,7 @@ function staticFaqFor(overlay, overlayPath) {
187514
188172
  const own6 = overlay.faq;
187515
188173
  if (own6 !== void 0) return deriveStaticFaq(own6);
187516
188174
  const bookPath = join115(dirname55(resolve79(overlayPath)), "book.json");
187517
- if (!existsSync103(bookPath)) return void 0;
188175
+ if (!existsSync104(bookPath)) return void 0;
187518
188176
  try {
187519
188177
  const book = JSON.parse(readFileSync121(bookPath, "utf8"));
187520
188178
  const copy = book?.copy;
@@ -187531,7 +188189,7 @@ function staticGalleriesFor(overlay, overlayPath) {
187531
188189
  overlay.galleries.map((gallery) => ({ role: "gallery", title: gallery.title, galleryImageUrls: gallery.photos }))
187532
188190
  );
187533
188191
  const bookPath = join115(dirname55(resolve79(overlayPath)), "book.json");
187534
- if (!existsSync103(bookPath)) return void 0;
188192
+ if (!existsSync104(bookPath)) return void 0;
187535
188193
  try {
187536
188194
  const book = JSON.parse(readFileSync121(bookPath, "utf8"));
187537
188195
  if (!book.look || book.look.unavailable || !Array.isArray(book.look.pages)) return void 0;
@@ -187565,7 +188223,7 @@ async function applyOverlays(input) {
187565
188223
  }
187566
188224
  const appDir3 = join115(repoRoot2, shellAppDir(shell));
187567
188225
  const committedPath = join115(appDir3, "provisioning.json");
187568
- const committedSite = existsSync103(committedPath) ? JSON.parse(readFileSync121(committedPath, "utf8")).site : void 0;
188226
+ const committedSite = existsSync104(committedPath) ? JSON.parse(readFileSync121(committedPath, "utf8")).site : void 0;
187569
188227
  if (committedSite !== void 0 && overlay.site === void 0) {
187570
188228
  throw new Error(
187571
188229
  `shell "${shell}" is a site shell: its contract names the customer's website (a \`site\` block), and this overlay has none. Re-run \`himi site analyze --url <site> --shell ${shell}\` to derive the site's page map (edit \`site.pages\` in provisioning.overlay.json if needed), then --apply.`
@@ -187606,7 +188264,7 @@ async function applyOverlays(input) {
187606
188264
  };
187607
188265
  let packageFaq = faq;
187608
188266
  let packageGalleries = galleries;
187609
- if (existsSync103(pkgProv)) {
188267
+ if (existsSync104(pkgProv)) {
187610
188268
  const cur = JSON.parse(readFileSync121(pkgProv, "utf8"));
187611
188269
  if (galleries === void 0 && !sameKnownSite(cur, overlay)) overlay.galleries = [];
187612
188270
  const merged = deepMerge4(cur, overlay);
@@ -187624,7 +188282,7 @@ async function applyOverlays(input) {
187624
188282
  overlay.installedVerticals.push("galleries");
187625
188283
  }
187626
188284
  const pkgTokens = join115(cwd, "tokens.json");
187627
- if (existsSync103(pkgTokens) && (tokens2.colors || tokens2.typography)) {
188285
+ if (existsSync104(pkgTokens) && (tokens2.colors || tokens2.typography)) {
187628
188286
  const cur = JSON.parse(readFileSync121(pkgTokens, "utf8"));
187629
188287
  let tokensIn = tokens2;
187630
188288
  if (tokens2.typography) {
@@ -187651,7 +188309,7 @@ async function applyOverlays(input) {
187651
188309
  }
187652
188310
  const homeAction = homeRefreshActionId(shell);
187653
188311
  const homeSrc = join115(cwd, "dev/index.ts");
187654
- if (homeAction && existsSync103(homeSrc)) {
188312
+ if (homeAction && existsSync104(homeSrc)) {
187655
188313
  const prev = readFileSync121(homeSrc, "utf8");
187656
188314
  const patched = patchBrandParams(prev, overlay, homeAction);
187657
188315
  let next = patched.src;
@@ -187687,13 +188345,13 @@ async function applyOverlays(input) {
187687
188345
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
187688
188346
  }
187689
188347
  }
187690
- if (!input.iconPath || !existsSync103(input.iconPath)) {
188348
+ if (!input.iconPath || !existsSync104(input.iconPath)) {
187691
188349
  const book = input.iconPath ? dirname55(dirname55(resolve79(input.iconPath))) : void 0;
187692
188350
  notes.push(
187693
188351
  book ? `no icon installed \u2014 ${siteLogoAbsence(book).hint}` : "no icon installed and no business book to say why \u2014 `himi icon generate` draws one from the accent, or `himi icon set <file.png>` installs the customer's own mark"
187694
188352
  );
187695
188353
  }
187696
- if (input.iconPath && existsSync103(input.iconPath)) {
188354
+ if (input.iconPath && existsSync104(input.iconPath)) {
187697
188355
  try {
187698
188356
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
187699
188357
  wrote.push(...installed.wrote);
@@ -188652,7 +189310,7 @@ __export(cli_exports3, {
188652
189310
  runSiteAnalyze: () => runSiteAnalyze,
188653
189311
  runSiteFunctionCli: () => runSiteFunctionCli
188654
189312
  });
188655
- import { existsSync as existsSync104, readFileSync as readFileSync122 } from "node:fs";
189313
+ import { existsSync as existsSync105, readFileSync as readFileSync122 } from "node:fs";
188656
189314
  import { resolve as resolve80 } from "node:path";
188657
189315
  function applyNeedsClient(shell) {
188658
189316
  if (shell === void 0 || !SHELL_NAMES.includes(shell)) return true;
@@ -188720,7 +189378,7 @@ async function runSiteAnalyze(opts) {
188720
189378
  if (book?.skipped) result.warnings = [...result.warnings ?? [], book.skipped];
188721
189379
  const derived = deriveSiteShell({ siteUrl: result.book.input.siteUrl, look: result.book.look, kind, routes });
188722
189380
  const prevOverlayPath = resolve80(outDir, "provisioning.overlay.json");
188723
- const prevSite = existsSync104(prevOverlayPath) ? (() => {
189381
+ const prevSite = existsSync105(prevOverlayPath) ? (() => {
188724
189382
  try {
188725
189383
  return JSON.parse(readFileSync122(prevOverlayPath, "utf8")).site;
188726
189384
  } catch {
@@ -188741,7 +189399,7 @@ async function runSiteAnalyze(opts) {
188741
189399
  }
188742
189400
  const previousOverlay = resolve80(outDir, "provisioning.overlay.json");
188743
189401
  carryOverGoals(
188744
- existsSync104(previousOverlay) ? readFileSync122(previousOverlay, "utf8") : void 0,
189402
+ existsSync105(previousOverlay) ? readFileSync122(previousOverlay, "utf8") : void 0,
188745
189403
  result.provisioningOverlay
188746
189404
  );
188747
189405
  const ownerCall = ownerAuth ? ownerTransportFrom(fetchImpl, ownerAuth) : void 0;
@@ -188827,7 +189485,7 @@ async function runSiteFunctionCli(opts) {
188827
189485
  });
188828
189486
  }
188829
189487
  function base44BookRoutes(bookPath, siteUrl) {
188830
- if (!existsSync104(bookPath)) return void 0;
189488
+ if (!existsSync105(bookPath)) return void 0;
188831
189489
  try {
188832
189490
  const book = JSON.parse(readFileSync122(bookPath, "utf8"));
188833
189491
  if (book.kind !== "base44-app-book" || !Array.isArray(book.routes)) return void 0;
@@ -189084,7 +189742,7 @@ __export(site_create_exports, {
189084
189742
  runSiteList: () => runSiteList,
189085
189743
  writeProvisioningBinding: () => writeProvisioningBinding
189086
189744
  });
189087
- import { existsSync as existsSync105, readFileSync as readFileSync123, writeFileSync as writeFileSync48 } from "node:fs";
189745
+ import { existsSync as existsSync106, readFileSync as readFileSync123, writeFileSync as writeFileSync48 } from "node:fs";
189088
189746
  import { resolve as resolvePath2 } from "node:path";
189089
189747
  function oauthClientName(projectName) {
189090
189748
  return `${projectName} client`;
@@ -189233,7 +189891,7 @@ async function runSiteCreate(opts) {
189233
189891
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
189234
189892
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
189235
189893
  }
189236
- function writeProvisioningBinding(target, binding2, fs2 = { existsSync: existsSync105, readFileSync: readFileSync123, writeFileSync: writeFileSync48 }) {
189894
+ function writeProvisioningBinding(target, binding2, fs2 = { existsSync: existsSync106, readFileSync: readFileSync123, writeFileSync: writeFileSync48 }) {
189237
189895
  const msid = binding2.siteId;
189238
189896
  const path2 = resolvePath2(target);
189239
189897
  const committed = committedShellProvisioning(path2);
@@ -190012,7 +190670,7 @@ __export(capture_exports, {
190012
190670
  nativeNotConfigured: () => nativeNotConfigured,
190013
190671
  variantWidth: () => variantWidth
190014
190672
  });
190015
- import { existsSync as existsSync106, mkdirSync as mkdirSync49, rmSync as rmSync31, writeFileSync as writeFileSync49 } from "node:fs";
190673
+ import { existsSync as existsSync107, mkdirSync as mkdirSync49, rmSync as rmSync31, writeFileSync as writeFileSync49 } from "node:fs";
190016
190674
  import { join as join116 } from "node:path";
190017
190675
  import { pathToFileURL as pathToFileURL8 } from "node:url";
190018
190676
  function variantWidth(variant) {
@@ -190025,7 +190683,7 @@ function frameFile(outDir, screen, variant, surface) {
190025
190683
  async function captureMockup(root, screen, entry, variant, outDir, session) {
190026
190684
  if (!entry.mockup) throw new MockupMissingError(`screen ${screen} declares no mockup in hhp.json`);
190027
190685
  const file2 = localPath(root, entry.mockup);
190028
- if (!existsSync106(file2)) throw new MockupMissingError(`screen ${screen}: mockup ${entry.mockup} does not exist`);
190686
+ if (!existsSync107(file2)) throw new MockupMissingError(`screen ${screen}: mockup ${entry.mockup} does not exist`);
190029
190687
  const own6 = session ?? new MockupSession();
190030
190688
  try {
190031
190689
  const png = await own6.shoot(file2, variant);
@@ -190333,8 +190991,8 @@ var init_score = __esm({
190333
190991
  });
190334
190992
 
190335
190993
  // ../visual-proof/evidence.mjs
190336
- import { existsSync as existsSync107, rmSync as rmSync34 } from "node:fs";
190337
- function resolveCaptionFont({ exists = existsSync107, env = process.env } = {}) {
190994
+ import { existsSync as existsSync108, rmSync as rmSync34 } from "node:fs";
190995
+ function resolveCaptionFont({ exists = existsSync108, env = process.env } = {}) {
190338
190996
  if (env?.VISUAL_PROOF_FONT) return env.VISUAL_PROOF_FONT;
190339
190997
  return CAPTION_FONT_CANDIDATES.find((p2) => exists(p2)) ?? null;
190340
190998
  }
@@ -190587,7 +191245,7 @@ __export(fidelity_exports, {
190587
191245
  himalayaRepoRoot: () => himalayaRepoRoot
190588
191246
  });
190589
191247
  import { execFileSync as execFileSync10 } from "node:child_process";
190590
- import { existsSync as existsSync108, realpathSync as realpathSync16 } from "node:fs";
191248
+ import { existsSync as existsSync109, realpathSync as realpathSync16 } from "node:fs";
190591
191249
  import { join as join121, relative as relative19, sep as sep22 } from "node:path";
190592
191250
  function himalayaRepoRoot(dir) {
190593
191251
  let top;
@@ -190596,7 +191254,7 @@ function himalayaRepoRoot(dir) {
190596
191254
  } catch {
190597
191255
  return null;
190598
191256
  }
190599
- if (!existsSync108(join121(top, "tools/judges/fidelity/JUDGE.md"))) return null;
191257
+ if (!existsSync109(join121(top, "tools/judges/fidelity/JUDGE.md"))) return null;
190600
191258
  const rel = relative19(realpathSync16(top), realpathSync16(dir));
190601
191259
  return rel.startsWith(`..${sep22}`) || rel === ".." ? null : top;
190602
191260
  }
@@ -190606,7 +191264,7 @@ function fidelityEnvelope(report3) {
190606
191264
  async function fidelityCommand(ctx) {
190607
191265
  const { io: io2, flags, print } = ctx;
190608
191266
  const dir = contentDir(flags);
190609
- if (!existsSync108(dir)) {
191267
+ if (!existsSync109(dir)) {
190610
191268
  io2.error(failure("fidelity_bad_flag", `content dir ${dir} does not exist`));
190611
191269
  return 2;
190612
191270
  }
@@ -190780,8 +191438,11 @@ var ServeCliClient = class {
190780
191438
  fetchTokens(app, release2) {
190781
191439
  return this.request("serve", "GET", `/v1/tokens?app=${encodeURIComponent(app)}${this.pinQ(release2)}`);
190782
191440
  }
190783
- fetchAppConfig(app, release2) {
190784
- return this.request("serve", "GET", `/v1/app-config/${encodeURIComponent(app)}${release2 ? `?release=${encodeURIComponent(release2)}` : ""}`);
191441
+ /** A pinned non-live [release] on a deployed plane needs [previewToken] (see `fetchScreen`). */
191442
+ fetchAppConfig(app, release2, previewToken) {
191443
+ return this.request("serve", "GET", `/v1/app-config/${encodeURIComponent(app)}${release2 ? `?release=${encodeURIComponent(release2)}` : ""}`, {
191444
+ ...previewToken ? { headers: { "x-himalaya-preview-token": previewToken } } : {}
191445
+ });
190785
191446
  }
190786
191447
  fetchBundle(app, hash2) {
190787
191448
  return this.request("serve", "GET", `/v1/bundles/${hash2}.bundle.js?app=${encodeURIComponent(app)}`);
@@ -191322,7 +191983,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
191322
191983
  "empty-app-ok",
191323
191984
  "aab",
191324
191985
  "managed",
191325
- "source-hash"
191986
+ "source-hash",
191987
+ "skip-captcha-check"
191326
191988
  ]);
191327
191989
  var SWITCH_FLAGS = new Set([...BOOLEAN_FLAGS].filter((name) => name !== "deep"));
191328
191990
  var SWITCH_CONSEQUENCE = {
@@ -192232,7 +192894,7 @@ var COMMANDS = [
192232
192894
  { name: "patch", summary: 'change ONE item of a published app \u2014 a screen descriptor, a Tier-5 bundle, tokens, or the app-config baseline \u2014 from a RAW FILE, with no content package and no build. The low-level door: it does not lint your package, because there is no package here; `himi push --only <selector>` is the same edit with the full build + lint gate and is what to reach for when the package is in hand. Derives a new release from the app\'s current LIVE release, or from `--from-release <id>`. An explicit `--draft` or `--live` is REQUIRED \u2014 a patch has no default blast radius \u2014 and `--notes` is required with `--live` on a screen or app-config patch, because off-repo `gitSha` is "dev" and the ledger notes are then the only durable record of bytes that correspond to no commit. A branch does not merge: activating one derived from an older release reverts every item the patch does not move, so that case is refused until `--accept-regressions`. A bundle patch must repoint a screen (`--for-screen`) or the bytes land under a manifest key nothing reads. It cannot ADD a screen (screens are git-only, RFC 0012), never claims a slug \u2014 the app must already be published \u2014 and carries no attestation, source archive, assets, icon or minBinaryVersion, each named in the output.', required: "screen <id> --file <descriptor.json> [--bundle <sha256|none>] | bundle --file <bundle.js> (--for-screen <id>[,<id>] | --no-rebind) | tokens --file <tokens.json> | app-config --file <app-config.json> ; (--draft|--live) --app <a> [--from-release <id>] [--accept-regressions] [--label <n>] [--notes <t>] [--force] [--allow-binary-gap] [--target <t>] (--control-token)" },
192233
192895
  { name: "flags", summary: "read and change the app's OPERATIONAL flags (RFC 0013) \u2014 `list` prints what the DEVICE door serves, `set <key> --definition` creates or replaces one, `ramp <key>` moves its rollout share or kills it, `rm <key>` deletes it and echoes the definition back so the removal is reversible. Every write is followed by a read of the same door the device uses, and the answer is reported as `servedNow`: flags are stored in the app's git-committed flags.json, so a control plane with no checkout accepts a write it will never serve.", required: "list|set|ramp|rm [<key>] [--definition <json|@file>] [--percent 0-100] [--enabled true|false] [--app <a>] (--control-token)" },
192234
192896
  { name: "visibility", summary: "read (no value) or set whether the public registry (/v1/apps + the Himi Player picker) LISTS the app. `public` lists it for everyone; `unlisted` hides it from listings while direct reads by app id \u2014 deep links, previews \u2014 keep working. App from --app or the content dir. Setting it needs no republish.", required: "[public|unlisted] [--app <a>] [--content <dir>] (--control-token)" },
192235
- { name: "dist", summary: "build an INSTALLABLE QA binary (signed IPA / APK) on the Wix Job Service Mac pool and return an install link \u2014 the serve plane brokers the job, the pool builds it. WAITS for the build by default (~4 min) and prints the install link; `--no-wait` returns a jobId immediately instead. Omitting `--release` builds whatever is LIVE for the app. A named draft is refused unless `--allow-draft` makes that exceptional build explicit. `status <jobId>` / `watch <jobId>` / `list --app <a>` / `cancel <jobId>` inspect and stop builds (`watch` reports up front whether the job mints its own signing identity or borrows one, so a doomed iOS build is cancellable in seconds rather than after the archive). Off-repo, name a target \u2014 `--target wix` for a self-serve Wix login or `--target bo` for the internal employee path (the in-repo default is a local dev plane that isn't reachable off-repo); self-serve callers may build their OWN apps under a tighter budget (2 in flight / 5 per 24h), while a manage-only owner-role credential is refused. `--shell` picks the native host to sign into (branded-lite | owner-lite | brandy | base44 | player | demo-app) and defaults to the content package's himalaya.content.json `shell` (templates carry the one they were written for), else branded-lite. The per-app shells ADD the release's declared capabilities to the binary, while Himi Player and demo-app refuse a release declaring one they lack; `--plan` previews it without building; `brandy` is the site shell \u2014 the customer's own website as native-framed tabs. `--signing` defaults to `wix-managed` \u2014 the Wix Apple account mints a per-app signing identity for the build, so the caller provisions no Apple credentials of their own; `--signing byo --site <id>` reuses an existing customer ad-hoc profile and certificate for iOS; requires an owned site and a completed personal signing binding. `--android-signing` is the Android counterpart and a SEPARATE axis (a keystore, not an Apple account): `qa-keystore` (default \u2014 the repo's committed QA key, identical on every pool machine, so a build installs over the previous one), `wix-keystore` (the Wix key the pool machine carries; the build FAILS naming the paths searched if it carries none, rather than quietly signing with something else), or `debug` (the old per-machine key \u2014 QA must uninstall any previous build first). `--aab` builds an Android App Bundle (.aab) for the owner to upload to Google Play AND an APK from the same build \u2014 a download link for the .aab, an install link and QR for the APK; Android only; the bundle is signed with the Wix production key (`wix-keystore`, the upload key Branded apps use) \u2014 any other `--android-signing` is refused \u2014 and the APK with the committed QA key; a self-serve caller gets one only as the store build of its own Containers app (it must own the app's site, and `--aab` sends your Wix login); any other self-serve `--aab` gets 403 android_format_not_allowed. NOT the normal way to ship: screens, Tier-5 bundles, tokens and app-config all go over the air with `himi push` in seconds \u2014 a binary trip is only needed for a NATIVE delta (a new SDUI component, a new declared capability/entitlement, a new bundle id, or a minBinaryVersion floor bump). Refuses to build an app whose launcher icon is missing, a placeholder, or one a store would reject \u2014 the one thing a binary carries that no later OTA push can fix; `--force` builds anyway", required: "[--app <a>] [--platform ios,android] [--release <id>] [--allow-draft] [--target <t>] [--shell branded-lite|owner-lite|brandy|base44|player|demo-app] [--signing wix-managed|byo] [--site <id>] [--android-signing qa-keystore|wix-keystore|debug] [--aab] [--borrow-ios-id <bundleId>] [--no-wait] [--force] [--plan] | status <jobId> [--target <t>] | watch <jobId> [--target <t>] [--interval <ms>] | list --app <a> [--target <t>] | cancel <jobId> [--target <t>]" },
192897
+ { name: "dist", summary: "build an INSTALLABLE QA binary (signed IPA / APK) on the Wix Job Service Mac pool and return an install link \u2014 the serve plane brokers the job, the pool builds it. WAITS for the build by default (~4 min) and prints the install link; `--no-wait` returns a jobId immediately instead. Omitting `--release` builds whatever is LIVE for the app. A named draft is refused unless `--allow-draft` makes that exceptional build explicit. `status <jobId>` / `watch <jobId>` / `list --app <a>` / `cancel <jobId>` inspect and stop builds (`watch` reports up front whether the job mints its own signing identity or borrows one, so a doomed iOS build is cancellable in seconds rather than after the archive). Off-repo, name a target \u2014 `--target wix` for a self-serve Wix login or `--target bo` for the internal employee path (the in-repo default is a local dev plane that isn't reachable off-repo); self-serve callers may build their OWN apps under a tighter budget (2 in flight / 5 per 24h), while a manage-only owner-role credential is refused. `--shell` picks the native host to sign into (branded-lite | owner-lite | brandy | base44 | player | demo-app) and defaults to the content package's himalaya.content.json `shell` (templates carry the one they were written for), else branded-lite. The per-app shells ADD the release's declared capabilities to the binary, while Himi Player and demo-app refuse a release declaring one they lack; `--plan` previews it without building; `brandy` is the site shell \u2014 the customer's own website as native-framed tabs. `--signing` defaults to `wix-managed` \u2014 the Wix Apple account mints a per-app signing identity for the build, so the caller provisions no Apple credentials of their own; `--signing byo --site <id>` reuses an existing customer ad-hoc profile and certificate for iOS; requires an owned site and a completed personal signing binding. `--android-signing` is the Android counterpart and a SEPARATE axis (a keystore, not an Apple account): `qa-keystore` (default \u2014 the repo's committed QA key, identical on every pool machine, so a build installs over the previous one), `wix-keystore` (the Wix key the pool machine carries; the build FAILS naming the paths searched if it carries none, rather than quietly signing with something else), or `debug` (the old per-machine key \u2014 QA must uninstall any previous build first). `--aab` builds an Android App Bundle (.aab) for the owner to upload to Google Play AND an APK from the same build \u2014 a download link for the .aab, an install link and QR for the APK; Android only; the bundle is signed with the Wix production key (`wix-keystore`, the upload key Branded apps use) \u2014 any other `--android-signing` is refused \u2014 and the APK with the committed QA key; a self-serve caller gets one only as the store build of its own Containers app (it must own the app's site, and `--aab` sends your Wix login); any other self-serve `--aab` gets 403 android_format_not_allowed. NOT the normal way to ship: screens, Tier-5 bundles, tokens and app-config all go over the air with `himi push` in seconds \u2014 a binary trip is only needed for a NATIVE delta (a new SDUI component, a new declared capability/entitlement, a new bundle id, or a minBinaryVersion floor bump). Refuses to build an app whose launcher icon is missing, a placeholder, or one a store would reject \u2014 the one thing a binary carries that no later OTA push can fix; `--force` builds anyway. Also refuses a release whose member login (WixConsumerAuthProvider) names no siteId, or not in lower case, when Wix says its site requires a member CAPTCHA \u2014 such a binary ships without the CAPTCHA key and every member sign-in fails; whatever it cannot read (the release, the site's setting) only warns and builds; `--skip-captcha-check` builds anyway", required: "[--app <a>] [--platform ios,android] [--release <id>] [--allow-draft] [--target <t>] [--shell branded-lite|owner-lite|brandy|base44|player|demo-app] [--signing wix-managed|byo] [--site <id>] [--android-signing qa-keystore|wix-keystore|debug] [--aab] [--borrow-ios-id <bundleId>] [--no-wait] [--force] [--skip-captcha-check] [--plan] | status <jobId> [--target <t>] | watch <jobId> [--target <t>] [--interval <ms>] | list --app <a> [--target <t>] | cancel <jobId> [--target <t>]" },
192236
192898
  { name: "testflight", summary: 'build, sign and upload an app to TestFlight (internal testers \u2014 no Beta App Review), then WAIT until App Store Connect finishes processing it. Sugar for `store upload --track testflight`. Prompts first: a build number is consumed permanently on a real Apple account and cannot be reused \u2014 `--yes` for CI, `--dry-run` to see the plan without touching anything. The wait is the real check, not a nicety: fastlane prints "Successfully uploaded" for a binary App Store Connect then rejects in processing, and a rejected binary leaves NO build record at all (the reason is emailed). Public App Store review is deliberately NOT automatable here \u2014 `--track appstore` is refused; use /submit-ios-app when you mean to go public', required: "<app> [--track testflight|internal] [--profile <name>] [--yes] [--dry-run] [--skip-build] [--no-wait] [--json]" },
192237
192899
  { name: "store", summary: "the App Store Connect group behind `testflight`. `preflight <app>` checks readiness offline (icon, entitlements, encryption flag, version, store.config ids, listing + review record); `upload <app>` is what `testflight` runs; `status <app>` reads live build + processing state. `--profile` selects WHICH App Store Connect account: an app built for a Wix customer ships under the CUSTOMER's Apple team (Apple guideline 4.2.6 forbids an app-generation service submitting on a client's behalf), declared per-app as ios.credentialProfile in store.config.yaml and resolved from ~/.himalaya/store-credentials/<name>.json \u2014 the .p8 itself never enters the repo. `iap draft|plan|apply|status|connect|screenshots|check|mappings` registers an app owner's digital products as in-app purchases (non-consumables, or consumable price tiers): draft reads the Wix catalog and writes the manifest, plan and status only read, apply is the one App Store mutation boundary, connect readies the Wix site to sell them (purchases app, In-App Purchase key, appleIAP provider, product mappings; --preview-tag routes only its Serve calls to a deploy preview), legacy screenshots --profile renders each product's purchase screen from the app (--content) as a bound iPhone shows it to a signed-in member and names the 1290\xD72796 PNG as the item's App Review screenshot, and none of them builds, signs or needs a checkout (`himi docs iap-products`) IAP provisioning defaults to Catalog; managed apply generates screenshots on the server with no local .p8 or profile fallback; --managed is optional and --profile selects legacy mode; --catalog-route or HIMI_IAP_CATALOG_ROUTE has no default.", required: "preflight <app> [--profile <n>] | upload <app> [--track <t>] [--profile <n>] [--yes] [--dry-run] | status <app> [--profile <n>] | iap draft --site <msid> " + IAP_CREDENTIAL_OPTIONS + " --apple-app <id> --bundle <id> --base-territory <t> --out <f> [--territories <a,b>] [--locale <l>] [--mode per-item|tiers] [--merge <f>] [--report <f>] [--json] | iap plan --manifest <f> " + IAP_CREDENTIAL_OPTIONS + " --out <f> [--json] | iap apply --plan <f> " + IAP_CREDENTIAL_OPTIONS + " --out <f> [--yes] [--limit <n>] [--no-submit] [--json] | iap status --manifest <f> " + IAP_CREDENTIAL_OPTIONS + " [--out <f>] [--json] | iap connect --manifest <f> [--profile <n>] [--catalog-route <prefix>] [--iap-key <AuthKey.p8> --iap-key-id <id>] [--content <dir>] [--preview-tag dp-<tag>] [--dry-run] [--yes] [--out <f>] [--json] | iap screenshots --manifest <f> --content <dir> --profile <n> [--out-dir <d>] [--only <id,\u2026>] [--force] [--dry-run] [--json] | iap check --manifest <f> --catalog-route <prefix> [--purchases-app <id>] [--content <dir>] [--plist <file>] [--visitor-client <id>] [--json] | iap mappings --site <msid> --purchases-app <id> --catalog-route <prefix> [--product <id> --active true|false --yes] [--json]" },
192238
192900
  { name: "guide", summary: "print the Himalaya overview + headless dev loop + available topics (offline)", required: "[--json]" },
@@ -192843,7 +193505,7 @@ async function hhpCommand(ctx) {
192843
193505
  // src/commands/init.ts
192844
193506
  init_scaffold();
192845
193507
  init_store();
192846
- import { existsSync as existsSync45 } from "node:fs";
193508
+ import { existsSync as existsSync46 } from "node:fs";
192847
193509
  import { join as join44, resolve as resolve43 } from "node:path";
192848
193510
  init_cli_shared();
192849
193511
  async function adoptAtInit(dir) {
@@ -192870,7 +193532,7 @@ var NoBase44SlotError = class extends Error {
192870
193532
  };
192871
193533
  async function initFromRemoteTemplate(name, parentDir, template, c2, opts = {}) {
192872
193534
  const dir = resolve43(parentDir, name);
192873
- if (existsSync45(join44(dir, "himalaya.content.json"))) {
193535
+ if (existsSync46(join44(dir, "himalaya.content.json"))) {
192874
193536
  throw new Error(`content package already exists at ${dir}`);
192875
193537
  }
192876
193538
  const r3 = await c2.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -192946,7 +193608,7 @@ async function initCommand(ctx) {
192946
193608
  const owed = openApprovals2(readApprovals2(dir).events).length;
192947
193609
  next.push(`carried the planning journal into the app (${carried.join(", ")}${owed ? `; ${owed} approval${owed === 1 ? "" : "s"} still owed` : ""}) \u2014 \`himi progress --brief\` there shows it`);
192948
193610
  }
192949
- const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f2) => existsSync45(join44(parentDir, f2)));
193611
+ const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f2) => existsSync46(join44(parentDir, f2)));
192950
193612
  if (planInParent.length) {
192951
193613
  next.push(`${planInParent.join(" and ")} in ${parentDir} ${planInParent.length > 1 ? "are" : "is"} not the app's \u2014 if that is the approved plan, move it over the template's boilerplate in ${dir}`);
192952
193614
  }
@@ -193013,7 +193675,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
193013
193675
  }
193014
193676
 
193015
193677
  // src/commands/template.ts
193016
- import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync67, existsSync as existsSync57, rmSync as rmSync16 } from "node:fs";
193678
+ import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync67, existsSync as existsSync58, rmSync as rmSync16 } from "node:fs";
193017
193679
  import { createHash as createHash30 } from "node:crypto";
193018
193680
  import { tmpdir as tmpdir3 } from "node:os";
193019
193681
  import { join as join57, resolve as resolve46, basename as basename15 } from "node:path";
@@ -193080,7 +193742,7 @@ async function cmsIssues(dir, app, strict, opts) {
193080
193742
  // src/surface-lint.ts
193081
193743
  init_car();
193082
193744
  init_preview_surfaces();
193083
- import { existsSync as existsSync46, readFileSync as readFileSync55 } from "node:fs";
193745
+ import { existsSync as existsSync47, readFileSync as readFileSync55 } from "node:fs";
193084
193746
  import { join as join45 } from "node:path";
193085
193747
  var VALID_PLATFORM_KEYS2 = /* @__PURE__ */ new Set([
193086
193748
  ...PREVIEW_SURFACES.map((s2) => s2.platformKey).filter((k2) => k2 !== null),
@@ -193093,7 +193755,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
193093
193755
  let platforms = {};
193094
193756
  let envelopeUnreadable = false;
193095
193757
  const p2 = join45(dir, "platforms.json");
193096
- if (existsSync46(p2)) {
193758
+ if (existsSync47(p2)) {
193097
193759
  let raw;
193098
193760
  try {
193099
193761
  raw = JSON.parse(readFileSync55(p2, "utf8"));
@@ -193126,7 +193788,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
193126
193788
  }
193127
193789
  if (envelopeUnreadable) return issues;
193128
193790
  const configTs = join45(dir, "config.ts");
193129
- if (existsSync46(configTs)) {
193791
+ if (existsSync47(configTs)) {
193130
193792
  const declared = parseCarConfig(readFileSync55(configTs, "utf8")).category;
193131
193793
  const inEnvelope = platforms.androidauto === true;
193132
193794
  if (inEnvelope && !declared) {
@@ -193186,10 +193848,10 @@ async function fontIssues(dir, strict, opts = {}) {
193186
193848
  }
193187
193849
 
193188
193850
  // src/stores-api-lint.ts
193189
- import { existsSync as existsSync49, readdirSync as readdirSync26, readFileSync as readFileSync58 } from "node:fs";
193851
+ import { existsSync as existsSync50, readdirSync as readdirSync26, readFileSync as readFileSync58 } from "node:fs";
193190
193852
  import { join as join48 } from "node:path";
193191
193853
  function storesV1Issues(contentDir2, strict) {
193192
- const roots2 = [join48(contentDir2, "tier5-src"), join48(contentDir2, "dev")].filter(existsSync49);
193854
+ const roots2 = [join48(contentDir2, "tier5-src"), join48(contentDir2, "dev")].filter(existsSync50);
193193
193855
  const files = [];
193194
193856
  const visit = (dir) => {
193195
193857
  for (const entry of readdirSync26(dir, { withFileTypes: true })) {
@@ -193462,7 +194124,7 @@ async function templateCommand(ctx) {
193462
194124
  const result = { ...outcome.result, ...fromSpec };
193463
194125
  const adoptDir = contentDir(flags);
193464
194126
  const kept = flags["dry-run"] !== true && (outcome.code === 0 || outcome.result.forcedKeep === true);
193465
- if (kept && existsSync57(join57(adoptDir, adoptBits.ADOPT_FILE))) {
194127
+ if (kept && existsSync58(join57(adoptDir, adoptBits.ADOPT_FILE))) {
193466
194128
  try {
193467
194129
  const inv = adoptBits.inspectApp(adoptDir);
193468
194130
  adoptBits.writeInventory(adoptDir, inv, { companions: "if-missing" });
@@ -193847,16 +194509,16 @@ async function ejectCommand(ctx) {
193847
194509
  // src/commands/validate.ts
193848
194510
  init_wix_placeholders();
193849
194511
  init_lint_report();
193850
- import { readFileSync as readFileSync77, existsSync as existsSync66 } from "node:fs";
194512
+ import { readFileSync as readFileSync77, existsSync as existsSync67 } from "node:fs";
193851
194513
  import { join as join69 } from "node:path";
193852
194514
  init_token_lint();
193853
194515
 
193854
194516
  // src/icon-lint.ts
193855
194517
  init_icon2();
193856
- import { existsSync as existsSync60 } from "node:fs";
194518
+ import { existsSync as existsSync61 } from "node:fs";
193857
194519
  import { join as join60 } from "node:path";
193858
194520
  async function iconIssues(dir, strict, opts = {}) {
193859
- if (!existsSync60(join60(dir, "himalaya.content.json"))) return [];
194521
+ if (!existsSync61(join60(dir, "himalaya.content.json"))) return [];
193860
194522
  const status = await iconStatus(dir);
193861
194523
  const issue2 = (severity, message2) => ({
193862
194524
  screen: null,
@@ -193919,7 +194581,7 @@ async function nativeConfigAdvisories(app, configSource) {
193919
194581
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
193920
194582
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
193921
194583
  const iosYmlPath = iosProjectYml2(app);
193922
- const iosYml = existsSync66(iosYmlPath) ? readFileSync77(iosYmlPath, "utf8") : "";
194584
+ const iosYml = existsSync67(iosYmlPath) ? readFileSync77(iosYmlPath, "utf8") : "";
193923
194585
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
193924
194586
  if (!optedIn) {
193925
194587
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -194009,7 +194671,7 @@ import { readFileSync as readFileSync80 } from "node:fs";
194009
194671
 
194010
194672
  // src/dpx.ts
194011
194673
  import { spawnSync as spawnSync3 } from "node:child_process";
194012
- import { existsSync as existsSync67, readFileSync as readFileSync78 } from "node:fs";
194674
+ import { existsSync as existsSync68, readFileSync as readFileSync78 } from "node:fs";
194013
194675
  import { homedir as homedir9 } from "node:os";
194014
194676
  import { join as join70 } from "node:path";
194015
194677
  var BO_COOKIE = "WixBoAuthentication_1_19_0";
@@ -194037,7 +194699,7 @@ function dpxSessionTokenPath() {
194037
194699
  var DPX_SESSION_PORT = 5151;
194038
194700
  function dpxSessionPresent(path2 = dpxSessionTokenPath()) {
194039
194701
  try {
194040
- return existsSync67(path2) && readFileSync78(path2, "utf8").trim().length > 0;
194702
+ return existsSync68(path2) && readFileSync78(path2, "utf8").trim().length > 0;
194041
194703
  } catch {
194042
194704
  return false;
194043
194705
  }
@@ -194272,13 +194934,13 @@ async function mcpCommand(ctx) {
194272
194934
 
194273
194935
  // src/commands/studio.ts
194274
194936
  init_cli_shared();
194275
- import { mkdirSync as mkdirSync36, writeFileSync as writeFileSync30, existsSync as existsSync70 } from "node:fs";
194937
+ import { mkdirSync as mkdirSync36, writeFileSync as writeFileSync30, existsSync as existsSync71 } from "node:fs";
194276
194938
  import { join as join74, resolve as resolve60, basename as basename19, dirname as dirname42 } from "node:path";
194277
194939
  async function studioCommand(ctx) {
194278
194940
  const { io: io2, pos, flags, print } = ctx;
194279
194941
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
194280
194942
  const dir = contentDir(flags);
194281
- if (!existsSync70(join74(dir, "himalaya.content.json"))) {
194943
+ if (!existsSync71(join74(dir, "himalaya.content.json"))) {
194282
194944
  io2.error(JSON.stringify({ ok: false, error: "not_a_content_package", dir, hint: "run from the app directory (or pass --content <dir>) \u2014 the board reads that package's .himi/" }));
194283
194945
  return 2;
194284
194946
  }
@@ -194338,7 +195000,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
194338
195000
 
194339
195001
  // src/commands/progress.ts
194340
195002
  init_cli_shared();
194341
- import { existsSync as existsSync71 } from "node:fs";
195003
+ import { existsSync as existsSync72 } from "node:fs";
194342
195004
  import { join as join75 } from "node:path";
194343
195005
  async function progressCommand2(ctx) {
194344
195006
  const { io: io2, pos, flags, print } = ctx;
@@ -194362,7 +195024,7 @@ async function progressCommand2(ctx) {
194362
195024
  } = await Promise.resolve().then(() => (init_progress(), progress_exports));
194363
195025
  const dir = contentDir(flags);
194364
195026
  const journal2 = journalPath2(dir);
194365
- const isContentPackage = existsSync71(join75(dir, "himalaya.content.json"));
195027
+ const isContentPackage = existsSync72(join75(dir, "himalaya.content.json"));
194366
195028
  const hint = isContentPackage ? {} : { hint: "no himalaya.content.json here \u2014 run from the app directory (or pass --content <dir>) so the build board finds this journal" };
194367
195029
  const message2 = pos.slice(1).join(" ").trim();
194368
195030
  const approvalFlags = ["approve", "done", "drop"].filter((f2) => flags[f2] !== void 0);
@@ -194552,7 +195214,7 @@ init_himi_report();
194552
195214
  init_attestation();
194553
195215
  init_wix_placeholders();
194554
195216
  init_lint_report();
194555
- import { existsSync as existsSync76, rmSync as rmSync21 } from "node:fs";
195217
+ import { existsSync as existsSync77, rmSync as rmSync21 } from "node:fs";
194556
195218
  import { join as join81 } from "node:path";
194557
195219
  init_crawl_layers();
194558
195220
  init_cli_shared();
@@ -194575,7 +195237,7 @@ async function testCommand(ctx) {
194575
195237
  const { io: io2, deps, pos, flags, print, serveBase } = ctx;
194576
195238
  const dir = contentDir(flags);
194577
195239
  const structuredOutput = flags.json === true || io2.isTTY !== true;
194578
- if (existsSync76(join81(dir, "functions"))) {
195240
+ if (existsSync77(join81(dir, "functions"))) {
194579
195241
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
194580
195242
  const loaded = await loadFunctionsApp2(dir);
194581
195243
  const results = [];
@@ -195240,7 +195902,7 @@ async function iconsCommand(ctx) {
195240
195902
 
195241
195903
  // src/commands/pull.ts
195242
195904
  init_cli_shared();
195243
- import { existsSync as existsSync78, readdirSync as readdirSync35, statSync as statSync21 } from "node:fs";
195905
+ import { existsSync as existsSync79, readdirSync as readdirSync35, statSync as statSync21 } from "node:fs";
195244
195906
  import { join as join84, resolve as resolve63 } from "node:path";
195245
195907
  async function pullCommand(ctx) {
195246
195908
  const { io: io2, pos, flags, print, client: client2 } = ctx;
@@ -195258,7 +195920,7 @@ async function pullCommand(ctx) {
195258
195920
  io2.error(JSON.stringify(linked));
195259
195921
  return 1;
195260
195922
  }
195261
- if (existsSync78(dest)) {
195923
+ if (existsSync79(dest)) {
195262
195924
  if (!statSync21(dest).isDirectory()) {
195263
195925
  io2.error(JSON.stringify({
195264
195926
  error: `${dest} exists and is not a directory`,
@@ -195271,7 +195933,7 @@ async function pullCommand(ctx) {
195271
195933
  io2.error(JSON.stringify({ error: `${dest} already exists and is not empty`, hint: "pass --force to write into it anyway (merging, and naming what it keeps), --replace to restore exactly what was archived, or --out/--name to land somewhere else" }));
195272
195934
  return 1;
195273
195935
  }
195274
- if (replace && occupied && !existsSync78(join84(dest, "himalaya.content.json"))) {
195936
+ if (replace && occupied && !existsSync79(join84(dest, "himalaya.content.json"))) {
195275
195937
  io2.error(JSON.stringify({
195276
195938
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
195277
195939
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -195352,7 +196014,7 @@ async function pullCommand(ctx) {
195352
196014
 
195353
196015
  // src/commands/push.ts
195354
196016
  import { createHash as createHash41 } from "node:crypto";
195355
- import { existsSync as existsSync81 } from "node:fs";
196017
+ import { existsSync as existsSync82 } from "node:fs";
195356
196018
  import { join as join87, relative as relative17 } from "node:path";
195357
196019
  init_config();
195358
196020
  init_release();
@@ -195520,7 +196182,7 @@ async function pushCommand(ctx) {
195520
196182
  const payload = await loadReleaseFromDir(declared, out2);
195521
196183
  const oneAppEvidence = join87(dir, ".himi", "one-app");
195522
196184
  const oneAppCoverage = join87(dir, "ONE-APP-COVERAGE.json");
195523
- const oneAppMarked = existsSync81(join87(oneAppEvidence, "manifest.json")) || existsSync81(oneAppCoverage);
196185
+ const oneAppMarked = existsSync82(join87(oneAppEvidence, "manifest.json")) || existsSync82(oneAppCoverage);
195524
196186
  let oneAppCoverageCount = 0;
195525
196187
  let oneAppCoverageReady = false;
195526
196188
  let oneAppNativeProof;
@@ -196188,12 +196850,12 @@ async function packCommand(ctx) {
196188
196850
 
196189
196851
  // src/commands/import.ts
196190
196852
  init_cli_shared();
196191
- import { existsSync as existsSync82, readdirSync as readdirSync36, statSync as statSync23 } from "node:fs";
196853
+ import { existsSync as existsSync83, readdirSync as readdirSync36, statSync as statSync23 } from "node:fs";
196192
196854
  import { resolve as resolve67 } from "node:path";
196193
196855
  function restoreDestinationIssue(dest, force) {
196194
196856
  const link = symlinkDestinationIssue(dest);
196195
196857
  if (link) return link;
196196
- if (!existsSync82(dest)) return null;
196858
+ if (!existsSync83(dest)) return null;
196197
196859
  if (!statSync23(dest).isDirectory()) {
196198
196860
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
196199
196861
  }
@@ -197385,7 +198047,7 @@ async function promoteCommand(ctx) {
197385
198047
  // src/commands/patch.ts
197386
198048
  init_lint_report();
197387
198049
  init_cli_shared();
197388
- import { readFileSync as readFileSync98, existsSync as existsSync86 } from "node:fs";
198050
+ import { readFileSync as readFileSync98, existsSync as existsSync87 } from "node:fs";
197389
198051
  import { createHash as createHash42 } from "node:crypto";
197390
198052
  var PATCH_HELP = `himi patch \u2014 change ONE item of a published app, from a raw file, on a machine
197391
198053
  that does not have the content package.
@@ -197464,7 +198126,7 @@ async function patchCommand(ctx) {
197464
198126
  io2.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
197465
198127
  return 2;
197466
198128
  }
197467
- if (!existsSync86(file2)) {
198129
+ if (!existsSync87(file2)) {
197468
198130
  io2.error(JSON.stringify({ error: `no such file: ${file2}` }));
197469
198131
  return 2;
197470
198132
  }
@@ -198025,7 +198687,7 @@ async function testflightCommand(ctx) {
198025
198687
  }
198026
198688
 
198027
198689
  // src/commands/dist.ts
198028
- import { existsSync as existsSync92 } from "node:fs";
198690
+ import { existsSync as existsSync93 } from "node:fs";
198029
198691
  import { join as join99 } from "node:path";
198030
198692
  init_wix_owner();
198031
198693
  init_cli_shared();
@@ -198065,7 +198727,7 @@ async function distCommand(ctx) {
198065
198727
  const rememberLastDist = async (jobs2) => {
198066
198728
  try {
198067
198729
  const dir = contentDir(flags);
198068
- if (!existsSync92(join99(dir, "himalaya.content.json"))) return;
198730
+ if (!existsSync93(join99(dir, "himalaya.content.json"))) return;
198069
198731
  const { lastDistFromJob: lastDistFromJob2, writeLastDist: writeLastDist2 } = await Promise.resolve().then(() => (init_capabilities(), capabilities_exports));
198070
198732
  const own6 = await appId(dir);
198071
198733
  for (const job of jobs2) {
@@ -198309,9 +198971,35 @@ ${label3}`);
198309
198971
  return 0;
198310
198972
  }
198311
198973
  const platform = str3(flags.platform)?.split(",").map((p2) => p2.trim()).filter(Boolean);
198974
+ const { captchaContentInput: captchaContentInput2, captchaDistGate: captchaDistGate2 } = await Promise.resolve().then(() => (init_captcha2(), captcha_exports));
198975
+ const captchaSiteId = str3(flags.site) ?? (await appId(contentDir(flags)) === app ? captchaContentInput2(contentDir(flags)).siteId : void 0);
198976
+ const { captchaBindable: captchaBindable2 } = await Promise.resolve().then(() => (init_captcha_bindable(), captcha_bindable_exports));
198977
+ const { DEFAULT_DIST_SHELL: DEFAULT_DIST_SHELL2 } = await Promise.resolve().then(() => (init_dist_jobs(), dist_jobs_exports));
198978
+ if (flags.force !== true && flags["skip-captcha-check"] !== true && captchaBindable2(distShell ?? DEFAULT_DIST_SHELL2, false)) {
198979
+ const verdict = await captchaDistGate2({
198980
+ client: c2,
198981
+ app,
198982
+ release: str3(flags.release),
198983
+ requestedSite: captchaSiteId,
198984
+ // The plane's own default when --platform is absent: Android (iOS for a customer TestFlight build).
198985
+ platforms: platform?.length ? platform : str3(flags.destination) === "testflight" ? ["ios"] : ["android"]
198986
+ });
198987
+ if (verdict.refuse) {
198988
+ print({
198989
+ ok: false,
198990
+ code: verdict.refuse.code,
198991
+ error: verdict.refuse.code,
198992
+ message: verdict.refuse.message,
198993
+ captchaPolicy: verdict.refuse.captchaPolicy,
198994
+ hint: "fix config.ts and `himi push`, then build that release: `himi dist --release <its id> --allow-draft` (a draft), or promote it first \u2014 or `himi dist --skip-captcha-check` to build without the member CAPTCHA key"
198995
+ });
198996
+ return 1;
198997
+ }
198998
+ if (verdict.advisory) (flags.json === true ? io2.error : io2.log)(`\u26A0 ${verdict.advisory}`);
198999
+ }
198312
199000
  {
198313
199001
  const distDir = contentDir(flags);
198314
- if (existsSync92(join99(distDir, "himalaya.content.json"))) {
199002
+ if (existsSync93(join99(distDir, "himalaya.content.json"))) {
198315
199003
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
198316
199004
  const errors = issues.filter((i2) => i2.severity === "error");
198317
199005
  for (const i2 of issues.filter((i3) => i3.severity === "warning")) {
@@ -198328,8 +199016,6 @@ ${label3}`);
198328
199016
  }
198329
199017
  }
198330
199018
  }
198331
- const { captchaContentInput: captchaContentInput2 } = await Promise.resolve().then(() => (init_captcha2(), captcha_exports));
198332
- const captchaSiteId = str3(flags.site) ?? captchaContentInput2(contentDir(flags)).siteId;
198333
199019
  let wixAuthorization;
198334
199020
  const customerTestflight = str3(flags.signing)?.trim().toLowerCase() === "byo" && str3(flags.destination) === "testflight";
198335
199021
  if (str3(flags.signing)?.trim().toLowerCase() === "byo") {
@@ -199321,7 +200007,7 @@ ${report3.ok ? "OK \u2014 data + serve plane reachable." : typeof report3.hint =
199321
200007
  // src/commands/guide.ts
199322
200008
  init_guides();
199323
200009
  init_cli_shared();
199324
- import { existsSync as existsSync93 } from "node:fs";
200010
+ import { existsSync as existsSync94 } from "node:fs";
199325
200011
  import { join as join101, resolve as resolve72 } from "node:path";
199326
200012
  async function runGuideCommand(command, pos, flags, io2, print) {
199327
200013
  const { referenceCache: referenceCache2, referenceIdentity: referenceIdentity2 } = await Promise.resolve().then(() => (init_guides(), guides_exports));
@@ -199549,7 +200235,7 @@ Seed your coding agent with \`himi agent-init\`. Guides: ${idx.portalUrl}`);
199549
200235
  ...res.removed.length ? { removed: res.removed, note: "older copies at the app root, moved to .himi/legacy-agent-files/ (a backup, in case one became app code) \u2014 the current ones live in .agents/skills/himalaya/" } : {},
199550
200236
  // In a git repo the refresh moves the diff identity an earlier `himi test` attested (that
199551
200237
  // hash is shared with the repo gates and deliberately exempts nothing but its own metadata).
199552
- ...existsSync93(join101(dir, ".himi", "attestations", "latest.json")) ? { next: "if `himi push` then refuses the attestation as stale, re-run `himi test` \u2014 in a git checkout this refresh changed the tree it covered" } : {},
200238
+ ...existsSync94(join101(dir, ".himi", "attestations", "latest.json")) ? { next: "if `himi push` then refuses the attestation as stale, re-run `himi test` \u2014 in a git checkout this refresh changed the tree it covered" } : {},
199553
200239
  ...res.skipped.length ? { skipped: res.skipped } : {},
199554
200240
  portalUrl: idx.portalUrl
199555
200241
  });
@@ -199617,7 +200303,7 @@ init_schema2();
199617
200303
  // src/cms.ts
199618
200304
  init_cms_schema();
199619
200305
  init_wix_owner();
199620
- import { existsSync as existsSync94, readFileSync as readFileSync112, writeFileSync as writeFileSync36 } from "node:fs";
200306
+ import { existsSync as existsSync95, readFileSync as readFileSync112, writeFileSync as writeFileSync36 } from "node:fs";
199621
200307
  import { join as join102 } from "node:path";
199622
200308
  var FOREIGN_DETAIL_LIMIT = 25;
199623
200309
  var MAX_COLLECTION_PAGES = 40;
@@ -200050,7 +200736,7 @@ async function seedCms(opts) {
200050
200736
  }
200051
200737
  function readSeedFile(dir) {
200052
200738
  const file2 = join102(dir, "cms-seed.json");
200053
- if (!existsSync94(file2)) return {};
200739
+ if (!existsSync95(file2)) return {};
200054
200740
  let parsed;
200055
200741
  try {
200056
200742
  parsed = JSON.parse(readFileSync112(file2, "utf8"));
@@ -200112,7 +200798,7 @@ async function writeSeedRows(opts) {
200112
200798
  };
200113
200799
  }
200114
200800
  let existing = {};
200115
- if (existsSync94(file2)) {
200801
+ if (existsSync95(file2)) {
200116
200802
  let parsed;
200117
200803
  try {
200118
200804
  parsed = JSON.parse(readFileSync112(file2, "utf8"));
@@ -200427,7 +201113,7 @@ async function cmsCommand(ctx) {
200427
201113
 
200428
201114
  // src/commands/base44.ts
200429
201115
  init_cli_shared();
200430
- import { existsSync as existsSync102 } from "node:fs";
201116
+ import { existsSync as existsSync103 } from "node:fs";
200431
201117
  import { join as join113, resolve as resolve77 } from "node:path";
200432
201118
  async function base44Command(ctx) {
200433
201119
  const { io: io2, pos, flags, print } = ctx;
@@ -200499,7 +201185,7 @@ async function base44Command(ctx) {
200499
201185
  return 1;
200500
201186
  };
200501
201187
  const gen = await Promise.resolve().then(() => (init_generate(), generate_exports));
200502
- const missing3 = gen.ENGINE_FILES.filter((f2) => !existsSync102(join113(dir, f2)));
201188
+ const missing3 = gen.ENGINE_FILES.filter((f2) => !existsSync103(join113(dir, f2)));
200503
201189
  if (missing3.length) {
200504
201190
  return fail6("content_dir_not_base44_starter", `${dir} is not a base44-starter content package: the generated screens import a shared engine it does not have (${missing3.join(", ")})`, { missing: missing3, hint: "scaffold one with `himi init <name> --template base44-starter --base44-app-id <id>`" });
200505
201191
  }
@@ -200585,7 +201271,7 @@ async function base44Command(ctx) {
200585
201271
  if (!provisioning || str3(flags.content) !== void 0 || !slug) return contentDir(flags);
200586
201272
  if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) return void 0;
200587
201273
  const dir = resolve77("apps", slug);
200588
- return existsSync102(join113(dir, "config.ts")) || existsSync102(join113(dir, "config.mjs")) ? dir : void 0;
201274
+ return existsSync103(join113(dir, "config.ts")) || existsSync103(join113(dir, "config.mjs")) ? dir : void 0;
200589
201275
  };
200590
201276
  if (sub === "entities" || sub === "functions") {
200591
201277
  if (sub === "entities" && !["query", "create", "seed"].includes(pos[2] ?? "")) {