@wix/himalaya-cli 0.1056.0 → 0.1057.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +1291 -1199
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -2168,462 +2168,1239 @@ var init_himi_report = __esm({
2168
2168
  }
2169
2169
  });
2170
2170
 
2171
- // ../../core/serve-storage/src/widget-identity.ts
2172
- function stableWidgets(widgets) {
2173
- if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return widgets;
2174
- return Object.fromEntries(Object.entries(widgets).map(([id, value]) => {
2175
- if (!value || typeof value !== "object" || Array.isArray(value)) return [id, value];
2176
- const { contentEtag: _regenerated, ...stable } = value;
2177
- return [id, stable];
2178
- }));
2179
- }
2180
- var init_widget_identity = __esm({
2181
- "../../core/serve-storage/src/widget-identity.ts"() {
2171
+ // ../ai-review-gate/paths.mjs
2172
+ import { resolve as resolve3, dirname as dirname3 } from "node:path";
2173
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2174
+ var REPO_ROOT;
2175
+ var init_paths = __esm({
2176
+ "../ai-review-gate/paths.mjs"() {
2177
+ REPO_ROOT = resolve3(dirname3(fileURLToPath2(import.meta.url)), "../..");
2182
2178
  }
2183
2179
  });
2184
2180
 
2185
- // ../../core/admin-web/src/utils/qr.ts
2186
- function rawDataModules(ver) {
2187
- let result = (16 * ver + 128) * ver + 64;
2188
- if (ver >= 2) {
2189
- const numAlign = Math.floor(ver / 7) + 2;
2190
- result -= (25 * numAlign - 10) * numAlign - 55;
2191
- if (ver >= 7) result -= 36;
2181
+ // ../ai-review-gate/review-diff.mjs
2182
+ var init_review_diff = __esm({
2183
+ "../ai-review-gate/review-diff.mjs"() {
2184
+ init_paths();
2192
2185
  }
2193
- return result;
2186
+ });
2187
+
2188
+ // ../pre-pr-review/command.mjs
2189
+ var SHELL_NAMES, SHELL_TOKEN;
2190
+ var init_command = __esm({
2191
+ "../pre-pr-review/command.mjs"() {
2192
+ SHELL_NAMES = "sh|bash|zsh|ksh|dash|ash|fish|csh|tcsh|pwsh|xonsh|nu";
2193
+ SHELL_TOKEN = new RegExp(`^(?:.*/)?(?:${SHELL_NAMES})$`);
2194
+ }
2195
+ });
2196
+
2197
+ // ../pre-pr-review/scope.mjs
2198
+ import { execFileSync } from "node:child_process";
2199
+ import { createHash } from "node:crypto";
2200
+ import { resolve as resolve4, dirname as dirname4, isAbsolute, join as join4 } from "node:path";
2201
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
2202
+ function resolveRepoRoot(cwd = process.cwd()) {
2203
+ let top;
2204
+ try {
2205
+ top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
2206
+ cwd,
2207
+ encoding: "utf8",
2208
+ stdio: ["ignore", "pipe", "pipe"]
2209
+ }).toString().trim();
2210
+ } catch (err) {
2211
+ throw new Error(`cannot resolve a repo root from ${cwd}: ${err?.message || err}`);
2212
+ }
2213
+ if (!top) throw new Error(`cannot resolve a repo root from ${cwd}: git named no toplevel`);
2214
+ return top;
2194
2215
  }
2195
- function byteCapacity(ver) {
2196
- const lenBits = ver < 10 ? 8 : 16;
2197
- return dataCodewords(ver) - Math.ceil((4 + lenBits) / 8);
2216
+ function git(args, opts = {}) {
2217
+ return execFileSync("git", args, {
2218
+ cwd: opts.cwd || REPO_ROOT2,
2219
+ encoding: "utf8",
2220
+ // A full PR diff can be megabytes; the default 1MB cap would truncate mid-hunk and the
2221
+ // delta parser would then read a malformed diff as "unsupported shape".
2222
+ maxBuffer: opts.maxBuffer ?? 256 * 1024 * 1024,
2223
+ stdio: ["ignore", "pipe", "pipe"]
2224
+ }).toString();
2198
2225
  }
2199
- function fitVersion(len) {
2200
- for (let v = 1; v <= QR_MAX_VERSION; v++) if (byteCapacity(v) >= len) return v;
2201
- return void 0;
2226
+ function diffHash(diff) {
2227
+ return createHash("sha256").update(diff).digest("hex");
2202
2228
  }
2203
- function rsDivisor(degree) {
2204
- const result = new Array(degree).fill(0);
2205
- result[degree - 1] = 1;
2206
- let root = 1;
2207
- for (let i = 0; i < degree; i++) {
2208
- for (let j = 0; j < degree; j++) {
2209
- result[j] = gfMul(result[j], root);
2210
- if (j + 1 < degree) result[j] ^= result[j + 1];
2211
- }
2212
- root = gfMul(root, 2);
2229
+ var REPO_ROOT2, BASE_REF;
2230
+ var init_scope = __esm({
2231
+ "../pre-pr-review/scope.mjs"() {
2232
+ init_paths();
2233
+ init_review_diff();
2234
+ init_command();
2235
+ REPO_ROOT2 = resolve4(dirname4(fileURLToPath3(import.meta.url)), "../..");
2236
+ BASE_REF = process.env.PRE_PR_REVIEW_BASE || "origin/master";
2213
2237
  }
2214
- return result;
2238
+ });
2239
+
2240
+ // ../verify-gate/diff-hash.mjs
2241
+ import {
2242
+ lstatSync as lstatSync2,
2243
+ mkdirSync as mkdirSync4,
2244
+ mkdtempSync,
2245
+ readFileSync as readFileSync4,
2246
+ readlinkSync,
2247
+ rmSync as rmSync2,
2248
+ writeFileSync as writeFileSync4
2249
+ } from "node:fs";
2250
+ import { createHash as createHash2 } from "node:crypto";
2251
+ import { tmpdir } from "node:os";
2252
+ import { basename, dirname as dirname5, join as join5, resolve as resolve5 } from "node:path";
2253
+ import { pathToFileURL } from "node:url";
2254
+ function isValidationMetadata(path2) {
2255
+ return VALIDATION_METADATA.has(path2) || /(^|[\\/])\.himi([\\/]|$)/.test(path2);
2215
2256
  }
2216
- function rsRemainder(data, degree) {
2217
- const divisor = rsDivisor(degree);
2218
- const res = new Array(degree).fill(0);
2219
- for (const b of data) {
2220
- const factor = b ^ res.shift();
2221
- res.push(0);
2222
- for (let i = 0; i < degree; i++) res[i] ^= gfMul(divisor[i], factor);
2257
+ function tryGit(args, root) {
2258
+ try {
2259
+ return git(args, { cwd: root });
2260
+ } catch {
2261
+ return null;
2223
2262
  }
2224
- return res;
2225
2263
  }
2226
- function encodeData(bytes2, ver) {
2227
- const bits = [];
2228
- const push = (val, len) => {
2229
- for (let i = len - 1; i >= 0; i--) bits.push(val >>> i & 1);
2230
- };
2231
- push(4, 4);
2232
- push(bytes2.length, ver < 10 ? 8 : 16);
2233
- for (const b of bytes2) push(b, 8);
2234
- const capacityBits = dataCodewords(ver) * 8;
2235
- push(0, Math.min(4, capacityBits - bits.length));
2236
- while (bits.length % 8 !== 0) bits.push(0);
2237
- const out = [];
2238
- for (let i = 0; i < bits.length; i += 8) {
2239
- let byte2 = 0;
2240
- for (let j = 0; j < 8; j++) byte2 = byte2 << 1 | bits[i + j];
2241
- out.push(byte2);
2264
+ function committedRecords(raw) {
2265
+ const fields = raw.split("\0");
2266
+ if (fields.length > 0 && fields[fields.length - 1] === "") fields.pop();
2267
+ if (fields.length % 2 !== 0) return null;
2268
+ const records = [];
2269
+ for (let i = 0; i < fields.length; i += 2) {
2270
+ const metadata2 = fields[i];
2271
+ const path2 = fields[i + 1];
2272
+ if (!metadata2.startsWith(":") || path2 === "") return null;
2273
+ if (isValidationMetadata(path2)) continue;
2274
+ records.push(`${Buffer.byteLength(path2)}:${path2}\0${metadata2}\0`);
2242
2275
  }
2243
- for (let pad2 = 236; out.length < dataCodewords(ver); pad2 ^= 236 ^ 17) out.push(pad2);
2244
- return out;
2276
+ return records.sort().join("");
2245
2277
  }
2246
- function interleave(data, ver) {
2247
- const numBlocks = NUM_BLOCKS_L[ver];
2248
- const eccLen = ECC_PER_BLOCK_L[ver];
2249
- const shortLen = Math.floor(totalCodewords(ver) / numBlocks) - eccLen;
2250
- const numShort = numBlocks - totalCodewords(ver) % numBlocks;
2251
- const blocks = [];
2252
- const eccs = [];
2253
- let off = 0;
2254
- for (let i = 0; i < numBlocks; i++) {
2255
- const len = shortLen + (i < numShort ? 0 : 1);
2256
- const blk = data.slice(off, off + len);
2257
- off += len;
2258
- blocks.push(blk);
2259
- eccs.push(rsRemainder(blk, eccLen));
2278
+ function committedIdentity(root) {
2279
+ const head = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root)?.trim();
2280
+ if (!head) return "unborn\0";
2281
+ const base = tryGit(["merge-base", BASE_REF, "HEAD"], root)?.trim();
2282
+ if (base) {
2283
+ const raw = tryGit(["diff", "--raw", "--no-renames", "-z", "--no-abbrev", base, "HEAD"], root);
2284
+ const records = raw === null ? null : committedRecords(raw);
2285
+ if (records !== null) return `base\0${base}\0${records}`;
2260
2286
  }
2261
- const out = [];
2262
- for (let i = 0; i < shortLen + 1; i++) {
2263
- for (let b = 0; b < numBlocks; b++) {
2264
- const v = blocks[b][i];
2265
- if (v !== void 0) out.push(v);
2266
- }
2287
+ const listing = tryGit(["ls-tree", "-rz", "--full-tree", "HEAD"], root);
2288
+ const tree = listing === null ? null : treeRecords(listing);
2289
+ return tree !== null ? `tree-listing\0${tree}` : `commit\0${head}\0`;
2290
+ }
2291
+ function treeRecords(listing) {
2292
+ const records = [];
2293
+ for (const record2 of listing.split("\0")) {
2294
+ if (record2 === "") continue;
2295
+ const tab = record2.indexOf(" ");
2296
+ if (tab <= 0 || tab === record2.length - 1) return null;
2297
+ const path2 = record2.slice(tab + 1);
2298
+ if (isValidationMetadata(path2)) continue;
2299
+ records.push(`${Buffer.byteLength(path2)}:${path2}\0${record2.slice(0, tab)}\0`);
2267
2300
  }
2268
- for (let i = 0; i < eccLen; i++) for (let b = 0; b < numBlocks; b++) out.push(eccs[b][i]);
2269
- return out;
2301
+ return records.sort().join("");
2270
2302
  }
2271
- function alignmentPositions(ver) {
2272
- if (ver === 1) return [];
2273
- const num3 = Math.floor(ver / 7) + 2;
2274
- const size = ver * 4 + 17;
2275
- const step = Math.ceil((ver * 4 + 4) / (num3 * 2 - 2)) * 2;
2276
- const res = [6];
2277
- for (let pos = size - 7; res.length < num3; pos -= step) res.splice(1, 0, pos);
2278
- return res;
2303
+ function treeEntries(output, index = false) {
2304
+ const entries = /* @__PURE__ */ new Map();
2305
+ for (const record2 of output.split("\0").filter(Boolean)) {
2306
+ const tab = record2.indexOf(" ");
2307
+ if (tab === -1) continue;
2308
+ const metadata2 = record2.slice(0, tab).split(" ");
2309
+ if (index && metadata2[2] !== "0") continue;
2310
+ entries.set(record2.slice(tab + 1), {
2311
+ mode: metadata2[0],
2312
+ oid: index ? metadata2[1] : metadata2[2]
2313
+ });
2314
+ }
2315
+ return entries;
2279
2316
  }
2280
- function bchFormat(data) {
2281
- let rem = data;
2282
- for (let i = 0; i < 10; i++) rem = rem << 1 ^ (rem >>> 9) * 1335;
2283
- return (data << 10 | rem) ^ 21522;
2317
+ function sameEntry(left, right) {
2318
+ if (!left || !right) return left === right;
2319
+ return left.mode === right.mode && left.oid === right.oid;
2284
2320
  }
2285
- function bchVersion(ver) {
2286
- let rem = ver;
2287
- for (let i = 0; i < 12; i++) rem = rem << 1 ^ (rem >>> 11) * 7973;
2288
- return ver << 12 | rem;
2321
+ function stagedIdentity(head, index, worktree) {
2322
+ if (sameEntry(index, head) || sameEntry(index, worktree)) return "";
2323
+ if (!index) return head ? "deleted" : "";
2324
+ return `${index.mode}:${index.oid}`;
2289
2325
  }
2290
- function penalty(m, size) {
2291
- let score = 0;
2292
- const run2 = (get) => {
2293
- for (let i = 0; i < size; i++) {
2294
- let last = get(i, 0), len = 1;
2295
- for (let j = 1; j < size; j++) {
2296
- const cur = get(i, j);
2297
- if (cur === last) {
2298
- len++;
2299
- if (len === 5) score += 3;
2300
- else if (len > 5) score++;
2301
- } else {
2302
- last = cur;
2303
- len = 1;
2304
- }
2305
- }
2306
- }
2307
- };
2308
- run2((i, j) => m[i][j]);
2309
- run2((i, j) => m[j][i]);
2310
- for (let y = 0; y < size - 1; y++)
2311
- for (let x = 0; x < size - 1; x++) {
2312
- const c = m[y][x];
2313
- if (c === m[y][x + 1] && c === m[y + 1][x] && c === m[y + 1][x + 1]) score += 3;
2314
- }
2315
- let dark = 0;
2316
- for (const row of m) for (const c of row) if (c) dark++;
2317
- const total = size * size;
2318
- score += Math.floor(Math.abs(dark * 20 - total * 10) / total) * 10;
2319
- return score;
2326
+ function blobOid(contents, algorithm) {
2327
+ const body = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
2328
+ return createHash2(algorithm).update(Buffer.from(`blob ${body.byteLength}\0`)).update(body).digest("hex");
2320
2329
  }
2321
- function encodeQr(text5) {
2322
- const bytes2 = [...new TextEncoder().encode(text5)];
2323
- const version2 = fitVersion(bytes2.length);
2324
- if (!version2) {
2325
- throw new RangeError(`QR payload too long: ${bytes2.length} bytes exceeds version ${QR_MAX_VERSION} at EC level L`);
2330
+ function validationDiffHash(cwd = process.cwd()) {
2331
+ const root = resolveRepoRoot(cwd);
2332
+ const born = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root) !== null;
2333
+ const staged = git(["diff", "--cached", "--name-only", "--no-renames", "-z", ...born ? ["HEAD"] : []], {
2334
+ cwd: root
2335
+ }).split("\0").filter(Boolean);
2336
+ const unstaged = git(["diff", "--name-only", "--no-renames", "-z"], { cwd: root }).split("\0").filter(Boolean);
2337
+ const untracked = git(["ls-files", "--others", "--exclude-standard", "-z"], {
2338
+ cwd: root
2339
+ }).split("\0").filter(Boolean);
2340
+ const headEntries = born ? treeEntries(git(["ls-tree", "-rz", "--full-tree", "HEAD"], { cwd: root })) : /* @__PURE__ */ new Map();
2341
+ const indexEntries = treeEntries(git(["ls-files", "--stage", "-z"], { cwd: root }), true);
2342
+ const objectAlgorithm = git(["rev-parse", "--show-object-format"], { cwd: root }).trim();
2343
+ let trustFilesystemMode = true;
2344
+ try {
2345
+ trustFilesystemMode = git(["config", "--bool", "--get", "core.fileMode"], { cwd: root }).trim() !== "false";
2346
+ } catch {
2326
2347
  }
2327
- const codewords = interleave(encodeData(bytes2, version2), version2);
2328
- const size = version2 * 4 + 17;
2329
- const modules = Array.from({ length: size }, () => new Array(size).fill(false));
2330
- const reserved = Array.from({ length: size }, () => new Array(size).fill(false));
2331
- const set = (x, y, dark) => {
2332
- modules[y][x] = dark;
2333
- reserved[y][x] = true;
2334
- };
2335
- const finder = (cx, cy) => {
2336
- for (let dy = -4; dy <= 4; dy++)
2337
- for (let dx = -4; dx <= 4; dx++) {
2338
- const x = cx + dx, y = cy + dy;
2339
- if (x < 0 || y < 0 || x >= size || y >= size) continue;
2340
- const d = Math.max(Math.abs(dx), Math.abs(dy));
2341
- set(x, y, d !== 2 && d !== 4);
2348
+ const paths = [.../* @__PURE__ */ new Set([...staged, ...unstaged, ...untracked])].filter((path2) => !isValidationMetadata(path2)).sort();
2349
+ const identity = paths.map((path2) => {
2350
+ const absolute = resolve5(root, path2);
2351
+ const headEntry = headEntries.get(path2);
2352
+ const indexEntry = indexEntries.get(path2);
2353
+ const deletedRecord = () => [`${Buffer.byteLength(path2)}:${path2}`, "deleted", stagedIdentity(headEntry, indexEntry, void 0), ""].join("\0");
2354
+ let stat2;
2355
+ try {
2356
+ stat2 = lstatSync2(absolute);
2357
+ } catch (error) {
2358
+ if (error?.code === "ENOENT") return deletedRecord();
2359
+ throw error;
2360
+ }
2361
+ try {
2362
+ if (stat2.isSymbolicLink()) {
2363
+ const target = readlinkSync(absolute);
2364
+ const worktreeEntry = { mode: "120000", oid: blobOid(target, objectAlgorithm) };
2365
+ const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
2366
+ return [
2367
+ `${Buffer.byteLength(path2)}:${path2}`,
2368
+ "symlink",
2369
+ "120000",
2370
+ diffHash(target),
2371
+ stagedState2,
2372
+ ""
2373
+ ].join("\0");
2342
2374
  }
2343
- };
2344
- finder(3, 3);
2345
- finder(size - 4, 3);
2346
- finder(3, size - 4);
2347
- for (let i = 8; i < size - 8; i++) {
2348
- set(i, 6, i % 2 === 0);
2349
- set(6, i, i % 2 === 0);
2350
- }
2351
- for (const ay of alignmentPositions(version2))
2352
- for (const ax of alignmentPositions(version2)) {
2353
- if (ax === 6 && ay === 6 || ax === 6 && ay === size - 7 || ax === size - 7 && ay === 6) continue;
2354
- for (let dy = -2; dy <= 2; dy++)
2355
- for (let dx = -2; dx <= 2; dx++) set(ax + dx, ay + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
2375
+ if (stat2.isFile()) {
2376
+ const filesystemMode = stat2.mode & 73 ? "100755" : "100644";
2377
+ const worktreeMode = trustFilesystemMode ? filesystemMode : indexEntry?.mode ?? headEntry?.mode ?? "100644";
2378
+ const worktreeOid = git(["hash-object", `--path=${path2}`, "--", path2], { cwd: root }).trim();
2379
+ const worktreeEntry = { mode: worktreeMode, oid: worktreeOid };
2380
+ const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
2381
+ return [
2382
+ `${Buffer.byteLength(path2)}:${path2}`,
2383
+ "file",
2384
+ worktreeMode,
2385
+ diffHash(readFileSync4(absolute)),
2386
+ stagedState2,
2387
+ ""
2388
+ ].join("\0");
2389
+ }
2390
+ } catch (error) {
2391
+ try {
2392
+ lstatSync2(absolute);
2393
+ } catch (recheck) {
2394
+ if (recheck?.code === "ENOENT") return deletedRecord();
2395
+ }
2396
+ throw error;
2356
2397
  }
2357
- for (let i = 0; i < 9; i++) {
2358
- reserved[i][8] = true;
2359
- reserved[8][i] = true;
2360
- }
2361
- for (let i = 0; i < 8; i++) {
2362
- reserved[size - 1 - i][8] = true;
2363
- reserved[8][size - 1 - i] = true;
2364
- }
2365
- set(8, size - 8, true);
2366
- if (version2 >= 7)
2367
- for (let i = 0; i < 18; i++) {
2368
- const a = size - 11 + i % 3, b = Math.floor(i / 3);
2369
- reserved[a][b] = true;
2370
- reserved[b][a] = true;
2398
+ const type = stat2.isDirectory() ? "directory" : "other";
2399
+ const stagedState = stagedIdentity(headEntry, indexEntry, void 0);
2400
+ return [
2401
+ `${Buffer.byteLength(path2)}:${path2}`,
2402
+ type,
2403
+ stat2.mode.toString(8),
2404
+ stagedState,
2405
+ ""
2406
+ ].join("\0");
2407
+ }).join("");
2408
+ const committed = committedIdentity(root);
2409
+ return diffHash(
2410
+ `validation-change-v3\0${Buffer.byteLength(committed)}\0${committed}${identity}`
2411
+ );
2412
+ }
2413
+ function seed(repo, commands) {
2414
+ for (const args of commands) git(args, { cwd: repo });
2415
+ }
2416
+ function write(repo, rel, body) {
2417
+ const absolute = resolve5(repo, rel);
2418
+ mkdirSync4(dirname5(absolute), { recursive: true });
2419
+ writeFileSync4(absolute, body);
2420
+ }
2421
+ function selfTestRepo({ withBase }) {
2422
+ const repo = mkdtempSync(join5(tmpdir(), "diff-hash-self-test-"));
2423
+ seed(repo, [
2424
+ ["init", "-q", repo],
2425
+ ["-C", repo, "config", "user.email", "self-test@test.invalid"],
2426
+ ["-C", repo, "config", "user.name", "diff-hash-self-test"]
2427
+ ]);
2428
+ write(repo, "src/base.txt", "base\n");
2429
+ seed(repo, [["-C", repo, "add", "-A"], ["-C", repo, "commit", "-qm", "base"]]);
2430
+ const base = git(["-C", repo, "rev-parse", "HEAD"], { cwd: repo }).trim();
2431
+ if (withBase) seed(repo, [["-C", repo, "update-ref", "refs/remotes/origin/master", base]]);
2432
+ return { repo, base };
2433
+ }
2434
+ function commitChange(repo, base, branch, rel, body) {
2435
+ seed(repo, [["-C", repo, "checkout", "-q", base], ["-C", repo, "checkout", "-q", "-b", branch]]);
2436
+ write(repo, rel, body);
2437
+ seed(repo, [["-C", repo, "add", rel], ["-C", repo, "commit", "-qm", `commit ${rel}`]]);
2438
+ return validationDiffHash(repo);
2439
+ }
2440
+ function runSelfTest() {
2441
+ let failed2 = 0;
2442
+ for (const testCase of SELF_TEST_CASES) {
2443
+ let ok = false;
2444
+ let thrown = null;
2445
+ try {
2446
+ ok = testCase.run() === true;
2447
+ } catch (error) {
2448
+ thrown = error?.message || String(error);
2371
2449
  }
2372
- let bit = 0;
2373
- const totalBits = codewords.length * 8;
2374
- for (let right = size - 1; right >= 1; right -= 2) {
2375
- if (right === 6) right = 5;
2376
- for (let vert = 0; vert < size; vert++) {
2377
- for (let j = 0; j < 2; j++) {
2378
- const x = right - j;
2379
- const upward = (right + 1 & 2) === 0;
2380
- const y = upward ? size - 1 - vert : vert;
2381
- if (reserved[y][x]) continue;
2382
- modules[y][x] = bit < totalBits ? (codewords[bit >>> 3] >>> 7 - (bit & 7) & 1) === 1 : false;
2383
- bit++;
2384
- }
2450
+ if (ok) {
2451
+ process.stdout.write(` \u2713 ${testCase.name}
2452
+ `);
2453
+ continue;
2385
2454
  }
2455
+ failed2++;
2456
+ process.stderr.write(` \u2717 ${testCase.name}
2457
+ ${thrown ?? testCase.reason}
2458
+ `);
2386
2459
  }
2387
- let best = null, bestMask = 0, bestScore = Infinity;
2388
- for (let mask = 0; mask < 8; mask++) {
2389
- const cand = modules.map((row) => [...row]);
2390
- for (let y = 0; y < size; y++)
2391
- for (let x = 0; x < size; x++) if (!reserved[y][x] && MASKS[mask](x, y)) cand[y][x] = !cand[y][x];
2392
- applyFormat(cand, size, mask);
2393
- if (version2 >= 7) applyVersion(cand, size, version2);
2394
- const s = penalty(cand, size);
2395
- if (s < bestScore) {
2396
- bestScore = s;
2397
- best = cand;
2398
- bestMask = mask;
2460
+ return failed2;
2461
+ }
2462
+ var VALIDATION_METADATA, SELF_TEST_CASES;
2463
+ var init_diff_hash = __esm({
2464
+ "../verify-gate/diff-hash.mjs"() {
2465
+ init_scope();
2466
+ VALIDATION_METADATA = /* @__PURE__ */ new Set([
2467
+ ".claude/.validation-baseline.json",
2468
+ ".claude/.validation-evidence.json"
2469
+ ]);
2470
+ SELF_TEST_CASES = [
2471
+ {
2472
+ name: "committed diffs are distinguished (the #1831 regression)",
2473
+ reason: "two unrelated COMMITTED diffs share one identity \u2014 a record for either would satisfy the gate for the other",
2474
+ run: () => {
2475
+ const { repo, base } = selfTestRepo({ withBase: true });
2476
+ try {
2477
+ return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
2478
+ } finally {
2479
+ rmSync2(repo, { recursive: true, force: true });
2480
+ }
2481
+ }
2482
+ },
2483
+ {
2484
+ name: "committed diffs are distinguished with NO base ref (fallback ladder)",
2485
+ reason: "without origin/master the committed identity collapses to a constant",
2486
+ run: () => {
2487
+ const { repo, base } = selfTestRepo({ withBase: false });
2488
+ try {
2489
+ return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
2490
+ } finally {
2491
+ rmSync2(repo, { recursive: true, force: true });
2492
+ }
2493
+ }
2494
+ },
2495
+ {
2496
+ name: "a clean tree is not the digest of an empty path list",
2497
+ reason: 'a clean tree still hashes to sha256("validation-worktree-v2\\0"), the constant measured in #1831',
2498
+ run: () => {
2499
+ const { repo } = selfTestRepo({ withBase: true });
2500
+ try {
2501
+ return validationDiffHash(repo) !== createHash2("sha256").update("validation-worktree-v2\0").digest("hex");
2502
+ } finally {
2503
+ rmSync2(repo, { recursive: true, force: true });
2504
+ }
2505
+ }
2506
+ },
2507
+ {
2508
+ name: "an uncommitted edit still changes the identity",
2509
+ reason: "the working-tree half of the identity stopped responding to edits",
2510
+ run: () => {
2511
+ const { repo } = selfTestRepo({ withBase: true });
2512
+ try {
2513
+ const before = validationDiffHash(repo);
2514
+ write(repo, "src/base.txt", "edited in the working tree\n");
2515
+ return validationDiffHash(repo) !== before;
2516
+ } finally {
2517
+ rmSync2(repo, { recursive: true, force: true });
2518
+ }
2519
+ }
2520
+ },
2521
+ {
2522
+ // The "can pass" half. Without it every case above is satisfied by a hash that simply
2523
+ // changes every call, which would identify nothing and invalidate every record.
2524
+ name: "one unchanged state hashes to one value",
2525
+ reason: "the identity is not stable for an unchanged state \u2014 every record would read stale",
2526
+ run: () => {
2527
+ const { repo } = selfTestRepo({ withBase: true });
2528
+ try {
2529
+ write(repo, "src/base.txt", "edited in the working tree\n");
2530
+ return validationDiffHash(repo) === validationDiffHash(repo);
2531
+ } finally {
2532
+ rmSync2(repo, { recursive: true, force: true });
2533
+ }
2534
+ }
2535
+ }
2536
+ ];
2537
+ if (process.argv[1] && basename(process.argv[1]) === "diff-hash.mjs" && import.meta.url === pathToFileURL(process.argv[1]).href) {
2538
+ if (process.argv.slice(2).includes("--self-test")) {
2539
+ process.stdout.write("\u25B8 diff-hash --self-test: proving the identity can change, and can hold\n");
2540
+ const failed2 = runSelfTest();
2541
+ if (failed2) {
2542
+ process.stderr.write(
2543
+ `\u2717 diff-hash self-test FAILED (${failed2} case(s)) \u2014 the evidence record's diff identity cannot be trusted
2544
+ `
2545
+ );
2546
+ process.exit(1);
2547
+ }
2548
+ process.stdout.write(`\u2713 diff-hash self-test passed (${SELF_TEST_CASES.length} cases)
2549
+ `);
2550
+ } else {
2551
+ process.stdout.write(`${validationDiffHash()}
2552
+ `);
2553
+ }
2399
2554
  }
2400
2555
  }
2401
- return { size, version: version2, mask: bestMask, modules: best };
2556
+ });
2557
+
2558
+ // ../../core/serve-storage/src/widget-identity.ts
2559
+ function stableWidgets(widgets) {
2560
+ if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return widgets;
2561
+ return Object.fromEntries(Object.entries(widgets).map(([id, value]) => {
2562
+ if (!value || typeof value !== "object" || Array.isArray(value)) return [id, value];
2563
+ const { contentEtag: _regenerated, ...stable } = value;
2564
+ return [id, stable];
2565
+ }));
2402
2566
  }
2403
- function applyFormat(m, size, mask) {
2404
- const bits = bchFormat(1 << 3 | mask);
2405
- const on3 = (i) => (bits >>> i & 1) === 1;
2406
- const put = (x, y, dark) => {
2407
- m[y][x] = dark;
2408
- };
2409
- for (let i = 0; i <= 5; i++) put(8, i, on3(i));
2410
- put(8, 7, on3(6));
2411
- put(8, 8, on3(7));
2412
- put(7, 8, on3(8));
2413
- for (let i = 9; i < 15; i++) put(14 - i, 8, on3(i));
2414
- for (let i = 0; i < 8; i++) put(size - 1 - i, 8, on3(i));
2415
- for (let i = 8; i < 15; i++) put(8, size - 15 + i, on3(i));
2416
- put(8, size - 8, true);
2417
- }
2418
- function applyVersion(m, size, version2) {
2419
- const bits = bchVersion(version2);
2420
- for (let i = 0; i < 18; i++) {
2421
- const on3 = (bits >>> i & 1) === 1;
2422
- const a = size - 11 + i % 3, b = Math.floor(i / 3);
2423
- m[a][b] = on3;
2424
- m[b][a] = on3;
2425
- }
2426
- }
2427
- var ECC_PER_BLOCK_L, NUM_BLOCKS_L, QR_MAX_VERSION, totalCodewords, dataCodewords, EXP, LOG, gfMul, MASKS;
2428
- var init_qr = __esm({
2429
- "../../core/admin-web/src/utils/qr.ts"() {
2430
- ECC_PER_BLOCK_L = [0, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28];
2431
- NUM_BLOCKS_L = [0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8];
2432
- QR_MAX_VERSION = 20;
2433
- totalCodewords = (ver) => Math.floor(rawDataModules(ver) / 8);
2434
- dataCodewords = (ver) => totalCodewords(ver) - ECC_PER_BLOCK_L[ver] * NUM_BLOCKS_L[ver];
2435
- EXP = new Uint8Array(512);
2436
- LOG = new Uint8Array(256);
2437
- (() => {
2438
- let x = 1;
2439
- for (let i = 0; i < 255; i++) {
2440
- EXP[i] = x;
2441
- LOG[x] = i;
2442
- x <<= 1;
2443
- if (x & 256) x ^= 285;
2444
- }
2445
- for (let i = 255; i < 512; i++) EXP[i] = EXP[i - 255];
2446
- })();
2447
- gfMul = (a, b) => a === 0 || b === 0 ? 0 : EXP[LOG[a] + LOG[b]];
2448
- MASKS = [
2449
- (x, y) => (x + y) % 2 === 0,
2450
- (_x, y) => y % 2 === 0,
2451
- (x) => x % 3 === 0,
2452
- (x, y) => (x + y) % 3 === 0,
2453
- (x, y) => (Math.floor(y / 2) + Math.floor(x / 3)) % 2 === 0,
2454
- (x, y) => x * y % 2 + x * y % 3 === 0,
2455
- (x, y) => (x * y % 2 + x * y % 3) % 2 === 0,
2456
- (x, y) => ((x + y) % 2 + x * y % 3) % 2 === 0
2457
- ];
2567
+ var init_widget_identity = __esm({
2568
+ "../../core/serve-storage/src/widget-identity.ts"() {
2458
2569
  }
2459
2570
  });
2460
2571
 
2461
- // ../../core/dev-server/src/wix-placeholders.ts
2462
- var PLACEHOLDER_CLIENT_ID;
2463
- var init_wix_placeholders = __esm({
2464
- "../../core/dev-server/src/wix-placeholders.ts"() {
2465
- PLACEHOLDER_CLIENT_ID = "YOUR_WIX_CLIENT_ID";
2572
+ // src/attestation.ts
2573
+ import { execFileSync as execFileSync2 } from "node:child_process";
2574
+ import { createHash as createHash3, randomBytes } from "node:crypto";
2575
+ import {
2576
+ existsSync as existsSync3,
2577
+ lstatSync as lstatSync3,
2578
+ mkdirSync as mkdirSync5,
2579
+ realpathSync,
2580
+ readlinkSync as readlinkSync2,
2581
+ readFileSync as readFileSync5,
2582
+ readdirSync as readdirSync3,
2583
+ unlinkSync,
2584
+ writeFileSync as writeFileSync5
2585
+ } from "node:fs";
2586
+ import { basename as basename2, join as join6, relative, resolve as resolve6 } from "node:path";
2587
+ function attestationVersionOf(value) {
2588
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2589
+ const version2 = value.attestation;
2590
+ return typeof version2 === "number" && Number.isInteger(version2) && version2 > 0 ? version2 : null;
2591
+ }
2592
+ function classifyRetainedRun(dir, name) {
2593
+ let value;
2594
+ try {
2595
+ value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
2596
+ } catch {
2597
+ return { kind: "unreadable" };
2466
2598
  }
2467
- });
2468
-
2469
- // ../../core/ts/src/widget-vocabulary.ts
2470
- function isComponentAllowedForFamily(key2, family) {
2471
- return WIDGET_VOCABULARY[key2]?.[family] === true;
2599
+ if (isAttestationV2(value)) return { kind: "current", run: value };
2600
+ const version2 = attestationVersionOf(value);
2601
+ if (version2 === null || version2 === ATTESTATION_VERSION) return { kind: "malformed" };
2602
+ return { kind: version2 < ATTESTATION_VERSION ? "legacy" : "future" };
2472
2603
  }
2473
- var WIDGET_VOCABULARY, WIDGET_COMPONENTS;
2474
- var init_widget_vocabulary = __esm({
2475
- "../../core/ts/src/widget-vocabulary.ts"() {
2476
- WIDGET_VOCABULARY = {
2477
- "himalaya.VStack": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: true },
2478
- "himalaya.HStack": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: true },
2479
- "himalaya.ZStack": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
2480
- "himalaya.Card": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true },
2481
- "himalaya.Text": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
2482
- "himalaya.Image": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: true },
2483
- "himalaya.Icon": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
2484
- "himalaya.Divider": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: false },
2485
- "himalaya.Spacer": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
2486
- "himalaya.Button": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: false },
2487
- "himalaya.ProgressBar": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
2488
- "himalaya.Gauge": { ios_home: true, ios_lock: true, android_home: false, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
2489
- // Line/area/bar sparkline. No Glance Canvas → android_home:false (degrades), like Gauge.
2490
- "himalaya.Chart": { ios_home: true, ios_lock: true, android_home: false, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
2491
- "himalaya.Label": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: false },
2492
- "himalaya.Chip": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: false },
2493
- "himalaya.ListItem": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true },
2494
- "himalaya.LazyList": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true },
2495
- // ── RFC 0052 temporal vocabulary: a DECLARED divergence, not a gap ──────────
2496
- // A 42-cell month grid and an hour-gutter day timetable are not "too big for a
2497
- // widget" — they are the wrong SHAPE for one, and both are interactive (month
2498
- // paging, tap-to-select, drag-to-reschedule) where a widget snapshot is frozen.
2499
- // Shrinking either produces something illegible rather than something smaller,
2500
- // so they are excluded from EVERY family, deliberately.
2501
- "himalaya.MonthCalendar": { ios_home: false, ios_lock: false, android_home: false, web_card: false, ios_live_activity: false, watch_complication: false, car_list: false },
2502
- "himalaya.TimeGrid": { ios_home: false, ios_lock: false, android_home: false, web_card: false, ios_live_activity: false, watch_complication: false, car_list: false },
2503
- // Agenda IS admitted: it is list-shaped, so it degrades to "the next few things"
2504
- // exactly the way LazyList/ListItem do — and "what's next today" is the single
2505
- // most useful widget a calendar can produce. Its row mirrors LazyList's: the
2506
- // accessory surfaces (lock / Live Activity / complication) drop lists entirely.
2507
- "himalaya.Agenda": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true }
2508
- };
2509
- WIDGET_COMPONENTS = new Set(Object.keys(WIDGET_VOCABULARY));
2604
+ function screenshotArtifactPath(contentDir2, hash) {
2605
+ return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
2606
+ }
2607
+ function canonical(value) {
2608
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
2609
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
2610
+ const record2 = value;
2611
+ return `{${Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${canonical(record2[key2])}`).join(",")}}`;
2612
+ }
2613
+ function releasePayloadHash(payload) {
2614
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha2562(canonical(payload));
2615
+ const record2 = payload;
2616
+ const manifest = record2.manifest;
2617
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha2562(canonical(payload));
2618
+ const manifestRecord = manifest;
2619
+ const widgets = manifestRecord.widgets;
2620
+ if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha2562(canonical(payload));
2621
+ return sha2562(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
2622
+ }
2623
+ function withoutSelfHash(attestation) {
2624
+ const { selfHash: _selfHash, ...body } = attestation;
2625
+ return body;
2626
+ }
2627
+ function selfHashFor(attestation) {
2628
+ return sha2562(canonical(withoutSelfHash(attestation)));
2629
+ }
2630
+ function repoRoot(contentDir2) {
2631
+ try {
2632
+ const root = execFileSync2("git", ["-C", contentDir2, "rev-parse", "--show-toplevel"], {
2633
+ encoding: "utf8",
2634
+ stdio: ["ignore", "pipe", "ignore"]
2635
+ }).trim();
2636
+ if (!root) return null;
2637
+ execFileSync2("git", ["-C", root, "rev-parse", "--verify", "--quiet", "HEAD^{commit}"], { stdio: "ignore" });
2638
+ return root;
2639
+ } catch {
2640
+ return null;
2510
2641
  }
2511
- });
2512
-
2513
- // ../../core/ts/src/preview-surfaces.ts
2514
- function surfaceById(id) {
2515
- return PREVIEW_SURFACES.find((s) => s.id === id);
2516
2642
  }
2517
- function surfaceFrozenFamily(surface) {
2518
- const gate = surface.gate;
2519
- return gate.kind === "family" || gate.kind === "projection" ? gate.family : void 0;
2643
+ function storedContentDir(contentDir2) {
2644
+ const absolute = resolve6(contentDir2);
2645
+ const root = repoRoot(absolute);
2646
+ return root ? relative(root, absolute) || "." : ".";
2520
2647
  }
2521
- function isWidgetFamilySurface(surface) {
2522
- return surface.gate.kind === "family";
2648
+ function currentGitDiffHash(contentDir2) {
2649
+ const root = repoRoot(resolve6(contentDir2));
2650
+ if (!root) return "unavailable";
2651
+ return validationDiffHash(root);
2523
2652
  }
2524
- function surfaceRenderMode(surface) {
2525
- const { kind } = surface.gate;
2526
- return kind === "family" || kind === "projection" ? "frozen" : "interactive";
2653
+ function currentGitUntrackedHash(contentDir2) {
2654
+ const unresolved = resolve6(contentDir2);
2655
+ const absolute = realpathSync.native(unresolved);
2656
+ const root = repoRoot(absolute);
2657
+ if (!root) return sourceTreeHash(absolute);
2658
+ const scope = relative(root, absolute) || ".";
2659
+ const listed = execFileSync2("git", ["-C", root, "ls-files", "--others", "--exclude-standard", "-z", "--", scope], {
2660
+ encoding: "utf8",
2661
+ maxBuffer: GIT_HASH_MAX_BUFFER
2662
+ });
2663
+ const hash = createHash3("sha256");
2664
+ for (const path2 of listed.split("\0").filter(Boolean).sort()) {
2665
+ const local = relative(absolute, join6(root, path2));
2666
+ const segs = local.split(/[\\/]/);
2667
+ if (NOT_ATTESTED_DIRS.has(segs[0])) continue;
2668
+ if (segs.length === 1 && AGENT_TOOLING_ROOT_FILES.includes(segs[0])) continue;
2669
+ const full = join6(root, path2);
2670
+ const stats = lstatSync3(full, { throwIfNoEntry: false });
2671
+ if (!stats) continue;
2672
+ const kind = stats.isSymbolicLink() ? "link" : stats.isDirectory() ? "dir" : stats.isFile() ? "file" : null;
2673
+ if (!kind) continue;
2674
+ const payload = kind === "link" ? Buffer.from(readlinkSync2(full)) : kind === "file" ? readFileSync5(full) : Buffer.alloc(0);
2675
+ const pathBytes = Buffer.from(path2);
2676
+ hash.update(`${kind} ${pathBytes.length} ${payload.length}
2677
+ `).update(pathBytes).update(payload);
2678
+ }
2679
+ return hash.digest("hex");
2527
2680
  }
2528
- function isViewTarget(key2) {
2529
- return SURFACE_GROUPS.includes(key2) || surfaceById(key2) !== void 0;
2681
+ function sourceTreeHash(root) {
2682
+ const hash = createHash3("sha256");
2683
+ const visit = (dir) => {
2684
+ for (const entry of readdirSync3(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
2685
+ if (entry.isDirectory() && (entry.name === ".git" || entry.name === "node_modules" || NOT_ATTESTED_DIRS.has(entry.name))) continue;
2686
+ const path2 = join6(dir, entry.name);
2687
+ const local = relative(root, path2);
2688
+ if (dir === root && entry.isFile() && AGENT_TOOLING_ROOT_FILES.includes(entry.name)) continue;
2689
+ if (entry.isDirectory()) visit(path2);
2690
+ else if (entry.isFile()) hash.update(local).update("\0").update(readFileSync5(path2)).update("\0");
2691
+ else if (entry.isSymbolicLink()) hash.update(local).update("\0link\0").update(readlinkSync2(path2)).update("\0");
2692
+ }
2693
+ };
2694
+ visit(root);
2695
+ return hash.digest("hex");
2530
2696
  }
2531
- function viewTargetRenderMode(target) {
2532
- if (target === "frozen") return "frozen";
2533
- if (SURFACE_GROUPS.includes(target)) return "interactive";
2534
- const surface = surfaceById(target);
2535
- return surface ? surfaceRenderMode(surface) : void 0;
2697
+ function isAttestationV2(value) {
2698
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2699
+ const a = value;
2700
+ const coverage = a.coverage;
2701
+ const report2 = a.report;
2702
+ return a.attestation === 2 && typeof a.runId === "string" && ["himi-test", "himi-render", "himi-shots"].includes(a.tool) && [a.app, a.contentDir, a.gitDiffHash, a.gitUntrackedHash, a.releaseId, a.releaseHash, a.selfHash].every((item) => typeof item === "string" && item.length > 0) && stringRecord(a.descriptorHashes) && stringRecord(a.screenshots) && !!coverage && [coverage.screens, coverage.actionsDispatched, coverage.uncoveredFns, coverage.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && typeof report2?.ok === "boolean" && !!report2.counts && [report2.counts.errors, report2.counts.warnings, report2.counts.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && (a.prev === null || typeof a.prev === "string");
2536
2703
  }
2537
- function resolveSurfaceViewKey(surfaceId, registered) {
2538
- const ladder = SURFACE_VIEW_LADDER[surfaceId];
2539
- if (!ladder) return void 0;
2540
- const keys = registered instanceof Set ? registered : new Set(registered);
2541
- return ladder.find((rung) => keys.has(rung));
2704
+ function readLatestAttestation(contentDir2) {
2705
+ try {
2706
+ const parsed = JSON.parse(readFileSync5(join6(resolve6(contentDir2), ".himi", "attestations", "latest.json"), "utf8"));
2707
+ return isAttestationV2(parsed) ? parsed : null;
2708
+ } catch {
2709
+ return null;
2710
+ }
2542
2711
  }
2543
- function isTargeted(surface, platforms) {
2544
- if (surface.platformKey === null) return true;
2545
- if (surface.platformKey === "macos") return platforms.macos !== false;
2546
- return platforms[surface.platformKey] === true;
2712
+ function readRetainedAttestations(contentDir2) {
2713
+ const dir = join6(resolve6(contentDir2), ".himi", "attestations");
2714
+ if (!existsSync3(dir)) return { runs: [], errors: [] };
2715
+ try {
2716
+ const names = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
2717
+ const parsedRuns = [];
2718
+ const errors = [];
2719
+ for (const name of names) {
2720
+ const classified = classifyRetainedRun(dir, name);
2721
+ if (classified.kind === "current") parsedRuns.push(classified.run);
2722
+ else if (classified.kind === "legacy") {
2723
+ errors.push(`attestation history contains retained run ${name} from an older attestation version \u2014 re-run himi test/render to start a v${ATTESTATION_VERSION} chain`);
2724
+ } else if (classified.kind === "future") {
2725
+ errors.push(`attestation history contains retained run ${name} from a newer attestation version \u2014 upgrade himi rather than deleting it`);
2726
+ } else if (classified.kind === "unreadable") {
2727
+ errors.push(`attestation history contains unreadable retained run ${name}`);
2728
+ } else errors.push(`attestation history contains malformed retained run ${name}`);
2729
+ }
2730
+ const latest = readLatestAttestation(contentDir2);
2731
+ if (existsSync3(join6(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
2732
+ else if (names.length > 0 && !latest) errors.push("attestation history has no latest.json");
2733
+ const byHash = new Map(parsedRuns.map((run2) => [run2.selfHash, run2]));
2734
+ if (byHash.size !== parsedRuns.length) errors.push("attestation history contains duplicate self hashes");
2735
+ for (const run2 of parsedRuns) {
2736
+ if (selfHashFor(run2) !== run2.selfHash) errors.push(`selfHash does not match retained run ${run2.runId}`);
2737
+ for (const expected of Object.values(run2.screenshots)) {
2738
+ const path2 = screenshotArtifactPath(contentDir2, expected);
2739
+ if (!existsSync3(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename2(path2)}`);
2740
+ else if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
2741
+ }
2742
+ }
2743
+ const runs = [];
2744
+ const seen = /* @__PURE__ */ new Set();
2745
+ let cursor = latest ?? void 0;
2746
+ while (cursor && !seen.has(cursor.selfHash)) {
2747
+ runs.push(cursor);
2748
+ seen.add(cursor.selfHash);
2749
+ cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
2750
+ }
2751
+ if (latest && !byHash.has(latest.selfHash)) errors.push("latest.json has no matching retained run");
2752
+ if (seen.size !== parsedRuns.length) errors.push("attestation hash chain does not reach every retained run");
2753
+ runs.push(...parsedRuns.filter((run2) => !seen.has(run2.selfHash)).sort((a, b) => a.runId < b.runId ? 1 : -1));
2754
+ return {
2755
+ runs,
2756
+ errors
2757
+ };
2758
+ } catch {
2759
+ return { runs: [], errors: ["attestation history could not be read"] };
2760
+ }
2547
2761
  }
2548
- function surfacesForApp(platforms, producedFamilies) {
2549
- const produced = producedFamilies === void 0 ? void 0 : producedFamilies instanceof Set ? producedFamilies : new Set(producedFamilies);
2550
- return PREVIEW_SURFACES.map((surface) => {
2551
- const inEnvelope = isTargeted(surface, platforms);
2552
- const family = isWidgetFamilySurface(surface) ? surfaceFrozenFamily(surface) : void 0;
2553
- if (produced && family !== void 0) {
2554
- const isProduced = produced.has(family);
2555
- return inEnvelope && isProduced ? { surface, targeted: true, produced: true } : {
2556
- surface,
2557
- targeted: false,
2558
- produced: isProduced,
2559
- // The four native families carry no platform key, so there is nothing to
2560
- // offer; `web_card` and `watch_complication` DO have one, and when that is
2561
- // the half that is missing it is still the right advice. Hence the guard —
2562
- // it is load-bearing for two families, not defensive padding.
2563
- ...inEnvelope ? {} : surface.platformKey ? { optIn: `"${surface.platformKey}": true` } : {},
2564
- ...isProduced ? {} : {
2565
- // The rebuild half is not padding: the produced set is read off BUILT
2566
- // bundles, so an author who has just written the registration and not
2567
- // rebuilt lands here too, and "write defineWidget" alone would be advice
2568
- // to write the code they are looking at.
2569
- needs: `defineWidget({ family: "${family}", \u2026 }) in a Tier-5 bundle \u2014 and a rebuild, if you just added it`
2570
- }
2571
- };
2762
+ function selectAttestationForPush(runs) {
2763
+ const newestBehavioral = runs.find((run2) => run2.tool === "himi-test");
2764
+ if (!newestBehavioral) return runs[0];
2765
+ const behavioralIndex = runs.indexOf(newestBehavioral);
2766
+ const newerFailure = runs.slice(0, behavioralIndex).find((run2) => run2.report.ok === false);
2767
+ return newerFailure ?? newestBehavioral;
2768
+ }
2769
+ function inRepoCommit(releaseId) {
2770
+ const m = /^([0-9a-f]{40})(?:-[0-9a-f]{12})?$/.exec(releaseId ?? "");
2771
+ return m ? m[1] : null;
2772
+ }
2773
+ function sameReleaseCommit(a, b) {
2774
+ if (a !== void 0 && a === b) return true;
2775
+ const commit = inRepoCommit(a);
2776
+ return commit !== null && commit === inRepoCommit(b);
2777
+ }
2778
+ function pushEvidence(contentDir2, releaseId, verify) {
2779
+ const retained = readRetainedAttestations(contentDir2);
2780
+ const sameCommit = retained.runs.filter((candidate) => sameReleaseCommit(candidate.releaseId, releaseId));
2781
+ const verified = sameCommit.filter((candidate) => candidate.releaseId === releaseId).filter(verify);
2782
+ const selected = selectAttestationForPush(verified);
2783
+ return {
2784
+ ...selected ? { selected } : {},
2785
+ historyErrors: retained.errors,
2786
+ staleRuns: verified.length === 0 ? sameCommit.length : 0
2787
+ };
2788
+ }
2789
+ function summaryOf(attestation) {
2790
+ return {
2791
+ runId: attestation.runId,
2792
+ selfHash: attestation.selfHash,
2793
+ tool: attestation.tool,
2794
+ screenIds: Object.keys(attestation.descriptorHashes).sort(),
2795
+ coverage: attestation.coverage,
2796
+ report: { ok: attestation.report.ok, counts: attestation.report.counts },
2797
+ gitDiffHash: attestation.gitDiffHash,
2798
+ releaseHash: attestation.releaseHash
2799
+ };
2800
+ }
2801
+ function writeAttestation(input, opts = {}) {
2802
+ const contentDir2 = resolve6(input.contentDir);
2803
+ const dir = join6(contentDir2, ".himi", "attestations");
2804
+ mkdirSync5(dir, { recursive: true });
2805
+ const previous = readLatestAttestation(contentDir2);
2806
+ const runId = `${(opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()}-${(opts.randomSuffix ?? (() => randomBytes(3).toString("hex")))()}`;
2807
+ const descriptorHashes = Object.fromEntries(
2808
+ Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha2562(bytes2)])
2809
+ );
2810
+ const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a], [b]) => a.localeCompare(b)).filter(([, path2]) => existsSync3(path2)).map(([screen, path2]) => {
2811
+ const bytes2 = readFileSync5(path2);
2812
+ return { screen, bytes: bytes2, hash: sha2562(bytes2) };
2813
+ });
2814
+ const screenshots = Object.fromEntries(screenshotEntries.map(({ screen, hash }) => [screen, hash]));
2815
+ for (const { bytes: bytes2, hash } of screenshotEntries) {
2816
+ const artifact = screenshotArtifactPath(contentDir2, hash);
2817
+ mkdirSync5(join6(dir, "artifacts"), { recursive: true });
2818
+ if (!existsSync3(artifact)) writeFileSync5(artifact, bytes2);
2819
+ }
2820
+ const unsigned = {
2821
+ attestation: 2,
2822
+ runId,
2823
+ tool: input.tool,
2824
+ app: input.app,
2825
+ contentDir: storedContentDir(contentDir2),
2826
+ gitDiffHash: input.gitDiffHash,
2827
+ releaseId: input.releaseId,
2828
+ releaseHash: releasePayloadHash(input.releasePayload),
2829
+ gitUntrackedHash: currentGitUntrackedHash(contentDir2),
2830
+ descriptorHashes,
2831
+ screenshots,
2832
+ coverage: input.coverage,
2833
+ report: input.report,
2834
+ prev: previous?.selfHash ?? null
2835
+ };
2836
+ const attestation = { ...unsigned, selfHash: "" };
2837
+ attestation.selfHash = selfHashFor(attestation);
2838
+ const json5 = JSON.stringify(attestation, null, 2) + "\n";
2839
+ const runFile = `${runId.replace(/:/g, "-")}.json`;
2840
+ writeFileSync5(join6(dir, runFile), json5);
2841
+ writeFileSync5(join6(dir, "latest.json"), json5);
2842
+ const keep = Math.max(1, opts.maxRuns ?? DEFAULT_MAX_RUNS);
2843
+ const allPriorRuns = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").filter((name) => name !== runFile).sort().reverse();
2844
+ const priorRuns = allPriorRuns.filter((name) => {
2845
+ const kind = classifyRetainedRun(dir, name).kind;
2846
+ if (kind === "legacy") {
2847
+ unlinkSync(join6(dir, name));
2848
+ return false;
2572
2849
  }
2573
- if (inEnvelope) return { surface, targeted: true };
2574
- return surface.platformKey ? { surface, targeted: false, optIn: `"${surface.platformKey}": true` } : { surface, targeted: false };
2850
+ return kind !== "future";
2851
+ });
2852
+ for (const name of priorRuns.slice(Math.max(0, keep - 1))) unlinkSync(join6(dir, name));
2853
+ const retainedFiles = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
2854
+ const retainedRuns = retainedFiles.map((name) => {
2855
+ const classified = classifyRetainedRun(dir, name);
2856
+ return classified.kind === "current" ? classified.run : null;
2575
2857
  });
2858
+ if (retainedRuns.every((run2) => run2 !== null)) {
2859
+ const retainedScreenshotHashes = new Set(
2860
+ retainedRuns.flatMap((run2) => Object.values(run2.screenshots))
2861
+ );
2862
+ const artifactDir = join6(dir, "artifacts");
2863
+ if (existsSync3(artifactDir)) {
2864
+ for (const name of readdirSync3(artifactDir).filter((candidate) => candidate.endsWith(".png"))) {
2865
+ if (!retainedScreenshotHashes.has(name.slice(0, -4))) unlinkSync(join6(artifactDir, name));
2866
+ }
2867
+ }
2868
+ }
2869
+ return attestation;
2576
2870
  }
2577
- function resolveSurfaceEnvelope(platforms, requested) {
2578
- const envelope = PREVIEW_SURFACES.filter((s) => isTargeted(s, platforms)).map((s) => s.id);
2579
- if (requested == null) return { enabled: envelope, rejected: [] };
2580
- const inEnvelope = new Set(envelope);
2581
- const enabled = [];
2582
- const rejected = [];
2583
- for (const id of requested) {
2584
- if (inEnvelope.has(id)) {
2585
- if (!enabled.includes(id)) enabled.push(id);
2586
- } else if (!surfaceById(id)) {
2587
- rejected.push({ id, reason: `"${id}" is not a surface` });
2871
+ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, releasePayload, opts = {}) {
2872
+ const errors = [];
2873
+ if (selfHashFor(attestation) !== attestation.selfHash) errors.push("selfHash does not match the attestation bytes");
2874
+ try {
2875
+ const dir = join6(resolve6(contentDir2), ".himi", "attestations");
2876
+ const latest = readLatestAttestation(contentDir2);
2877
+ if (!latest || opts.requireLatest !== false && latest.selfHash !== attestation.selfHash) {
2878
+ errors.push("attestation does not match latest.json");
2588
2879
  } else {
2589
- const key2 = surfaceById(id).platformKey;
2590
- rejected.push({
2591
- id,
2592
- reason: `"${id}" is outside this binary's envelope \u2014 add ${key2 ? `"${key2}": true` : "the target"} to apps/<app>/platforms.json and ship a new binary`
2593
- });
2880
+ const classified = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").map((name) => classifyRetainedRun(dir, name)).filter((entry) => entry.kind !== "future");
2881
+ const retained = classified.map((entry) => entry.kind === "current" ? entry.run : null);
2882
+ if (retained.some((run2) => run2 === null)) {
2883
+ errors.push("attestation history contains a malformed retained run");
2884
+ } else {
2885
+ const runs = retained;
2886
+ const byHash = new Map(runs.map((run2) => [run2.selfHash, run2]));
2887
+ if (!byHash.has(attestation.selfHash)) errors.push("attestation has no matching retained run");
2888
+ if (!byHash.has(latest.selfHash)) {
2889
+ errors.push("latest.json has no matching retained run");
2890
+ } else {
2891
+ const seen = /* @__PURE__ */ new Set();
2892
+ let cursor = latest;
2893
+ while (cursor && !seen.has(cursor.selfHash)) {
2894
+ seen.add(cursor.selfHash);
2895
+ if (selfHashFor(cursor) !== cursor.selfHash) {
2896
+ errors.push(`selfHash does not match retained run ${cursor.runId}`);
2897
+ break;
2898
+ }
2899
+ cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
2900
+ }
2901
+ if (seen.size !== runs.length) errors.push("attestation hash chain does not reach every retained run");
2902
+ }
2903
+ }
2594
2904
  }
2905
+ } catch {
2906
+ errors.push("attestation history could not be verified");
2595
2907
  }
2596
- return { enabled: envelope.filter((id) => enabled.includes(id)), rejected };
2597
- }
2598
- function isComponentAllowed(surface, componentType) {
2599
- const gate = surface.gate;
2600
- if (gate.kind === "none") return { allowed: true };
2601
- if (gate.kind === "framework") {
2602
- const missing = gate.unavailable[componentType];
2603
- return missing ? { allowed: false, reason: `${missing} is unavailable on ${gate.platform}` } : { allowed: true };
2908
+ if (attestation.app !== expectedApp) errors.push(`attestation app does not match ${expectedApp}`);
2909
+ if (attestation.contentDir !== storedContentDir(contentDir2)) errors.push("attestation contentDir does not match the requested content directory");
2910
+ if (releasePayloadHash(releasePayload) !== attestation.releaseHash) {
2911
+ errors.push("releaseHash does not match the current release payload");
2604
2912
  }
2605
- if (gate.kind === "projection") {
2606
- return WIDGET_VOCABULARY[componentType]?.[gate.family] ? { allowed: true } : {
2607
- allowed: false,
2608
- // Name the catalog. "No Android Auto template expresses himalaya.Gauge"
2609
- // tells an author it is unsupported; adding "(templates: list)" tells them
2610
- // WHAT the surface can express, which is the thing they can act on.
2611
- reason: `no ${gate.platform} template expresses ${componentType} (templates: ${gate.templates.join(", ")})`
2612
- };
2913
+ try {
2914
+ const liveDiffHash = currentGitDiffHash(contentDir2);
2915
+ if (liveDiffHash !== "unavailable" && liveDiffHash !== attestation.gitDiffHash) {
2916
+ errors.push("gitDiffHash does not match the live git diff");
2917
+ }
2918
+ } catch {
2919
+ errors.push("gitDiffHash could not be recomputed from the live git diff");
2613
2920
  }
2614
- const row = WIDGET_VOCABULARY[componentType];
2615
- if (!row) return { allowed: true };
2616
- return row[gate.family] ? { allowed: true } : { allowed: false, reason: `not in the ${gate.family} allowlist` };
2617
- }
2618
- var PREVIEW_EXEMPT_PLATFORM_KEYS, WATCH_UNAVAILABLE, CAR_TEMPLATES, PROJECTION_SNAPSHOT_SIZE, widgetSurface, PREVIEW_SURFACES, SURFACE_GROUPS, SURFACE_VIEW_LADDER;
2619
- var init_preview_surfaces = __esm({
2620
- "../../core/ts/src/preview-surfaces.ts"() {
2621
- init_widget_vocabulary();
2622
- PREVIEW_EXEMPT_PLATFORM_KEYS = ["astro"];
2623
- WATCH_UNAVAILABLE = {
2624
- "himalaya.WebView": "WebKit",
2625
- "himalaya.VideoPlayer": "AVKit",
2626
- "himalaya.Lottie": "Lottie",
2921
+ try {
2922
+ const liveUntrackedHash = currentGitUntrackedHash(contentDir2);
2923
+ if (liveUntrackedHash !== "unavailable" && liveUntrackedHash !== attestation.gitUntrackedHash) {
2924
+ errors.push("gitUntrackedHash does not match the live untracked source bytes");
2925
+ }
2926
+ } catch {
2927
+ errors.push("gitUntrackedHash could not be recomputed from the live untracked source bytes");
2928
+ }
2929
+ for (const [screen, expected] of Object.entries(attestation.descriptorHashes)) {
2930
+ const bytes2 = descriptors[screen];
2931
+ if (bytes2 === void 0) {
2932
+ errors.push(`missing baked descriptor for ${screen}`);
2933
+ continue;
2934
+ }
2935
+ if (sha2562(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
2936
+ }
2937
+ for (const [screen, expected] of Object.entries(attestation.screenshots)) {
2938
+ const path2 = screenshotArtifactPath(contentDir2, expected);
2939
+ if (!existsSync3(path2)) {
2940
+ errors.push(`missing screenshot for ${screen}: ${basename2(path2)}`);
2941
+ continue;
2942
+ }
2943
+ if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
2944
+ }
2945
+ return { ok: errors.length === 0, errors };
2946
+ }
2947
+ var DEFAULT_MAX_RUNS, ATTESTATION_VERSION, GIT_HASH_MAX_BUFFER, sha2562, NOT_ATTESTED_DIRS, stringRecord;
2948
+ var init_attestation = __esm({
2949
+ "src/attestation.ts"() {
2950
+ init_diff_hash();
2951
+ init_source_deny();
2952
+ init_widget_identity();
2953
+ DEFAULT_MAX_RUNS = 20;
2954
+ ATTESTATION_VERSION = 2;
2955
+ GIT_HASH_MAX_BUFFER = 128 * 1024 * 1024;
2956
+ sha2562 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
2957
+ NOT_ATTESTED_DIRS = /* @__PURE__ */ new Set([".himi", ...AGENT_TOOLING_DIRS]);
2958
+ stringRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
2959
+ }
2960
+ });
2961
+
2962
+ // ../../core/admin-web/src/utils/qr.ts
2963
+ function rawDataModules(ver) {
2964
+ let result = (16 * ver + 128) * ver + 64;
2965
+ if (ver >= 2) {
2966
+ const numAlign = Math.floor(ver / 7) + 2;
2967
+ result -= (25 * numAlign - 10) * numAlign - 55;
2968
+ if (ver >= 7) result -= 36;
2969
+ }
2970
+ return result;
2971
+ }
2972
+ function byteCapacity(ver) {
2973
+ const lenBits = ver < 10 ? 8 : 16;
2974
+ return dataCodewords(ver) - Math.ceil((4 + lenBits) / 8);
2975
+ }
2976
+ function fitVersion(len) {
2977
+ for (let v = 1; v <= QR_MAX_VERSION; v++) if (byteCapacity(v) >= len) return v;
2978
+ return void 0;
2979
+ }
2980
+ function rsDivisor(degree) {
2981
+ const result = new Array(degree).fill(0);
2982
+ result[degree - 1] = 1;
2983
+ let root = 1;
2984
+ for (let i = 0; i < degree; i++) {
2985
+ for (let j = 0; j < degree; j++) {
2986
+ result[j] = gfMul(result[j], root);
2987
+ if (j + 1 < degree) result[j] ^= result[j + 1];
2988
+ }
2989
+ root = gfMul(root, 2);
2990
+ }
2991
+ return result;
2992
+ }
2993
+ function rsRemainder(data, degree) {
2994
+ const divisor = rsDivisor(degree);
2995
+ const res = new Array(degree).fill(0);
2996
+ for (const b of data) {
2997
+ const factor = b ^ res.shift();
2998
+ res.push(0);
2999
+ for (let i = 0; i < degree; i++) res[i] ^= gfMul(divisor[i], factor);
3000
+ }
3001
+ return res;
3002
+ }
3003
+ function encodeData(bytes2, ver) {
3004
+ const bits = [];
3005
+ const push = (val, len) => {
3006
+ for (let i = len - 1; i >= 0; i--) bits.push(val >>> i & 1);
3007
+ };
3008
+ push(4, 4);
3009
+ push(bytes2.length, ver < 10 ? 8 : 16);
3010
+ for (const b of bytes2) push(b, 8);
3011
+ const capacityBits = dataCodewords(ver) * 8;
3012
+ push(0, Math.min(4, capacityBits - bits.length));
3013
+ while (bits.length % 8 !== 0) bits.push(0);
3014
+ const out = [];
3015
+ for (let i = 0; i < bits.length; i += 8) {
3016
+ let byte2 = 0;
3017
+ for (let j = 0; j < 8; j++) byte2 = byte2 << 1 | bits[i + j];
3018
+ out.push(byte2);
3019
+ }
3020
+ for (let pad2 = 236; out.length < dataCodewords(ver); pad2 ^= 236 ^ 17) out.push(pad2);
3021
+ return out;
3022
+ }
3023
+ function interleave(data, ver) {
3024
+ const numBlocks = NUM_BLOCKS_L[ver];
3025
+ const eccLen = ECC_PER_BLOCK_L[ver];
3026
+ const shortLen = Math.floor(totalCodewords(ver) / numBlocks) - eccLen;
3027
+ const numShort = numBlocks - totalCodewords(ver) % numBlocks;
3028
+ const blocks = [];
3029
+ const eccs = [];
3030
+ let off = 0;
3031
+ for (let i = 0; i < numBlocks; i++) {
3032
+ const len = shortLen + (i < numShort ? 0 : 1);
3033
+ const blk = data.slice(off, off + len);
3034
+ off += len;
3035
+ blocks.push(blk);
3036
+ eccs.push(rsRemainder(blk, eccLen));
3037
+ }
3038
+ const out = [];
3039
+ for (let i = 0; i < shortLen + 1; i++) {
3040
+ for (let b = 0; b < numBlocks; b++) {
3041
+ const v = blocks[b][i];
3042
+ if (v !== void 0) out.push(v);
3043
+ }
3044
+ }
3045
+ for (let i = 0; i < eccLen; i++) for (let b = 0; b < numBlocks; b++) out.push(eccs[b][i]);
3046
+ return out;
3047
+ }
3048
+ function alignmentPositions(ver) {
3049
+ if (ver === 1) return [];
3050
+ const num3 = Math.floor(ver / 7) + 2;
3051
+ const size = ver * 4 + 17;
3052
+ const step = Math.ceil((ver * 4 + 4) / (num3 * 2 - 2)) * 2;
3053
+ const res = [6];
3054
+ for (let pos = size - 7; res.length < num3; pos -= step) res.splice(1, 0, pos);
3055
+ return res;
3056
+ }
3057
+ function bchFormat(data) {
3058
+ let rem = data;
3059
+ for (let i = 0; i < 10; i++) rem = rem << 1 ^ (rem >>> 9) * 1335;
3060
+ return (data << 10 | rem) ^ 21522;
3061
+ }
3062
+ function bchVersion(ver) {
3063
+ let rem = ver;
3064
+ for (let i = 0; i < 12; i++) rem = rem << 1 ^ (rem >>> 11) * 7973;
3065
+ return ver << 12 | rem;
3066
+ }
3067
+ function penalty(m, size) {
3068
+ let score = 0;
3069
+ const run2 = (get) => {
3070
+ for (let i = 0; i < size; i++) {
3071
+ let last = get(i, 0), len = 1;
3072
+ for (let j = 1; j < size; j++) {
3073
+ const cur = get(i, j);
3074
+ if (cur === last) {
3075
+ len++;
3076
+ if (len === 5) score += 3;
3077
+ else if (len > 5) score++;
3078
+ } else {
3079
+ last = cur;
3080
+ len = 1;
3081
+ }
3082
+ }
3083
+ }
3084
+ };
3085
+ run2((i, j) => m[i][j]);
3086
+ run2((i, j) => m[j][i]);
3087
+ for (let y = 0; y < size - 1; y++)
3088
+ for (let x = 0; x < size - 1; x++) {
3089
+ const c = m[y][x];
3090
+ if (c === m[y][x + 1] && c === m[y + 1][x] && c === m[y + 1][x + 1]) score += 3;
3091
+ }
3092
+ let dark = 0;
3093
+ for (const row of m) for (const c of row) if (c) dark++;
3094
+ const total = size * size;
3095
+ score += Math.floor(Math.abs(dark * 20 - total * 10) / total) * 10;
3096
+ return score;
3097
+ }
3098
+ function encodeQr(text5) {
3099
+ const bytes2 = [...new TextEncoder().encode(text5)];
3100
+ const version2 = fitVersion(bytes2.length);
3101
+ if (!version2) {
3102
+ throw new RangeError(`QR payload too long: ${bytes2.length} bytes exceeds version ${QR_MAX_VERSION} at EC level L`);
3103
+ }
3104
+ const codewords = interleave(encodeData(bytes2, version2), version2);
3105
+ const size = version2 * 4 + 17;
3106
+ const modules = Array.from({ length: size }, () => new Array(size).fill(false));
3107
+ const reserved = Array.from({ length: size }, () => new Array(size).fill(false));
3108
+ const set = (x, y, dark) => {
3109
+ modules[y][x] = dark;
3110
+ reserved[y][x] = true;
3111
+ };
3112
+ const finder = (cx, cy) => {
3113
+ for (let dy = -4; dy <= 4; dy++)
3114
+ for (let dx = -4; dx <= 4; dx++) {
3115
+ const x = cx + dx, y = cy + dy;
3116
+ if (x < 0 || y < 0 || x >= size || y >= size) continue;
3117
+ const d = Math.max(Math.abs(dx), Math.abs(dy));
3118
+ set(x, y, d !== 2 && d !== 4);
3119
+ }
3120
+ };
3121
+ finder(3, 3);
3122
+ finder(size - 4, 3);
3123
+ finder(3, size - 4);
3124
+ for (let i = 8; i < size - 8; i++) {
3125
+ set(i, 6, i % 2 === 0);
3126
+ set(6, i, i % 2 === 0);
3127
+ }
3128
+ for (const ay of alignmentPositions(version2))
3129
+ for (const ax of alignmentPositions(version2)) {
3130
+ if (ax === 6 && ay === 6 || ax === 6 && ay === size - 7 || ax === size - 7 && ay === 6) continue;
3131
+ for (let dy = -2; dy <= 2; dy++)
3132
+ for (let dx = -2; dx <= 2; dx++) set(ax + dx, ay + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
3133
+ }
3134
+ for (let i = 0; i < 9; i++) {
3135
+ reserved[i][8] = true;
3136
+ reserved[8][i] = true;
3137
+ }
3138
+ for (let i = 0; i < 8; i++) {
3139
+ reserved[size - 1 - i][8] = true;
3140
+ reserved[8][size - 1 - i] = true;
3141
+ }
3142
+ set(8, size - 8, true);
3143
+ if (version2 >= 7)
3144
+ for (let i = 0; i < 18; i++) {
3145
+ const a = size - 11 + i % 3, b = Math.floor(i / 3);
3146
+ reserved[a][b] = true;
3147
+ reserved[b][a] = true;
3148
+ }
3149
+ let bit = 0;
3150
+ const totalBits = codewords.length * 8;
3151
+ for (let right = size - 1; right >= 1; right -= 2) {
3152
+ if (right === 6) right = 5;
3153
+ for (let vert = 0; vert < size; vert++) {
3154
+ for (let j = 0; j < 2; j++) {
3155
+ const x = right - j;
3156
+ const upward = (right + 1 & 2) === 0;
3157
+ const y = upward ? size - 1 - vert : vert;
3158
+ if (reserved[y][x]) continue;
3159
+ modules[y][x] = bit < totalBits ? (codewords[bit >>> 3] >>> 7 - (bit & 7) & 1) === 1 : false;
3160
+ bit++;
3161
+ }
3162
+ }
3163
+ }
3164
+ let best = null, bestMask = 0, bestScore = Infinity;
3165
+ for (let mask = 0; mask < 8; mask++) {
3166
+ const cand = modules.map((row) => [...row]);
3167
+ for (let y = 0; y < size; y++)
3168
+ for (let x = 0; x < size; x++) if (!reserved[y][x] && MASKS[mask](x, y)) cand[y][x] = !cand[y][x];
3169
+ applyFormat(cand, size, mask);
3170
+ if (version2 >= 7) applyVersion(cand, size, version2);
3171
+ const s = penalty(cand, size);
3172
+ if (s < bestScore) {
3173
+ bestScore = s;
3174
+ best = cand;
3175
+ bestMask = mask;
3176
+ }
3177
+ }
3178
+ return { size, version: version2, mask: bestMask, modules: best };
3179
+ }
3180
+ function applyFormat(m, size, mask) {
3181
+ const bits = bchFormat(1 << 3 | mask);
3182
+ const on3 = (i) => (bits >>> i & 1) === 1;
3183
+ const put = (x, y, dark) => {
3184
+ m[y][x] = dark;
3185
+ };
3186
+ for (let i = 0; i <= 5; i++) put(8, i, on3(i));
3187
+ put(8, 7, on3(6));
3188
+ put(8, 8, on3(7));
3189
+ put(7, 8, on3(8));
3190
+ for (let i = 9; i < 15; i++) put(14 - i, 8, on3(i));
3191
+ for (let i = 0; i < 8; i++) put(size - 1 - i, 8, on3(i));
3192
+ for (let i = 8; i < 15; i++) put(8, size - 15 + i, on3(i));
3193
+ put(8, size - 8, true);
3194
+ }
3195
+ function applyVersion(m, size, version2) {
3196
+ const bits = bchVersion(version2);
3197
+ for (let i = 0; i < 18; i++) {
3198
+ const on3 = (bits >>> i & 1) === 1;
3199
+ const a = size - 11 + i % 3, b = Math.floor(i / 3);
3200
+ m[a][b] = on3;
3201
+ m[b][a] = on3;
3202
+ }
3203
+ }
3204
+ var ECC_PER_BLOCK_L, NUM_BLOCKS_L, QR_MAX_VERSION, totalCodewords, dataCodewords, EXP, LOG, gfMul, MASKS;
3205
+ var init_qr = __esm({
3206
+ "../../core/admin-web/src/utils/qr.ts"() {
3207
+ ECC_PER_BLOCK_L = [0, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28];
3208
+ NUM_BLOCKS_L = [0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8];
3209
+ QR_MAX_VERSION = 20;
3210
+ totalCodewords = (ver) => Math.floor(rawDataModules(ver) / 8);
3211
+ dataCodewords = (ver) => totalCodewords(ver) - ECC_PER_BLOCK_L[ver] * NUM_BLOCKS_L[ver];
3212
+ EXP = new Uint8Array(512);
3213
+ LOG = new Uint8Array(256);
3214
+ (() => {
3215
+ let x = 1;
3216
+ for (let i = 0; i < 255; i++) {
3217
+ EXP[i] = x;
3218
+ LOG[x] = i;
3219
+ x <<= 1;
3220
+ if (x & 256) x ^= 285;
3221
+ }
3222
+ for (let i = 255; i < 512; i++) EXP[i] = EXP[i - 255];
3223
+ })();
3224
+ gfMul = (a, b) => a === 0 || b === 0 ? 0 : EXP[LOG[a] + LOG[b]];
3225
+ MASKS = [
3226
+ (x, y) => (x + y) % 2 === 0,
3227
+ (_x, y) => y % 2 === 0,
3228
+ (x) => x % 3 === 0,
3229
+ (x, y) => (x + y) % 3 === 0,
3230
+ (x, y) => (Math.floor(y / 2) + Math.floor(x / 3)) % 2 === 0,
3231
+ (x, y) => x * y % 2 + x * y % 3 === 0,
3232
+ (x, y) => (x * y % 2 + x * y % 3) % 2 === 0,
3233
+ (x, y) => ((x + y) % 2 + x * y % 3) % 2 === 0
3234
+ ];
3235
+ }
3236
+ });
3237
+
3238
+ // ../../core/dev-server/src/wix-placeholders.ts
3239
+ var PLACEHOLDER_CLIENT_ID;
3240
+ var init_wix_placeholders = __esm({
3241
+ "../../core/dev-server/src/wix-placeholders.ts"() {
3242
+ PLACEHOLDER_CLIENT_ID = "YOUR_WIX_CLIENT_ID";
3243
+ }
3244
+ });
3245
+
3246
+ // ../../core/ts/src/widget-vocabulary.ts
3247
+ function isComponentAllowedForFamily(key2, family) {
3248
+ return WIDGET_VOCABULARY[key2]?.[family] === true;
3249
+ }
3250
+ var WIDGET_VOCABULARY, WIDGET_COMPONENTS;
3251
+ var init_widget_vocabulary = __esm({
3252
+ "../../core/ts/src/widget-vocabulary.ts"() {
3253
+ WIDGET_VOCABULARY = {
3254
+ "himalaya.VStack": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: true },
3255
+ "himalaya.HStack": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: true },
3256
+ "himalaya.ZStack": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
3257
+ "himalaya.Card": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true },
3258
+ "himalaya.Text": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
3259
+ "himalaya.Image": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: true },
3260
+ "himalaya.Icon": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
3261
+ "himalaya.Divider": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: false },
3262
+ "himalaya.Spacer": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
3263
+ "himalaya.Button": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: false },
3264
+ "himalaya.ProgressBar": { ios_home: true, ios_lock: true, android_home: true, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
3265
+ "himalaya.Gauge": { ios_home: true, ios_lock: true, android_home: false, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
3266
+ // Line/area/bar sparkline. No Glance Canvas → android_home:false (degrades), like Gauge.
3267
+ "himalaya.Chart": { ios_home: true, ios_lock: true, android_home: false, web_card: true, ios_live_activity: true, watch_complication: true, car_list: false },
3268
+ "himalaya.Label": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: false },
3269
+ "himalaya.Chip": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: true, watch_complication: false, car_list: false },
3270
+ "himalaya.ListItem": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true },
3271
+ "himalaya.LazyList": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true },
3272
+ // ── RFC 0052 temporal vocabulary: a DECLARED divergence, not a gap ──────────
3273
+ // A 42-cell month grid and an hour-gutter day timetable are not "too big for a
3274
+ // widget" — they are the wrong SHAPE for one, and both are interactive (month
3275
+ // paging, tap-to-select, drag-to-reschedule) where a widget snapshot is frozen.
3276
+ // Shrinking either produces something illegible rather than something smaller,
3277
+ // so they are excluded from EVERY family, deliberately.
3278
+ "himalaya.MonthCalendar": { ios_home: false, ios_lock: false, android_home: false, web_card: false, ios_live_activity: false, watch_complication: false, car_list: false },
3279
+ "himalaya.TimeGrid": { ios_home: false, ios_lock: false, android_home: false, web_card: false, ios_live_activity: false, watch_complication: false, car_list: false },
3280
+ // Agenda IS admitted: it is list-shaped, so it degrades to "the next few things"
3281
+ // exactly the way LazyList/ListItem do — and "what's next today" is the single
3282
+ // most useful widget a calendar can produce. Its row mirrors LazyList's: the
3283
+ // accessory surfaces (lock / Live Activity / complication) drop lists entirely.
3284
+ "himalaya.Agenda": { ios_home: true, ios_lock: false, android_home: true, web_card: true, ios_live_activity: false, watch_complication: false, car_list: true }
3285
+ };
3286
+ WIDGET_COMPONENTS = new Set(Object.keys(WIDGET_VOCABULARY));
3287
+ }
3288
+ });
3289
+
3290
+ // ../../core/ts/src/preview-surfaces.ts
3291
+ function surfaceById(id) {
3292
+ return PREVIEW_SURFACES.find((s) => s.id === id);
3293
+ }
3294
+ function surfaceFrozenFamily(surface) {
3295
+ const gate = surface.gate;
3296
+ return gate.kind === "family" || gate.kind === "projection" ? gate.family : void 0;
3297
+ }
3298
+ function isWidgetFamilySurface(surface) {
3299
+ return surface.gate.kind === "family";
3300
+ }
3301
+ function surfaceRenderMode(surface) {
3302
+ const { kind } = surface.gate;
3303
+ return kind === "family" || kind === "projection" ? "frozen" : "interactive";
3304
+ }
3305
+ function isViewTarget(key2) {
3306
+ return SURFACE_GROUPS.includes(key2) || surfaceById(key2) !== void 0;
3307
+ }
3308
+ function viewTargetRenderMode(target) {
3309
+ if (target === "frozen") return "frozen";
3310
+ if (SURFACE_GROUPS.includes(target)) return "interactive";
3311
+ const surface = surfaceById(target);
3312
+ return surface ? surfaceRenderMode(surface) : void 0;
3313
+ }
3314
+ function resolveSurfaceViewKey(surfaceId, registered) {
3315
+ const ladder = SURFACE_VIEW_LADDER[surfaceId];
3316
+ if (!ladder) return void 0;
3317
+ const keys = registered instanceof Set ? registered : new Set(registered);
3318
+ return ladder.find((rung) => keys.has(rung));
3319
+ }
3320
+ function isTargeted(surface, platforms) {
3321
+ if (surface.platformKey === null) return true;
3322
+ if (surface.platformKey === "macos") return platforms.macos !== false;
3323
+ return platforms[surface.platformKey] === true;
3324
+ }
3325
+ function surfacesForApp(platforms, producedFamilies) {
3326
+ const produced = producedFamilies === void 0 ? void 0 : producedFamilies instanceof Set ? producedFamilies : new Set(producedFamilies);
3327
+ return PREVIEW_SURFACES.map((surface) => {
3328
+ const inEnvelope = isTargeted(surface, platforms);
3329
+ const family = isWidgetFamilySurface(surface) ? surfaceFrozenFamily(surface) : void 0;
3330
+ if (produced && family !== void 0) {
3331
+ const isProduced = produced.has(family);
3332
+ return inEnvelope && isProduced ? { surface, targeted: true, produced: true } : {
3333
+ surface,
3334
+ targeted: false,
3335
+ produced: isProduced,
3336
+ // The four native families carry no platform key, so there is nothing to
3337
+ // offer; `web_card` and `watch_complication` DO have one, and when that is
3338
+ // the half that is missing it is still the right advice. Hence the guard —
3339
+ // it is load-bearing for two families, not defensive padding.
3340
+ ...inEnvelope ? {} : surface.platformKey ? { optIn: `"${surface.platformKey}": true` } : {},
3341
+ ...isProduced ? {} : {
3342
+ // The rebuild half is not padding: the produced set is read off BUILT
3343
+ // bundles, so an author who has just written the registration and not
3344
+ // rebuilt lands here too, and "write defineWidget" alone would be advice
3345
+ // to write the code they are looking at.
3346
+ needs: `defineWidget({ family: "${family}", \u2026 }) in a Tier-5 bundle \u2014 and a rebuild, if you just added it`
3347
+ }
3348
+ };
3349
+ }
3350
+ if (inEnvelope) return { surface, targeted: true };
3351
+ return surface.platformKey ? { surface, targeted: false, optIn: `"${surface.platformKey}": true` } : { surface, targeted: false };
3352
+ });
3353
+ }
3354
+ function resolveSurfaceEnvelope(platforms, requested) {
3355
+ const envelope = PREVIEW_SURFACES.filter((s) => isTargeted(s, platforms)).map((s) => s.id);
3356
+ if (requested == null) return { enabled: envelope, rejected: [] };
3357
+ const inEnvelope = new Set(envelope);
3358
+ const enabled = [];
3359
+ const rejected = [];
3360
+ for (const id of requested) {
3361
+ if (inEnvelope.has(id)) {
3362
+ if (!enabled.includes(id)) enabled.push(id);
3363
+ } else if (!surfaceById(id)) {
3364
+ rejected.push({ id, reason: `"${id}" is not a surface` });
3365
+ } else {
3366
+ const key2 = surfaceById(id).platformKey;
3367
+ rejected.push({
3368
+ id,
3369
+ reason: `"${id}" is outside this binary's envelope \u2014 add ${key2 ? `"${key2}": true` : "the target"} to apps/<app>/platforms.json and ship a new binary`
3370
+ });
3371
+ }
3372
+ }
3373
+ return { enabled: envelope.filter((id) => enabled.includes(id)), rejected };
3374
+ }
3375
+ function isComponentAllowed(surface, componentType) {
3376
+ const gate = surface.gate;
3377
+ if (gate.kind === "none") return { allowed: true };
3378
+ if (gate.kind === "framework") {
3379
+ const missing = gate.unavailable[componentType];
3380
+ return missing ? { allowed: false, reason: `${missing} is unavailable on ${gate.platform}` } : { allowed: true };
3381
+ }
3382
+ if (gate.kind === "projection") {
3383
+ return WIDGET_VOCABULARY[componentType]?.[gate.family] ? { allowed: true } : {
3384
+ allowed: false,
3385
+ // Name the catalog. "No Android Auto template expresses himalaya.Gauge"
3386
+ // tells an author it is unsupported; adding "(templates: list)" tells them
3387
+ // WHAT the surface can express, which is the thing they can act on.
3388
+ reason: `no ${gate.platform} template expresses ${componentType} (templates: ${gate.templates.join(", ")})`
3389
+ };
3390
+ }
3391
+ const row = WIDGET_VOCABULARY[componentType];
3392
+ if (!row) return { allowed: true };
3393
+ return row[gate.family] ? { allowed: true } : { allowed: false, reason: `not in the ${gate.family} allowlist` };
3394
+ }
3395
+ var PREVIEW_EXEMPT_PLATFORM_KEYS, WATCH_UNAVAILABLE, CAR_TEMPLATES, PROJECTION_SNAPSHOT_SIZE, widgetSurface, PREVIEW_SURFACES, SURFACE_GROUPS, SURFACE_VIEW_LADDER;
3396
+ var init_preview_surfaces = __esm({
3397
+ "../../core/ts/src/preview-surfaces.ts"() {
3398
+ init_widget_vocabulary();
3399
+ PREVIEW_EXEMPT_PLATFORM_KEYS = ["astro"];
3400
+ WATCH_UNAVAILABLE = {
3401
+ "himalaya.WebView": "WebKit",
3402
+ "himalaya.VideoPlayer": "AVKit",
3403
+ "himalaya.Lottie": "Lottie",
2627
3404
  "himalaya.Rive": "RiveRuntime"
2628
3405
  };
2629
3406
  CAR_TEMPLATES = ["list"];
@@ -3224,7 +4001,7 @@ function appConfigTs(app) {
3224
4001
  return join10(appDir(app), "config.ts");
3225
4002
  }
3226
4003
  var REPO_ROOT3, withoutExt;
3227
- var init_paths = __esm({
4004
+ var init_paths2 = __esm({
3228
4005
  "../store-release/paths.ts"() {
3229
4006
  REPO_ROOT3 = resolveRepoRoot2();
3230
4007
  withoutExt = (p) => p.replace(/\\/g, "/").replace(/\.(?:[mc]?ts|[mc]?js)$/, "");
@@ -3418,7 +4195,7 @@ function splashPlan(app, configTs, manifestPath, hasIosMarkers) {
3418
4195
  var LABEL_IOS_SPLASH, IOS_COLORSET_NAME, ANDROID_COLOR_RES, ANDROID_STYLE_NAME, ANDROID_SPLASH_FILE, ANDROID_SPLASH_PARENT, DEFAULT_BACKGROUND_TOKEN, GENERATED_XML_NOTE;
3419
4196
  var init_splash = __esm({
3420
4197
  "../native-config/splash.ts"() {
3421
- init_paths();
4198
+ init_paths2();
3422
4199
  LABEL_IOS_SPLASH = "himalaya:splash:info";
3423
4200
  IOS_COLORSET_NAME = "HimalayaLaunchBackground";
3424
4201
  ANDROID_COLOR_RES = "himalaya_splash_background";
@@ -5880,6 +6657,7 @@ function walk(node, registry, path2, out) {
5880
6657
  var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA;
5881
6658
  var init_tokens = __esm({
5882
6659
  "../../core/ts/src/tokens.ts"() {
6660
+ "use strict";
5883
6661
  SYSTEM_FONT_FAMILY = "$system";
5884
6662
  TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
5885
6663
  }
@@ -5906,6 +6684,7 @@ function resolveTranslation(value, catalog, sourceCatalog) {
5906
6684
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
5907
6685
  var init_bindings = __esm({
5908
6686
  "../../core/ts/src/bindings.ts"() {
6687
+ "use strict";
5909
6688
  BINDING_SIGILS = ["service", "flag", "item", "t"];
5910
6689
  BINDING_SIGIL_RE = new RegExp(
5911
6690
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -14627,11 +15406,12 @@ function uxEvidenceForRelease(content, release) {
14627
15406
  const failedPlatforms = forRelease.filter(([, entry]) => entry.status === "failed").map(([platform]) => platform);
14628
15407
  return failedPlatforms.length > 0 ? { ...base, state: "failed", platforms, failedPlatforms, malformed } : { ...base, state: "recorded", platforms, malformed };
14629
15408
  }
14630
- if (recorded.some(([, entry]) => entry.releaseId === releaseId)) {
15409
+ const sameCommit = recorded.filter(([, entry]) => sameReleaseCommit(entry.releaseId, releaseId));
15410
+ if (sameCommit.length > 0) {
14631
15411
  return {
14632
15412
  ...base,
14633
15413
  state: "outdated",
14634
- platforms: recorded.filter(([, entry]) => entry.releaseId === releaseId).map(([platform]) => platform),
15414
+ platforms: sameCommit.map(([platform]) => platform),
14635
15415
  malformed
14636
15416
  };
14637
15417
  }
@@ -14648,6 +15428,7 @@ var NATIVE_PLATFORMS, NATIVE_AUDIT_STATUSES;
14648
15428
  var init_ux_evidence = __esm({
14649
15429
  "src/ux-evidence.ts"() {
14650
15430
  init_mobile_ux_contract();
15431
+ init_attestation();
14651
15432
  NATIVE_PLATFORMS = ["ios", "android"];
14652
15433
  NATIVE_AUDIT_STATUSES = ["passed", "failed", "unavailable"];
14653
15434
  }
@@ -15945,7 +16726,7 @@ function generationAllowed(app, configTs, opts = {}) {
15945
16726
  var DEFAULT_ACCENT_TOKEN, DEFAULT_GLYPH_SCALE, GLYPH_SCALE_MIN, GLYPH_SCALE_MAX, LEGACY_PLACEHOLDER_SHA256;
15946
16727
  var init_plan2 = __esm({
15947
16728
  "../app-icons/plan.ts"() {
15948
- init_paths();
16729
+ init_paths2();
15949
16730
  init_glyphs();
15950
16731
  DEFAULT_ACCENT_TOKEN = "accentPrimary";
15951
16732
  DEFAULT_GLYPH_SCALE = 0.62;
@@ -16013,7 +16794,7 @@ var ANDROID_ICON_BACKGROUND_RES, ANDROID_ADAPTIVE_FILES, ANDROID_ICON_BACKGROUND
16013
16794
  var init_icon2 = __esm({
16014
16795
  "../native-config/icon.ts"() {
16015
16796
  init_plan2();
16016
- init_paths();
16797
+ init_paths2();
16017
16798
  ANDROID_ICON_BACKGROUND_RES = "ic_launcher_background";
16018
16799
  ANDROID_ADAPTIVE_FILES = ["ic_launcher.xml", "ic_launcher_round.xml"];
16019
16800
  ANDROID_ICON_BACKGROUND_FILE = "ic_launcher_background.xml";
@@ -16522,7 +17303,7 @@ var init_gen_native_config = __esm({
16522
17303
  "../native-config/gen-native-config.ts"() {
16523
17304
  init_src2();
16524
17305
  init_src2();
16525
- init_paths();
17306
+ init_paths2();
16526
17307
  init_splash();
16527
17308
  init_icon2();
16528
17309
  init_live_activity();
@@ -37205,6 +37986,44 @@ var init_src6 = __esm({
37205
37986
  }
37206
37987
  });
37207
37988
 
37989
+ // ../../core/serve-storage/src/release-patch.ts
37990
+ import { createHash as createHash17 } from "node:crypto";
37991
+ function sha256hex(b) {
37992
+ return createHash17("sha256").update(b).digest("hex");
37993
+ }
37994
+ function stableStringify(value) {
37995
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
37996
+ if (Array.isArray(value)) return "[" + value.map(stableStringify).join(",") + "]";
37997
+ const object = value;
37998
+ return "{" + Object.keys(object).sort().map((key2) => JSON.stringify(key2) + ":" + stableStringify(object[key2])).join(",") + "}";
37999
+ }
38000
+ function manifestContent(m) {
38001
+ const {
38002
+ releaseId: _releaseId,
38003
+ gitSha: _gitSha,
38004
+ shellSha: _shellSha,
38005
+ label: _label,
38006
+ notes: _notes,
38007
+ publishedAt: _publishedAt,
38008
+ publishedBy: _publishedBy,
38009
+ attestation: _attestation,
38010
+ forcedPastErrors: _forcedPastErrors,
38011
+ ...content
38012
+ } = m;
38013
+ return content;
38014
+ }
38015
+ function releaseContentId(m) {
38016
+ const content = manifestContent(m);
38017
+ const widgets = content.widgets;
38018
+ if (widgets && typeof widgets === "object" && !Array.isArray(widgets)) content.widgets = stableWidgets(widgets);
38019
+ return sha256hex(stableStringify(content));
38020
+ }
38021
+ var init_release_patch = __esm({
38022
+ "../../core/serve-storage/src/release-patch.ts"() {
38023
+ init_widget_identity();
38024
+ }
38025
+ });
38026
+
37208
38027
  // ../../core/dev-server/src/net-scenario.ts
37209
38028
  function resolveScenario(rules, req, upstream) {
37210
38029
  const ctx = {
@@ -39918,11 +40737,11 @@ var init_dependencies = __esm({
39918
40737
  });
39919
40738
 
39920
40739
  // ../../core/functions-build/src/sbom.ts
39921
- import { createHash as createHash17 } from "node:crypto";
40740
+ import { createHash as createHash18 } from "node:crypto";
39922
40741
  function createFunctionSbom(packages) {
39923
40742
  const components2 = [...packages].sort((a, b) => a.name.localeCompare(b.name)).map(({ name, version: version2, integrity: integrity2, license }) => ({ name, version: version2, integrity: integrity2, license }));
39924
40743
  const body = JSON.stringify({ format: "himalaya-sbom-v1", components: components2 });
39925
- return { format: "himalaya-sbom-v1", components: components2, hash: createHash17("sha256").update(body).digest("hex") };
40744
+ return { format: "himalaya-sbom-v1", components: components2, hash: createHash18("sha256").update(body).digest("hex") };
39926
40745
  }
39927
40746
  var init_sbom = __esm({
39928
40747
  "../../core/functions-build/src/sbom.ts"() {
@@ -39930,12 +40749,12 @@ var init_sbom = __esm({
39930
40749
  });
39931
40750
 
39932
40751
  // ../../core/functions-build/src/build.ts
39933
- import { createHash as createHash18 } from "node:crypto";
40752
+ import { createHash as createHash19 } from "node:crypto";
39934
40753
  function sha2563(value) {
39935
- return createHash18("sha256").update(value).digest("hex");
40754
+ return createHash19("sha256").update(value).digest("hex");
39936
40755
  }
39937
40756
  function sri(value) {
39938
- return `sha256-${createHash18("sha256").update(value).digest("base64")}`;
40757
+ return `sha256-${createHash19("sha256").update(value).digest("base64")}`;
39939
40758
  }
39940
40759
  function schemaHash(schema) {
39941
40760
  if (!schema) return "";
@@ -40116,6 +40935,7 @@ __export(build_exports2, {
40116
40935
  pinBakedServerBaseUrl: () => pinBakedServerBaseUrl,
40117
40936
  pinnedReleaseFromEnv: () => pinnedReleaseFromEnv,
40118
40937
  publishableFontFaces: () => publishableFontFaces,
40938
+ rebindReleaseId: () => rebindReleaseId,
40119
40939
  releaseIdFor: () => releaseIdFor,
40120
40940
  servicesFromBundleJs: () => servicesFromBundleJs,
40121
40941
  shellSha: () => shellSha,
@@ -40125,7 +40945,7 @@ __export(build_exports2, {
40125
40945
  withStoreNavBundle: () => withStoreNavBundle,
40126
40946
  withoutDevOnlyAppConfig: () => withoutDevOnlyAppConfig
40127
40947
  });
40128
- import { createHash as createHash19 } from "node:crypto";
40948
+ import { createHash as createHash20 } from "node:crypto";
40129
40949
  import { readFileSync as readFileSync42, readdirSync as readdirSync21, existsSync as existsSync39 } from "node:fs";
40130
40950
  import { dirname as dirname23, join as join37, resolve as resolve32 } from "node:path";
40131
40951
  import { fileURLToPath as fileURLToPath19 } from "node:url";
@@ -40195,10 +41015,10 @@ async function bakeCatalogs(app, locale, fetchCatalog, putDoc) {
40195
41015
  return out;
40196
41016
  }
40197
41017
  function sha2564(buf) {
40198
- return createHash19("sha256").update(buf).digest("hex");
41018
+ return createHash20("sha256").update(buf).digest("hex");
40199
41019
  }
40200
41020
  function sri2(buf) {
40201
- return "sha256-" + createHash19("sha256").update(buf).digest("base64");
41021
+ return "sha256-" + createHash20("sha256").update(buf).digest("base64");
40202
41022
  }
40203
41023
  async function widgetFailureDetail(res) {
40204
41024
  try {
@@ -40294,14 +41114,14 @@ function pinBakedServerBaseUrl(json5, base) {
40294
41114
  const boundary = /:\d+$/.test(base) ? "(?!\\d)" : "(?![\\w.-])(?!:\\d)";
40295
41115
  return json5.replace(new RegExp(`${escaped}${boundary}`, "g"), BAKED_SERVER_BASE_URL);
40296
41116
  }
40297
- function stableStringify(v) {
41117
+ function stableStringify2(v) {
40298
41118
  if (v === null || typeof v !== "object") return JSON.stringify(v);
40299
- if (Array.isArray(v)) return "[" + v.map(stableStringify).join(",") + "]";
41119
+ if (Array.isArray(v)) return "[" + v.map(stableStringify2).join(",") + "]";
40300
41120
  const o = v;
40301
- return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + stableStringify(o[k])).join(",") + "}";
41121
+ return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + stableStringify2(o[k])).join(",") + "}";
40302
41122
  }
40303
41123
  function contentReleaseId(content) {
40304
- return sha2564(stableStringify(content));
41124
+ return sha2564(stableStringify2(content));
40305
41125
  }
40306
41126
  function servicesFromBundleJs(bundleJs, input = {}) {
40307
41127
  try {
@@ -40316,7 +41136,13 @@ function hydrationServices(tier5Dir, bundleName, input = {}) {
40316
41136
  return js ? servicesFromBundleJs(js, input) : null;
40317
41137
  }
40318
41138
  function releaseIdFor(sha, content) {
40319
- return sha !== "dev" ? sha : contentReleaseId(content);
41139
+ if (sha === "dev") return contentReleaseId(content);
41140
+ const stored = JSON.parse(JSON.stringify(content));
41141
+ return `${sha}-${releaseContentId(stored).slice(0, 12)}`;
41142
+ }
41143
+ function rebindReleaseId(manifest) {
41144
+ const { releaseId: _releaseId, gitSha: gitSha2, shellSha: _shellSha, ...content } = manifest;
41145
+ return { ...manifest, releaseId: releaseIdFor(gitSha2, content) };
40320
41146
  }
40321
41147
  function shellSha(root = REPO_ROOT7) {
40322
41148
  if (!isMonorepoCheckout(root)) return "";
@@ -41261,6 +42087,7 @@ var init_build3 = __esm({
41261
42087
  init_src6();
41262
42088
  init_check();
41263
42089
  init_file_store();
42090
+ init_release_patch();
41264
42091
  init_validate_descriptor();
41265
42092
  init_wix_member_login();
41266
42093
  init_wix_placeholders();
@@ -41923,7 +42750,7 @@ __export(studio_exports, {
41923
42750
  renderStudioHtml: () => renderStudioHtml,
41924
42751
  writeStudioRecord: () => writeStudioRecord
41925
42752
  });
41926
- import { createHash as createHash20 } from "node:crypto";
42753
+ import { createHash as createHash21 } from "node:crypto";
41927
42754
  import { existsSync as existsSync41, readdirSync as readdirSync22, readFileSync as readFileSync44, realpathSync as realpathSync5, statSync as statSync8 } from "node:fs";
41928
42755
  import { basename as basename10, extname as extname3, join as join39, relative as relative7, resolve as resolve33, sep as sep9 } from "node:path";
41929
42756
  import { pathToFileURL as pathToFileURL7 } from "node:url";
@@ -42161,7 +42988,7 @@ function buildView(dir, memory, now) {
42161
42988
  reveal: memory.reveal ?? null
42162
42989
  };
42163
42990
  const { generatedAt: _ignored, timelineFrames: _count, ...stable } = view;
42164
- const v = createHash20("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
42991
+ const v = createHash21("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
42165
42992
  return { v, ...view };
42166
42993
  }
42167
42994
  function writeAtomic(file, content) {
@@ -42659,7 +43486,7 @@ function patchHonesty(opts) {
42659
43486
  return { attestation, source };
42660
43487
  }
42661
43488
  var SELECTOR_KINDS;
42662
- var init_release_patch = __esm({
43489
+ var init_release_patch2 = __esm({
42663
43490
  "src/release-patch.ts"() {
42664
43491
  init_widget_identity();
42665
43492
  SELECTOR_KINDS = ["changed", "screen:<id|glob>", "bundle:<name>", "tokens", "app-config"];
@@ -45405,7 +46232,7 @@ __export(pack_exports, {
45405
46232
  unpackTo: () => unpackTo,
45406
46233
  writeZip: () => writeZip
45407
46234
  });
45408
- import { createHash as createHash21 } from "node:crypto";
46235
+ import { createHash as createHash22 } from "node:crypto";
45409
46236
  import { deflateRawSync, inflateRawSync } from "node:zlib";
45410
46237
  import { existsSync as existsSync44, mkdirSync as mkdirSync21, readFileSync as readFileSync48, readdirSync as readdirSync24, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
45411
46238
  import { dirname as dirname26, join as join42, resolve as resolve37 } from "node:path";
@@ -45555,11 +46382,11 @@ function gatherReleaseEntries(appDir2) {
45555
46382
  return { entries, manifest, skipped };
45556
46383
  }
45557
46384
  function sha256Hex4(bytes2) {
45558
- return createHash21("sha256").update(bytes2).digest("hex");
46385
+ return createHash22("sha256").update(bytes2).digest("hex");
45559
46386
  }
45560
46387
  function computeContentHash(entries) {
45561
46388
  const input = packageHashInput(entries.map((e) => ({ path: e.path, hash: sha256Hex4(e.bytes) })));
45562
- return "sha256-" + createHash21("sha256").update(input).digest("base64");
46389
+ return "sha256-" + createHash22("sha256").update(input).digest("base64");
45563
46390
  }
45564
46391
  function buildEntries(appDir2, app, opts) {
45565
46392
  const { entries: released, manifest, skipped } = gatherReleaseEntries(appDir2);
@@ -47768,10 +48595,10 @@ var init_calls = __esm({
47768
48595
  });
47769
48596
 
47770
48597
  // ../../core/functions-runtime/src/queue.ts
47771
- import { createHash as createHash22 } from "node:crypto";
48598
+ import { createHash as createHash23 } from "node:crypto";
47772
48599
  function inputSchemaHash(definition) {
47773
48600
  const schema = definition.input;
47774
- return schema ? createHash22("sha256").update(JSON.stringify(schema.descriptor ?? { kind: schema.kind, optional: schema.optional === true })).digest("hex") : "";
48601
+ return schema ? createHash23("sha256").update(JSON.stringify(schema.descriptor ?? { kind: schema.kind, optional: schema.optional === true })).digest("hex") : "";
47775
48602
  }
47776
48603
  function createFunctionEnqueueCapability(options) {
47777
48604
  return {
@@ -47837,14 +48664,14 @@ var init_queue = __esm({
47837
48664
  });
47838
48665
 
47839
48666
  // ../../core/data-resources/src/plan.ts
47840
- import { createHash as createHash23 } from "node:crypto";
48667
+ import { createHash as createHash24 } from "node:crypto";
47841
48668
  function canonical2(value) {
47842
48669
  if (Array.isArray(value)) return `[${value.map(canonical2).join(",")}]`;
47843
48670
  if (value && typeof value === "object") return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key2, item]) => `${JSON.stringify(key2)}:${canonical2(item)}`).join(",")}}`;
47844
48671
  return JSON.stringify(value);
47845
48672
  }
47846
48673
  function hashResourcePlan(plan) {
47847
- return createHash23("sha256").update(canonical2(plan)).digest("hex");
48674
+ return createHash24("sha256").update(canonical2(plan)).digest("hex");
47848
48675
  }
47849
48676
  function finalize(input) {
47850
48677
  return { ...input, hash: hashResourcePlan(input) };
@@ -48780,7 +49607,7 @@ __export(studio_reel_exports, {
48780
49607
  thin: () => thin,
48781
49608
  utcDay: () => utcDay
48782
49609
  });
48783
- import { createHash as createHash24 } from "node:crypto";
49610
+ import { createHash as createHash25 } from "node:crypto";
48784
49611
  import { lstatSync as lstatSync9, readFileSync as readFileSync55 } from "node:fs";
48785
49612
  import { basename as basename12, join as join49 } from "node:path";
48786
49613
  function loadMemory2(dir) {
@@ -48868,7 +49695,7 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
48868
49695
  const st = lstatSync9(path2);
48869
49696
  if (!st.isFile() || st.nlink !== 1) return null;
48870
49697
  const bytes2 = readFileSync55(path2);
48871
- return createHash24("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
49698
+ return createHash25("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
48872
49699
  } catch {
48873
49700
  return null;
48874
49701
  }
@@ -49288,7 +50115,7 @@ __export(browser_authoring_exports, {
49288
50115
  sdkSubpathReader: () => sdkSubpathReader
49289
50116
  });
49290
50117
  import { createServer as createServer3 } from "node:http";
49291
- import { createHash as createHash25, randomBytes as randomBytes4 } from "node:crypto";
50118
+ import { createHash as createHash26, randomBytes as randomBytes4 } from "node:crypto";
49292
50119
  import { existsSync as existsSync52, readdirSync as readdirSync26, readFileSync as readFileSync57, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
49293
50120
  import { join as join51, posix as posix2, relative as relative9, sep as sep12 } from "node:path";
49294
50121
  function humanMs(ms) {
@@ -49387,7 +50214,7 @@ function encodeJob(job) {
49387
50214
  return Buffer.from(JSON.stringify(job), "utf8").toString("base64");
49388
50215
  }
49389
50216
  function mintToken2(payload, nonce = randomBytes4(16).toString("hex")) {
49390
- const digest2 = createHash25("sha256").update(payload, "utf8").digest("base64url");
50217
+ const digest2 = createHash26("sha256").update(payload, "utf8").digest("base64url");
49391
50218
  return `${nonce}.${digest2}`;
49392
50219
  }
49393
50220
  function defaultOpenBrowser2(url) {
@@ -50310,7 +51137,7 @@ function makeServeHandler(opts) {
50310
51137
  }
50311
51138
  const PINNED_MANIFEST_CACHE_CAP = 100;
50312
51139
  const pinnedManifestCache = /* @__PURE__ */ new Map();
50313
- const sha256hex = (b) => sha256Hex(b);
51140
+ const sha256hex2 = (b) => sha256Hex(b);
50314
51141
  function resolvePin(request, url) {
50315
51142
  return request.headers.get("x-himalaya-release") || url.searchParams.get("release") || null;
50316
51143
  }
@@ -50363,7 +51190,7 @@ ${releaseId}`;
50363
51190
  const doc = store.getDoc ? await store.getDoc(app, hash) : null;
50364
51191
  if (doc) return doc;
50365
51192
  const bytes2 = await slot();
50366
- return bytes2 && sha256hex(bytes2) === hash ? bytes2 : null;
51193
+ return bytes2 && sha256hex2(bytes2) === hash ? bytes2 : null;
50367
51194
  }
50368
51195
  function callbackUrlFor(url) {
50369
51196
  const base = ((typeof opts.publicBaseUrl === "function" ? opts.publicBaseUrl(url) : opts.publicBaseUrl) ?? url.origin).replace(/\/$/, "");
@@ -51027,7 +51854,7 @@ ${releaseId}`;
51027
51854
  }
51028
51855
  }
51029
51856
  }
51030
- const cfgTag = ringPinned ? sha256hex(JSON.stringify(appConfig)) : mm.appConfigHash ?? "";
51857
+ const cfgTag = ringPinned ? sha256hex2(JSON.stringify(appConfig)) : mm.appConfigHash ?? "";
51031
51858
  const etag = strongEtag(`${mm.releaseId}:${cfgTag}:${mm.tokensHash ?? ""}:${screenHash}:${homeBundle?.sha256 ?? ""}`);
51032
51859
  const cache = pin && !ringPinned ? PINNED_CACHE_CONTROL : DOC_CACHE_CONTROL;
51033
51860
  const vary = pin ? { vary: ringPinned ? `x-himalaya-release, ${RELEASE_SOURCE_HEADER}` : "x-himalaya-release" } : {};
@@ -52774,7 +53601,7 @@ function loadStoreConfig(app) {
52774
53601
  }
52775
53602
  var init_preflight = __esm({
52776
53603
  "../store-release/preflight.ts"() {
52777
- init_paths();
53604
+ init_paths2();
52778
53605
  }
52779
53606
  });
52780
53607
 
@@ -52914,7 +53741,7 @@ __export(preflight_app_exports, {
52914
53741
  profileAccount: () => profileAccount
52915
53742
  });
52916
53743
  import { readFileSync as readFileSync66, existsSync as existsSync60, readdirSync as readdirSync28, statSync as statSync15 } from "node:fs";
52917
- import { createHash as createHash26 } from "node:crypto";
53744
+ import { createHash as createHash27 } from "node:crypto";
52918
53745
  import { join as join57 } from "node:path";
52919
53746
  function analyze(projectYml, storeConfig, icon, account) {
52920
53747
  const f = [];
@@ -53203,7 +54030,7 @@ function placeholderHashes2() {
53203
54030
  const p = join57(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
53204
54031
  try {
53205
54032
  if (!statSync15(p).isFile()) continue;
53206
- return [LEGACY_PLACEHOLDER_SHA2562, createHash26("sha256").update(readFileSync66(p)).digest("hex")];
54033
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash27("sha256").update(readFileSync66(p)).digest("hex")];
53207
54034
  } catch {
53208
54035
  }
53209
54036
  }
@@ -53240,7 +54067,7 @@ async function appIconState(iosDir) {
53240
54067
  try {
53241
54068
  if (!statSync15(p).isFile()) continue;
53242
54069
  const bytes2 = readFileSync66(p);
53243
- const hash = createHash26("sha256").update(bytes2).digest("hex");
54070
+ const hash = createHash27("sha256").update(bytes2).digest("hex");
53244
54071
  const pixels = await pixelIssues(bytes2);
53245
54072
  return {
53246
54073
  exists: true,
@@ -53275,7 +54102,7 @@ ${app}: ${errors.length} error(s), ${warns.length} warning(s).`);
53275
54102
  var APP_STORE_SIZES, PNG_COLOR_TYPE_RGB, PLAY_FEATURE_GRAPHIC, LEGACY_PLACEHOLDER_SHA2562;
53276
54103
  var init_preflight_app = __esm({
53277
54104
  "../store-release/preflight-app.ts"() {
53278
- init_paths();
54105
+ init_paths2();
53279
54106
  init_preflight();
53280
54107
  init_credential_profile();
53281
54108
  init_app_icon_rules();
@@ -53382,7 +54209,7 @@ function bumpVersion(app, opts) {
53382
54209
  }
53383
54210
  var init_version = __esm({
53384
54211
  "../store-release/version.ts"() {
53385
- init_paths();
54212
+ init_paths2();
53386
54213
  }
53387
54214
  });
53388
54215
 
@@ -53441,7 +54268,7 @@ async function main2() {
53441
54268
  var ASC_API;
53442
54269
  var init_register_bundle = __esm({
53443
54270
  "../store-release/register-bundle.ts"() {
53444
- init_paths();
54271
+ init_paths2();
53445
54272
  init_preflight();
53446
54273
  ASC_API = "https://api.appstoreconnect.apple.com";
53447
54274
  if (runningAsScript("store-release/register-bundle.ts")) {
@@ -53506,7 +54333,7 @@ async function main3() {
53506
54333
  var ASC;
53507
54334
  var init_asc_status = __esm({
53508
54335
  "../store-release/asc-status.ts"() {
53509
- init_paths();
54336
+ init_paths2();
53510
54337
  init_register_bundle();
53511
54338
  init_preflight();
53512
54339
  ASC = "https://api.appstoreconnect.apple.com";
@@ -63446,7 +64273,7 @@ var init_context = __esm({
63446
64273
  // src/cli.ts
63447
64274
  import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync75, mkdirSync as mkdirSync31, writeFileSync as writeFileSync29, existsSync as existsSync63, rmSync as rmSync14, readdirSync as readdirSync29, statSync as statSync17, lstatSync as lstatSync11, accessSync, constants as constants2, renameSync as renameSync7, cpSync as cpSync3 } from "node:fs";
63448
64275
  import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
63449
- import { createHash as createHash27, randomUUID as randomUUID5 } from "node:crypto";
64276
+ import { createHash as createHash28, randomUUID as randomUUID5 } from "node:crypto";
63450
64277
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
63451
64278
  import { join as join61, resolve as resolve47, relative as relative11, basename as basename15, dirname as dirname34 } from "node:path";
63452
64279
  import { fileURLToPath as fileURLToPath24 } from "node:url";
@@ -64046,743 +64873,7 @@ init_store();
64046
64873
  init_contract();
64047
64874
  init_app_link();
64048
64875
  init_himi_report();
64049
-
64050
- // src/attestation.ts
64051
- import { execFileSync as execFileSync2 } from "node:child_process";
64052
- import { createHash as createHash3, randomBytes } from "node:crypto";
64053
- import {
64054
- existsSync as existsSync3,
64055
- lstatSync as lstatSync3,
64056
- mkdirSync as mkdirSync5,
64057
- realpathSync,
64058
- readlinkSync as readlinkSync2,
64059
- readFileSync as readFileSync5,
64060
- readdirSync as readdirSync3,
64061
- unlinkSync,
64062
- writeFileSync as writeFileSync5
64063
- } from "node:fs";
64064
- import { basename as basename2, join as join6, relative, resolve as resolve6 } from "node:path";
64065
-
64066
- // ../verify-gate/diff-hash.mjs
64067
- import {
64068
- lstatSync as lstatSync2,
64069
- mkdirSync as mkdirSync4,
64070
- mkdtempSync,
64071
- readFileSync as readFileSync4,
64072
- readlinkSync,
64073
- rmSync as rmSync2,
64074
- writeFileSync as writeFileSync4
64075
- } from "node:fs";
64076
- import { createHash as createHash2 } from "node:crypto";
64077
- import { tmpdir } from "node:os";
64078
- import { basename, dirname as dirname5, join as join5, resolve as resolve5 } from "node:path";
64079
- import { pathToFileURL } from "node:url";
64080
-
64081
- // ../pre-pr-review/scope.mjs
64082
- import { execFileSync } from "node:child_process";
64083
- import { createHash } from "node:crypto";
64084
- import { resolve as resolve4, dirname as dirname4, isAbsolute, join as join4 } from "node:path";
64085
- import { fileURLToPath as fileURLToPath3 } from "node:url";
64086
-
64087
- // ../ai-review-gate/paths.mjs
64088
- import { resolve as resolve3, dirname as dirname3 } from "node:path";
64089
- import { fileURLToPath as fileURLToPath2 } from "node:url";
64090
- var REPO_ROOT = resolve3(dirname3(fileURLToPath2(import.meta.url)), "../..");
64091
-
64092
- // ../pre-pr-review/command.mjs
64093
- var SHELL_NAMES = "sh|bash|zsh|ksh|dash|ash|fish|csh|tcsh|pwsh|xonsh|nu";
64094
- var SHELL_TOKEN = new RegExp(`^(?:.*/)?(?:${SHELL_NAMES})$`);
64095
-
64096
- // ../pre-pr-review/scope.mjs
64097
- var REPO_ROOT2 = resolve4(dirname4(fileURLToPath3(import.meta.url)), "../..");
64098
- var BASE_REF = process.env.PRE_PR_REVIEW_BASE || "origin/master";
64099
- function resolveRepoRoot(cwd = process.cwd()) {
64100
- let top;
64101
- try {
64102
- top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
64103
- cwd,
64104
- encoding: "utf8",
64105
- stdio: ["ignore", "pipe", "pipe"]
64106
- }).toString().trim();
64107
- } catch (err) {
64108
- throw new Error(`cannot resolve a repo root from ${cwd}: ${err?.message || err}`);
64109
- }
64110
- if (!top) throw new Error(`cannot resolve a repo root from ${cwd}: git named no toplevel`);
64111
- return top;
64112
- }
64113
- function git(args, opts = {}) {
64114
- return execFileSync("git", args, {
64115
- cwd: opts.cwd || REPO_ROOT2,
64116
- encoding: "utf8",
64117
- // A full PR diff can be megabytes; the default 1MB cap would truncate mid-hunk and the
64118
- // delta parser would then read a malformed diff as "unsupported shape".
64119
- maxBuffer: opts.maxBuffer ?? 256 * 1024 * 1024,
64120
- stdio: ["ignore", "pipe", "pipe"]
64121
- }).toString();
64122
- }
64123
- function diffHash(diff) {
64124
- return createHash("sha256").update(diff).digest("hex");
64125
- }
64126
-
64127
- // ../verify-gate/diff-hash.mjs
64128
- var VALIDATION_METADATA = /* @__PURE__ */ new Set([
64129
- ".claude/.validation-baseline.json",
64130
- ".claude/.validation-evidence.json"
64131
- ]);
64132
- function isValidationMetadata(path2) {
64133
- return VALIDATION_METADATA.has(path2) || /(^|[\\/])\.himi([\\/]|$)/.test(path2);
64134
- }
64135
- function tryGit(args, root) {
64136
- try {
64137
- return git(args, { cwd: root });
64138
- } catch {
64139
- return null;
64140
- }
64141
- }
64142
- function committedRecords(raw) {
64143
- const fields = raw.split("\0");
64144
- if (fields.length > 0 && fields[fields.length - 1] === "") fields.pop();
64145
- if (fields.length % 2 !== 0) return null;
64146
- const records = [];
64147
- for (let i = 0; i < fields.length; i += 2) {
64148
- const metadata2 = fields[i];
64149
- const path2 = fields[i + 1];
64150
- if (!metadata2.startsWith(":") || path2 === "") return null;
64151
- if (isValidationMetadata(path2)) continue;
64152
- records.push(`${Buffer.byteLength(path2)}:${path2}\0${metadata2}\0`);
64153
- }
64154
- return records.sort().join("");
64155
- }
64156
- function committedIdentity(root) {
64157
- const head = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root)?.trim();
64158
- if (!head) return "unborn\0";
64159
- const base = tryGit(["merge-base", BASE_REF, "HEAD"], root)?.trim();
64160
- if (base) {
64161
- const raw = tryGit(["diff", "--raw", "--no-renames", "-z", "--no-abbrev", base, "HEAD"], root);
64162
- const records = raw === null ? null : committedRecords(raw);
64163
- if (records !== null) return `base\0${base}\0${records}`;
64164
- }
64165
- const listing = tryGit(["ls-tree", "-rz", "--full-tree", "HEAD"], root);
64166
- const tree = listing === null ? null : treeRecords(listing);
64167
- return tree !== null ? `tree-listing\0${tree}` : `commit\0${head}\0`;
64168
- }
64169
- function treeRecords(listing) {
64170
- const records = [];
64171
- for (const record2 of listing.split("\0")) {
64172
- if (record2 === "") continue;
64173
- const tab = record2.indexOf(" ");
64174
- if (tab <= 0 || tab === record2.length - 1) return null;
64175
- const path2 = record2.slice(tab + 1);
64176
- if (isValidationMetadata(path2)) continue;
64177
- records.push(`${Buffer.byteLength(path2)}:${path2}\0${record2.slice(0, tab)}\0`);
64178
- }
64179
- return records.sort().join("");
64180
- }
64181
- function treeEntries(output, index = false) {
64182
- const entries = /* @__PURE__ */ new Map();
64183
- for (const record2 of output.split("\0").filter(Boolean)) {
64184
- const tab = record2.indexOf(" ");
64185
- if (tab === -1) continue;
64186
- const metadata2 = record2.slice(0, tab).split(" ");
64187
- if (index && metadata2[2] !== "0") continue;
64188
- entries.set(record2.slice(tab + 1), {
64189
- mode: metadata2[0],
64190
- oid: index ? metadata2[1] : metadata2[2]
64191
- });
64192
- }
64193
- return entries;
64194
- }
64195
- function sameEntry(left, right) {
64196
- if (!left || !right) return left === right;
64197
- return left.mode === right.mode && left.oid === right.oid;
64198
- }
64199
- function stagedIdentity(head, index, worktree) {
64200
- if (sameEntry(index, head) || sameEntry(index, worktree)) return "";
64201
- if (!index) return head ? "deleted" : "";
64202
- return `${index.mode}:${index.oid}`;
64203
- }
64204
- function blobOid(contents, algorithm) {
64205
- const body = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
64206
- return createHash2(algorithm).update(Buffer.from(`blob ${body.byteLength}\0`)).update(body).digest("hex");
64207
- }
64208
- function validationDiffHash(cwd = process.cwd()) {
64209
- const root = resolveRepoRoot(cwd);
64210
- const born = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root) !== null;
64211
- const staged = git(["diff", "--cached", "--name-only", "--no-renames", "-z", ...born ? ["HEAD"] : []], {
64212
- cwd: root
64213
- }).split("\0").filter(Boolean);
64214
- const unstaged = git(["diff", "--name-only", "--no-renames", "-z"], { cwd: root }).split("\0").filter(Boolean);
64215
- const untracked = git(["ls-files", "--others", "--exclude-standard", "-z"], {
64216
- cwd: root
64217
- }).split("\0").filter(Boolean);
64218
- const headEntries = born ? treeEntries(git(["ls-tree", "-rz", "--full-tree", "HEAD"], { cwd: root })) : /* @__PURE__ */ new Map();
64219
- const indexEntries = treeEntries(git(["ls-files", "--stage", "-z"], { cwd: root }), true);
64220
- const objectAlgorithm = git(["rev-parse", "--show-object-format"], { cwd: root }).trim();
64221
- let trustFilesystemMode = true;
64222
- try {
64223
- trustFilesystemMode = git(["config", "--bool", "--get", "core.fileMode"], { cwd: root }).trim() !== "false";
64224
- } catch {
64225
- }
64226
- const paths = [.../* @__PURE__ */ new Set([...staged, ...unstaged, ...untracked])].filter((path2) => !isValidationMetadata(path2)).sort();
64227
- const identity = paths.map((path2) => {
64228
- const absolute = resolve5(root, path2);
64229
- const headEntry = headEntries.get(path2);
64230
- const indexEntry = indexEntries.get(path2);
64231
- const deletedRecord = () => [`${Buffer.byteLength(path2)}:${path2}`, "deleted", stagedIdentity(headEntry, indexEntry, void 0), ""].join("\0");
64232
- let stat2;
64233
- try {
64234
- stat2 = lstatSync2(absolute);
64235
- } catch (error) {
64236
- if (error?.code === "ENOENT") return deletedRecord();
64237
- throw error;
64238
- }
64239
- try {
64240
- if (stat2.isSymbolicLink()) {
64241
- const target = readlinkSync(absolute);
64242
- const worktreeEntry = { mode: "120000", oid: blobOid(target, objectAlgorithm) };
64243
- const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
64244
- return [
64245
- `${Buffer.byteLength(path2)}:${path2}`,
64246
- "symlink",
64247
- "120000",
64248
- diffHash(target),
64249
- stagedState2,
64250
- ""
64251
- ].join("\0");
64252
- }
64253
- if (stat2.isFile()) {
64254
- const filesystemMode = stat2.mode & 73 ? "100755" : "100644";
64255
- const worktreeMode = trustFilesystemMode ? filesystemMode : indexEntry?.mode ?? headEntry?.mode ?? "100644";
64256
- const worktreeOid = git(["hash-object", `--path=${path2}`, "--", path2], { cwd: root }).trim();
64257
- const worktreeEntry = { mode: worktreeMode, oid: worktreeOid };
64258
- const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
64259
- return [
64260
- `${Buffer.byteLength(path2)}:${path2}`,
64261
- "file",
64262
- worktreeMode,
64263
- diffHash(readFileSync4(absolute)),
64264
- stagedState2,
64265
- ""
64266
- ].join("\0");
64267
- }
64268
- } catch (error) {
64269
- try {
64270
- lstatSync2(absolute);
64271
- } catch (recheck) {
64272
- if (recheck?.code === "ENOENT") return deletedRecord();
64273
- }
64274
- throw error;
64275
- }
64276
- const type = stat2.isDirectory() ? "directory" : "other";
64277
- const stagedState = stagedIdentity(headEntry, indexEntry, void 0);
64278
- return [
64279
- `${Buffer.byteLength(path2)}:${path2}`,
64280
- type,
64281
- stat2.mode.toString(8),
64282
- stagedState,
64283
- ""
64284
- ].join("\0");
64285
- }).join("");
64286
- const committed = committedIdentity(root);
64287
- return diffHash(
64288
- `validation-change-v3\0${Buffer.byteLength(committed)}\0${committed}${identity}`
64289
- );
64290
- }
64291
- function seed(repo, commands) {
64292
- for (const args of commands) git(args, { cwd: repo });
64293
- }
64294
- function write(repo, rel, body) {
64295
- const absolute = resolve5(repo, rel);
64296
- mkdirSync4(dirname5(absolute), { recursive: true });
64297
- writeFileSync4(absolute, body);
64298
- }
64299
- function selfTestRepo({ withBase }) {
64300
- const repo = mkdtempSync(join5(tmpdir(), "diff-hash-self-test-"));
64301
- seed(repo, [
64302
- ["init", "-q", repo],
64303
- ["-C", repo, "config", "user.email", "self-test@test.invalid"],
64304
- ["-C", repo, "config", "user.name", "diff-hash-self-test"]
64305
- ]);
64306
- write(repo, "src/base.txt", "base\n");
64307
- seed(repo, [["-C", repo, "add", "-A"], ["-C", repo, "commit", "-qm", "base"]]);
64308
- const base = git(["-C", repo, "rev-parse", "HEAD"], { cwd: repo }).trim();
64309
- if (withBase) seed(repo, [["-C", repo, "update-ref", "refs/remotes/origin/master", base]]);
64310
- return { repo, base };
64311
- }
64312
- function commitChange(repo, base, branch, rel, body) {
64313
- seed(repo, [["-C", repo, "checkout", "-q", base], ["-C", repo, "checkout", "-q", "-b", branch]]);
64314
- write(repo, rel, body);
64315
- seed(repo, [["-C", repo, "add", rel], ["-C", repo, "commit", "-qm", `commit ${rel}`]]);
64316
- return validationDiffHash(repo);
64317
- }
64318
- var SELF_TEST_CASES = [
64319
- {
64320
- name: "committed diffs are distinguished (the #1831 regression)",
64321
- reason: "two unrelated COMMITTED diffs share one identity \u2014 a record for either would satisfy the gate for the other",
64322
- run: () => {
64323
- const { repo, base } = selfTestRepo({ withBase: true });
64324
- try {
64325
- return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
64326
- } finally {
64327
- rmSync2(repo, { recursive: true, force: true });
64328
- }
64329
- }
64330
- },
64331
- {
64332
- name: "committed diffs are distinguished with NO base ref (fallback ladder)",
64333
- reason: "without origin/master the committed identity collapses to a constant",
64334
- run: () => {
64335
- const { repo, base } = selfTestRepo({ withBase: false });
64336
- try {
64337
- return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
64338
- } finally {
64339
- rmSync2(repo, { recursive: true, force: true });
64340
- }
64341
- }
64342
- },
64343
- {
64344
- name: "a clean tree is not the digest of an empty path list",
64345
- reason: 'a clean tree still hashes to sha256("validation-worktree-v2\\0"), the constant measured in #1831',
64346
- run: () => {
64347
- const { repo } = selfTestRepo({ withBase: true });
64348
- try {
64349
- return validationDiffHash(repo) !== createHash2("sha256").update("validation-worktree-v2\0").digest("hex");
64350
- } finally {
64351
- rmSync2(repo, { recursive: true, force: true });
64352
- }
64353
- }
64354
- },
64355
- {
64356
- name: "an uncommitted edit still changes the identity",
64357
- reason: "the working-tree half of the identity stopped responding to edits",
64358
- run: () => {
64359
- const { repo } = selfTestRepo({ withBase: true });
64360
- try {
64361
- const before = validationDiffHash(repo);
64362
- write(repo, "src/base.txt", "edited in the working tree\n");
64363
- return validationDiffHash(repo) !== before;
64364
- } finally {
64365
- rmSync2(repo, { recursive: true, force: true });
64366
- }
64367
- }
64368
- },
64369
- {
64370
- // The "can pass" half. Without it every case above is satisfied by a hash that simply
64371
- // changes every call, which would identify nothing and invalidate every record.
64372
- name: "one unchanged state hashes to one value",
64373
- reason: "the identity is not stable for an unchanged state \u2014 every record would read stale",
64374
- run: () => {
64375
- const { repo } = selfTestRepo({ withBase: true });
64376
- try {
64377
- write(repo, "src/base.txt", "edited in the working tree\n");
64378
- return validationDiffHash(repo) === validationDiffHash(repo);
64379
- } finally {
64380
- rmSync2(repo, { recursive: true, force: true });
64381
- }
64382
- }
64383
- }
64384
- ];
64385
- function runSelfTest() {
64386
- let failed2 = 0;
64387
- for (const testCase of SELF_TEST_CASES) {
64388
- let ok = false;
64389
- let thrown = null;
64390
- try {
64391
- ok = testCase.run() === true;
64392
- } catch (error) {
64393
- thrown = error?.message || String(error);
64394
- }
64395
- if (ok) {
64396
- process.stdout.write(` \u2713 ${testCase.name}
64397
- `);
64398
- continue;
64399
- }
64400
- failed2++;
64401
- process.stderr.write(` \u2717 ${testCase.name}
64402
- ${thrown ?? testCase.reason}
64403
- `);
64404
- }
64405
- return failed2;
64406
- }
64407
- if (process.argv[1] && basename(process.argv[1]) === "diff-hash.mjs" && import.meta.url === pathToFileURL(process.argv[1]).href) {
64408
- if (process.argv.slice(2).includes("--self-test")) {
64409
- process.stdout.write("\u25B8 diff-hash --self-test: proving the identity can change, and can hold\n");
64410
- const failed2 = runSelfTest();
64411
- if (failed2) {
64412
- process.stderr.write(
64413
- `\u2717 diff-hash self-test FAILED (${failed2} case(s)) \u2014 the evidence record's diff identity cannot be trusted
64414
- `
64415
- );
64416
- process.exit(1);
64417
- }
64418
- process.stdout.write(`\u2713 diff-hash self-test passed (${SELF_TEST_CASES.length} cases)
64419
- `);
64420
- } else {
64421
- process.stdout.write(`${validationDiffHash()}
64422
- `);
64423
- }
64424
- }
64425
-
64426
- // src/attestation.ts
64427
- init_source_deny();
64428
- init_widget_identity();
64429
- var DEFAULT_MAX_RUNS = 20;
64430
- var ATTESTATION_VERSION = 2;
64431
- function attestationVersionOf(value) {
64432
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
64433
- const version2 = value.attestation;
64434
- return typeof version2 === "number" && Number.isInteger(version2) && version2 > 0 ? version2 : null;
64435
- }
64436
- function classifyRetainedRun(dir, name) {
64437
- let value;
64438
- try {
64439
- value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
64440
- } catch {
64441
- return { kind: "unreadable" };
64442
- }
64443
- if (isAttestationV2(value)) return { kind: "current", run: value };
64444
- const version2 = attestationVersionOf(value);
64445
- if (version2 === null || version2 === ATTESTATION_VERSION) return { kind: "malformed" };
64446
- return { kind: version2 < ATTESTATION_VERSION ? "legacy" : "future" };
64447
- }
64448
- function screenshotArtifactPath(contentDir2, hash) {
64449
- return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
64450
- }
64451
- var GIT_HASH_MAX_BUFFER = 128 * 1024 * 1024;
64452
- var sha2562 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
64453
- function canonical(value) {
64454
- if (value === null || typeof value !== "object") return JSON.stringify(value);
64455
- if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
64456
- const record2 = value;
64457
- return `{${Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${canonical(record2[key2])}`).join(",")}}`;
64458
- }
64459
- function releasePayloadHash(payload) {
64460
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha2562(canonical(payload));
64461
- const record2 = payload;
64462
- const manifest = record2.manifest;
64463
- if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha2562(canonical(payload));
64464
- const manifestRecord = manifest;
64465
- const widgets = manifestRecord.widgets;
64466
- if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha2562(canonical(payload));
64467
- return sha2562(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
64468
- }
64469
- function withoutSelfHash(attestation) {
64470
- const { selfHash: _selfHash, ...body } = attestation;
64471
- return body;
64472
- }
64473
- function selfHashFor(attestation) {
64474
- return sha2562(canonical(withoutSelfHash(attestation)));
64475
- }
64476
- function repoRoot(contentDir2) {
64477
- try {
64478
- const root = execFileSync2("git", ["-C", contentDir2, "rev-parse", "--show-toplevel"], {
64479
- encoding: "utf8",
64480
- stdio: ["ignore", "pipe", "ignore"]
64481
- }).trim();
64482
- if (!root) return null;
64483
- execFileSync2("git", ["-C", root, "rev-parse", "--verify", "--quiet", "HEAD^{commit}"], { stdio: "ignore" });
64484
- return root;
64485
- } catch {
64486
- return null;
64487
- }
64488
- }
64489
- function storedContentDir(contentDir2) {
64490
- const absolute = resolve6(contentDir2);
64491
- const root = repoRoot(absolute);
64492
- return root ? relative(root, absolute) || "." : ".";
64493
- }
64494
- function currentGitDiffHash(contentDir2) {
64495
- const root = repoRoot(resolve6(contentDir2));
64496
- if (!root) return "unavailable";
64497
- return validationDiffHash(root);
64498
- }
64499
- function currentGitUntrackedHash(contentDir2) {
64500
- const unresolved = resolve6(contentDir2);
64501
- const absolute = realpathSync.native(unresolved);
64502
- const root = repoRoot(absolute);
64503
- if (!root) return sourceTreeHash(absolute);
64504
- const scope = relative(root, absolute) || ".";
64505
- const listed = execFileSync2("git", ["-C", root, "ls-files", "--others", "--exclude-standard", "-z", "--", scope], {
64506
- encoding: "utf8",
64507
- maxBuffer: GIT_HASH_MAX_BUFFER
64508
- });
64509
- const hash = createHash3("sha256");
64510
- for (const path2 of listed.split("\0").filter(Boolean).sort()) {
64511
- const local = relative(absolute, join6(root, path2));
64512
- const segs = local.split(/[\\/]/);
64513
- if (NOT_ATTESTED_DIRS.has(segs[0])) continue;
64514
- if (segs.length === 1 && AGENT_TOOLING_ROOT_FILES.includes(segs[0])) continue;
64515
- const full = join6(root, path2);
64516
- const stats = lstatSync3(full, { throwIfNoEntry: false });
64517
- if (!stats) continue;
64518
- const kind = stats.isSymbolicLink() ? "link" : stats.isDirectory() ? "dir" : stats.isFile() ? "file" : null;
64519
- if (!kind) continue;
64520
- const payload = kind === "link" ? Buffer.from(readlinkSync2(full)) : kind === "file" ? readFileSync5(full) : Buffer.alloc(0);
64521
- const pathBytes = Buffer.from(path2);
64522
- hash.update(`${kind} ${pathBytes.length} ${payload.length}
64523
- `).update(pathBytes).update(payload);
64524
- }
64525
- return hash.digest("hex");
64526
- }
64527
- var NOT_ATTESTED_DIRS = /* @__PURE__ */ new Set([".himi", ...AGENT_TOOLING_DIRS]);
64528
- function sourceTreeHash(root) {
64529
- const hash = createHash3("sha256");
64530
- const visit = (dir) => {
64531
- for (const entry of readdirSync3(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
64532
- if (entry.isDirectory() && (entry.name === ".git" || entry.name === "node_modules" || NOT_ATTESTED_DIRS.has(entry.name))) continue;
64533
- const path2 = join6(dir, entry.name);
64534
- const local = relative(root, path2);
64535
- if (dir === root && entry.isFile() && AGENT_TOOLING_ROOT_FILES.includes(entry.name)) continue;
64536
- if (entry.isDirectory()) visit(path2);
64537
- else if (entry.isFile()) hash.update(local).update("\0").update(readFileSync5(path2)).update("\0");
64538
- else if (entry.isSymbolicLink()) hash.update(local).update("\0link\0").update(readlinkSync2(path2)).update("\0");
64539
- }
64540
- };
64541
- visit(root);
64542
- return hash.digest("hex");
64543
- }
64544
- var stringRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
64545
- function isAttestationV2(value) {
64546
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
64547
- const a = value;
64548
- const coverage = a.coverage;
64549
- const report2 = a.report;
64550
- return a.attestation === 2 && typeof a.runId === "string" && ["himi-test", "himi-render", "himi-shots"].includes(a.tool) && [a.app, a.contentDir, a.gitDiffHash, a.gitUntrackedHash, a.releaseId, a.releaseHash, a.selfHash].every((item) => typeof item === "string" && item.length > 0) && stringRecord(a.descriptorHashes) && stringRecord(a.screenshots) && !!coverage && [coverage.screens, coverage.actionsDispatched, coverage.uncoveredFns, coverage.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && typeof report2?.ok === "boolean" && !!report2.counts && [report2.counts.errors, report2.counts.warnings, report2.counts.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && (a.prev === null || typeof a.prev === "string");
64551
- }
64552
- function readLatestAttestation(contentDir2) {
64553
- try {
64554
- const parsed = JSON.parse(readFileSync5(join6(resolve6(contentDir2), ".himi", "attestations", "latest.json"), "utf8"));
64555
- return isAttestationV2(parsed) ? parsed : null;
64556
- } catch {
64557
- return null;
64558
- }
64559
- }
64560
- function readRetainedAttestations(contentDir2) {
64561
- const dir = join6(resolve6(contentDir2), ".himi", "attestations");
64562
- if (!existsSync3(dir)) return { runs: [], errors: [] };
64563
- try {
64564
- const names = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
64565
- const parsedRuns = [];
64566
- const errors = [];
64567
- for (const name of names) {
64568
- const classified = classifyRetainedRun(dir, name);
64569
- if (classified.kind === "current") parsedRuns.push(classified.run);
64570
- else if (classified.kind === "legacy") {
64571
- errors.push(`attestation history contains retained run ${name} from an older attestation version \u2014 re-run himi test/render to start a v${ATTESTATION_VERSION} chain`);
64572
- } else if (classified.kind === "future") {
64573
- errors.push(`attestation history contains retained run ${name} from a newer attestation version \u2014 upgrade himi rather than deleting it`);
64574
- } else if (classified.kind === "unreadable") {
64575
- errors.push(`attestation history contains unreadable retained run ${name}`);
64576
- } else errors.push(`attestation history contains malformed retained run ${name}`);
64577
- }
64578
- const latest = readLatestAttestation(contentDir2);
64579
- if (existsSync3(join6(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
64580
- else if (names.length > 0 && !latest) errors.push("attestation history has no latest.json");
64581
- const byHash = new Map(parsedRuns.map((run2) => [run2.selfHash, run2]));
64582
- if (byHash.size !== parsedRuns.length) errors.push("attestation history contains duplicate self hashes");
64583
- for (const run2 of parsedRuns) {
64584
- if (selfHashFor(run2) !== run2.selfHash) errors.push(`selfHash does not match retained run ${run2.runId}`);
64585
- for (const expected of Object.values(run2.screenshots)) {
64586
- const path2 = screenshotArtifactPath(contentDir2, expected);
64587
- if (!existsSync3(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename2(path2)}`);
64588
- else if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
64589
- }
64590
- }
64591
- const runs = [];
64592
- const seen = /* @__PURE__ */ new Set();
64593
- let cursor = latest ?? void 0;
64594
- while (cursor && !seen.has(cursor.selfHash)) {
64595
- runs.push(cursor);
64596
- seen.add(cursor.selfHash);
64597
- cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
64598
- }
64599
- if (latest && !byHash.has(latest.selfHash)) errors.push("latest.json has no matching retained run");
64600
- if (seen.size !== parsedRuns.length) errors.push("attestation hash chain does not reach every retained run");
64601
- runs.push(...parsedRuns.filter((run2) => !seen.has(run2.selfHash)).sort((a, b) => a.runId < b.runId ? 1 : -1));
64602
- return {
64603
- runs,
64604
- errors
64605
- };
64606
- } catch {
64607
- return { runs: [], errors: ["attestation history could not be read"] };
64608
- }
64609
- }
64610
- function selectAttestationForPush(runs) {
64611
- const newestBehavioral = runs.find((run2) => run2.tool === "himi-test");
64612
- if (!newestBehavioral) return runs[0];
64613
- const behavioralIndex = runs.indexOf(newestBehavioral);
64614
- const newerFailure = runs.slice(0, behavioralIndex).find((run2) => run2.report.ok === false);
64615
- return newerFailure ?? newestBehavioral;
64616
- }
64617
- function pushEvidence(contentDir2, releaseId, verify) {
64618
- const retained = readRetainedAttestations(contentDir2);
64619
- const forRelease = retained.runs.filter((candidate) => candidate.releaseId === releaseId);
64620
- const verified = forRelease.filter(verify);
64621
- const selected = selectAttestationForPush(verified);
64622
- return {
64623
- ...selected ? { selected } : {},
64624
- historyErrors: retained.errors,
64625
- staleRuns: verified.length === 0 ? forRelease.length : 0
64626
- };
64627
- }
64628
- function summaryOf(attestation) {
64629
- return {
64630
- runId: attestation.runId,
64631
- selfHash: attestation.selfHash,
64632
- tool: attestation.tool,
64633
- screenIds: Object.keys(attestation.descriptorHashes).sort(),
64634
- coverage: attestation.coverage,
64635
- report: { ok: attestation.report.ok, counts: attestation.report.counts },
64636
- gitDiffHash: attestation.gitDiffHash,
64637
- releaseHash: attestation.releaseHash
64638
- };
64639
- }
64640
- function writeAttestation(input, opts = {}) {
64641
- const contentDir2 = resolve6(input.contentDir);
64642
- const dir = join6(contentDir2, ".himi", "attestations");
64643
- mkdirSync5(dir, { recursive: true });
64644
- const previous = readLatestAttestation(contentDir2);
64645
- const runId = `${(opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()}-${(opts.randomSuffix ?? (() => randomBytes(3).toString("hex")))()}`;
64646
- const descriptorHashes = Object.fromEntries(
64647
- Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha2562(bytes2)])
64648
- );
64649
- const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a], [b]) => a.localeCompare(b)).filter(([, path2]) => existsSync3(path2)).map(([screen, path2]) => {
64650
- const bytes2 = readFileSync5(path2);
64651
- return { screen, bytes: bytes2, hash: sha2562(bytes2) };
64652
- });
64653
- const screenshots = Object.fromEntries(screenshotEntries.map(({ screen, hash }) => [screen, hash]));
64654
- for (const { bytes: bytes2, hash } of screenshotEntries) {
64655
- const artifact = screenshotArtifactPath(contentDir2, hash);
64656
- mkdirSync5(join6(dir, "artifacts"), { recursive: true });
64657
- if (!existsSync3(artifact)) writeFileSync5(artifact, bytes2);
64658
- }
64659
- const unsigned = {
64660
- attestation: 2,
64661
- runId,
64662
- tool: input.tool,
64663
- app: input.app,
64664
- contentDir: storedContentDir(contentDir2),
64665
- gitDiffHash: input.gitDiffHash,
64666
- releaseId: input.releaseId,
64667
- releaseHash: releasePayloadHash(input.releasePayload),
64668
- gitUntrackedHash: currentGitUntrackedHash(contentDir2),
64669
- descriptorHashes,
64670
- screenshots,
64671
- coverage: input.coverage,
64672
- report: input.report,
64673
- prev: previous?.selfHash ?? null
64674
- };
64675
- const attestation = { ...unsigned, selfHash: "" };
64676
- attestation.selfHash = selfHashFor(attestation);
64677
- const json5 = JSON.stringify(attestation, null, 2) + "\n";
64678
- const runFile = `${runId.replace(/:/g, "-")}.json`;
64679
- writeFileSync5(join6(dir, runFile), json5);
64680
- writeFileSync5(join6(dir, "latest.json"), json5);
64681
- const keep = Math.max(1, opts.maxRuns ?? DEFAULT_MAX_RUNS);
64682
- const allPriorRuns = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").filter((name) => name !== runFile).sort().reverse();
64683
- const priorRuns = allPriorRuns.filter((name) => {
64684
- const kind = classifyRetainedRun(dir, name).kind;
64685
- if (kind === "legacy") {
64686
- unlinkSync(join6(dir, name));
64687
- return false;
64688
- }
64689
- return kind !== "future";
64690
- });
64691
- for (const name of priorRuns.slice(Math.max(0, keep - 1))) unlinkSync(join6(dir, name));
64692
- const retainedFiles = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
64693
- const retainedRuns = retainedFiles.map((name) => {
64694
- const classified = classifyRetainedRun(dir, name);
64695
- return classified.kind === "current" ? classified.run : null;
64696
- });
64697
- if (retainedRuns.every((run2) => run2 !== null)) {
64698
- const retainedScreenshotHashes = new Set(
64699
- retainedRuns.flatMap((run2) => Object.values(run2.screenshots))
64700
- );
64701
- const artifactDir = join6(dir, "artifacts");
64702
- if (existsSync3(artifactDir)) {
64703
- for (const name of readdirSync3(artifactDir).filter((candidate) => candidate.endsWith(".png"))) {
64704
- if (!retainedScreenshotHashes.has(name.slice(0, -4))) unlinkSync(join6(artifactDir, name));
64705
- }
64706
- }
64707
- }
64708
- return attestation;
64709
- }
64710
- function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, releasePayload, opts = {}) {
64711
- const errors = [];
64712
- if (selfHashFor(attestation) !== attestation.selfHash) errors.push("selfHash does not match the attestation bytes");
64713
- try {
64714
- const dir = join6(resolve6(contentDir2), ".himi", "attestations");
64715
- const latest = readLatestAttestation(contentDir2);
64716
- if (!latest || opts.requireLatest !== false && latest.selfHash !== attestation.selfHash) {
64717
- errors.push("attestation does not match latest.json");
64718
- } else {
64719
- const classified = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").map((name) => classifyRetainedRun(dir, name)).filter((entry) => entry.kind !== "future");
64720
- const retained = classified.map((entry) => entry.kind === "current" ? entry.run : null);
64721
- if (retained.some((run2) => run2 === null)) {
64722
- errors.push("attestation history contains a malformed retained run");
64723
- } else {
64724
- const runs = retained;
64725
- const byHash = new Map(runs.map((run2) => [run2.selfHash, run2]));
64726
- if (!byHash.has(attestation.selfHash)) errors.push("attestation has no matching retained run");
64727
- if (!byHash.has(latest.selfHash)) {
64728
- errors.push("latest.json has no matching retained run");
64729
- } else {
64730
- const seen = /* @__PURE__ */ new Set();
64731
- let cursor = latest;
64732
- while (cursor && !seen.has(cursor.selfHash)) {
64733
- seen.add(cursor.selfHash);
64734
- if (selfHashFor(cursor) !== cursor.selfHash) {
64735
- errors.push(`selfHash does not match retained run ${cursor.runId}`);
64736
- break;
64737
- }
64738
- cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
64739
- }
64740
- if (seen.size !== runs.length) errors.push("attestation hash chain does not reach every retained run");
64741
- }
64742
- }
64743
- }
64744
- } catch {
64745
- errors.push("attestation history could not be verified");
64746
- }
64747
- if (attestation.app !== expectedApp) errors.push(`attestation app does not match ${expectedApp}`);
64748
- if (attestation.contentDir !== storedContentDir(contentDir2)) errors.push("attestation contentDir does not match the requested content directory");
64749
- if (releasePayloadHash(releasePayload) !== attestation.releaseHash) {
64750
- errors.push("releaseHash does not match the current release payload");
64751
- }
64752
- try {
64753
- const liveDiffHash = currentGitDiffHash(contentDir2);
64754
- if (liveDiffHash !== "unavailable" && liveDiffHash !== attestation.gitDiffHash) {
64755
- errors.push("gitDiffHash does not match the live git diff");
64756
- }
64757
- } catch {
64758
- errors.push("gitDiffHash could not be recomputed from the live git diff");
64759
- }
64760
- try {
64761
- const liveUntrackedHash = currentGitUntrackedHash(contentDir2);
64762
- if (liveUntrackedHash !== "unavailable" && liveUntrackedHash !== attestation.gitUntrackedHash) {
64763
- errors.push("gitUntrackedHash does not match the live untracked source bytes");
64764
- }
64765
- } catch {
64766
- errors.push("gitUntrackedHash could not be recomputed from the live untracked source bytes");
64767
- }
64768
- for (const [screen, expected] of Object.entries(attestation.descriptorHashes)) {
64769
- const bytes2 = descriptors[screen];
64770
- if (bytes2 === void 0) {
64771
- errors.push(`missing baked descriptor for ${screen}`);
64772
- continue;
64773
- }
64774
- if (sha2562(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
64775
- }
64776
- for (const [screen, expected] of Object.entries(attestation.screenshots)) {
64777
- const path2 = screenshotArtifactPath(contentDir2, expected);
64778
- if (!existsSync3(path2)) {
64779
- errors.push(`missing screenshot for ${screen}: ${basename2(path2)}`);
64780
- continue;
64781
- }
64782
- if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
64783
- }
64784
- return { ok: errors.length === 0, errors };
64785
- }
64876
+ init_attestation();
64786
64877
 
64787
64878
  // ../../core/admin-web/src/utils/qr-terminal.ts
64788
64879
  init_qr();
@@ -68001,7 +68092,7 @@ function nativeConfigTokenNotice(configSource, optedIn) {
68001
68092
  }
68002
68093
  async function nativeConfigAdvisories(app, configSource) {
68003
68094
  try {
68004
- const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
68095
+ const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
68005
68096
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
68006
68097
  const iosYmlPath = iosProjectYml2(app);
68007
68098
  const iosYml = existsSync63(iosYmlPath) ? readFileSync75(iosYmlPath, "utf8") : "";
@@ -68522,7 +68613,7 @@ function uxHandoverHint(uxEvidence, ctx) {
68522
68613
  if (uxEvidence.state === "failed") {
68523
68614
  return `a mobile-UX drive for ${releaseId} IS recorded, and it FAILED on ${uxEvidence.failedPlatforms.join(", ")} (see ${uxEvidence.path}). A published draft is the owner-private preview a customer is handed, so fix what the drive found and re-record it \u2014 re-running \`himi ux record\` over a failure without changing the app just writes the same failure down again. Only if you intend to hand a customer a preview whose UX you already know is broken, \`${door} --force\`.`;
68524
68615
  }
68525
- const why = uxEvidence.state === "absent" ? `nothing has recorded a mobile-UX drive for this package \u2014 ${uxEvidence.path} does not exist` : uxEvidence.state === "unrecorded" ? uxEvidence.malformed.length ? `${uxEvidence.path} has no iOS or Android drive this gate can read \u2014 ${uxEvidence.malformed.join("; ")}` : `${uxEvidence.path} exists but carries no iOS or Android drive (\`himi render\` writes its rendered widths there too, so the file alone proves nothing)` : uxEvidence.state === "unreadable" ? `${ctx.quote(`${ctx.contentDir}/${uxEvidence.path}`)} ${uxEvidence.error} \u2014 delete it and record again, because re-recording over a malformed store does not repair it` : uxEvidence.state === "outdated" && ctx.nondeterministicWorkers?.length ? `a mobile-UX drive for ${releaseId} is recorded, but it describes different content from the release this push just built \u2014 an uncommitted edit since the record would do that, and so does the worker named below` : uxEvidence.state === "outdated" ? `a mobile-UX drive for ${releaseId} is recorded, but the package has CHANGED since \u2014 in-repo a release id is the git HEAD sha, so editing a screen, a token or a worker without committing keeps the id while replacing everything it identifies (a record written before this check also lands here, carrying no content hash)` : `the recorded mobile-UX drive is about ${uxEvidence.otherReleases.length ? `release ${uxEvidence.otherReleases.join(", ")}` : "no release at all"}, not ${releaseId}${uxEvidence.unstamped ? ` (${uxEvidence.unstamped} drive(s) name no release)` : ""}`;
68616
+ const why = uxEvidence.state === "absent" ? `nothing has recorded a mobile-UX drive for this package \u2014 ${uxEvidence.path} does not exist` : uxEvidence.state === "unrecorded" ? uxEvidence.malformed.length ? `${uxEvidence.path} has no iOS or Android drive this gate can read \u2014 ${uxEvidence.malformed.join("; ")}` : `${uxEvidence.path} exists but carries no iOS or Android drive (\`himi render\` writes its rendered widths there too, so the file alone proves nothing)` : uxEvidence.state === "unreadable" ? `${ctx.quote(`${ctx.contentDir}/${uxEvidence.path}`)} ${uxEvidence.error} \u2014 delete it and record again, because re-recording over a malformed store does not repair it` : uxEvidence.state === "outdated" && ctx.nondeterministicWorkers?.length ? `a mobile-UX drive for an earlier build of this commit is recorded, but it describes different content from the release this push just built (${releaseId}) \u2014 an uncommitted edit since the record would do that, and so does the worker named below` : uxEvidence.state === "outdated" ? `a mobile-UX drive for an earlier build of this commit is recorded, but the package has CHANGED since \u2014 editing a screen, a token or a worker without committing makes a new release, ${releaseId} (a record written before this check also lands here, carrying no content hash)` : `the recorded mobile-UX drive is about ${uxEvidence.otherReleases.length ? `release ${uxEvidence.otherReleases.join(", ")}` : "no release at all"}, not ${releaseId}${uxEvidence.unstamped ? ` (${uxEvidence.unstamped} drive(s) name no release)` : ""}`;
68526
68617
  const drifting = ctx.nondeterministicWorkers ?? [];
68527
68618
  if (drifting.length > 0) {
68528
68619
  return `${why}. ${releaseDriftCause(drifting, "Re-recording", "a drive")}; then record the drive: ${uxRemedy}, and re-run \`${door}\`. Only if you intend to hand over a preview WITHOUT that evidence, \`${door} --force\`.`;
@@ -68757,7 +68848,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68757
68848
  };
68758
68849
  const runReleasePatch = async (opts) => {
68759
68850
  const { app, plan, draft } = opts;
68760
- const { regressionDiff: regressionDiff2, patchHonesty: patchHonesty2 } = await Promise.resolve().then(() => (init_release_patch(), release_patch_exports));
68851
+ const { regressionDiff: regressionDiff2, patchHonesty: patchHonesty2 } = await Promise.resolve().then(() => (init_release_patch2(), release_patch_exports));
68761
68852
  const target = targetName();
68762
68853
  const baseFound = await fetchReleaseManifest(client2, app, opts.fromRelease);
68763
68854
  if ("error" in baseFound) {
@@ -69166,12 +69257,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69166
69257
  try {
69167
69258
  const bytes2 = readFileSync75(iconPath);
69168
69259
  payload.icon = bytes2.toString("base64");
69169
- iconHash = createHash27("sha256").update(bytes2).digest("hex");
69260
+ iconHash = createHash28("sha256").update(bytes2).digest("hex");
69170
69261
  } catch {
69171
69262
  }
69172
69263
  }
69173
69264
  payload.app = templateAppId;
69174
- payload.manifest = { ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} };
69265
+ const { rebindReleaseId: rebindReleaseId2 } = await Promise.resolve().then(() => (init_build3(), build_exports2));
69266
+ payload.manifest = rebindReleaseId2({ ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} });
69175
69267
  const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
69176
69268
  if (Object.keys(files).length === 0) {
69177
69269
  io.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
@@ -70797,7 +70889,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70797
70889
  const contentArg = shellQuote2(contentRel);
70798
70890
  if (onlyRaw !== void 0) {
70799
70891
  const onlyDraft = flags.draft === true;
70800
- const { parseOnlySelectors: parseOnlySelectors2, planFromPayload: planFromPayload2, notCarriedDiff: notCarriedDiff2 } = await Promise.resolve().then(() => (init_release_patch(), release_patch_exports));
70892
+ const { parseOnlySelectors: parseOnlySelectors2, planFromPayload: planFromPayload2, notCarriedDiff: notCarriedDiff2 } = await Promise.resolve().then(() => (init_release_patch2(), release_patch_exports));
70801
70893
  const parsed = parseOnlySelectors2(onlyRaw, {
70802
70894
  screens: Object.keys(manifest.screens ?? {}),
70803
70895
  bundles: Object.keys(manifest.bundles ?? {})
@@ -70855,7 +70947,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70855
70947
  let localSourceHash;
70856
70948
  try {
70857
70949
  const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
70858
- localSourceHash = createHash27("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
70950
+ localSourceHash = createHash28("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
70859
70951
  } catch {
70860
70952
  }
70861
70953
  return runReleasePatch({
@@ -70930,7 +71022,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70930
71022
  }
70931
71023
  const evidence = pushEvidence(dir, manifest.releaseId, (candidate) => verifyAttestation(candidate, dir, payload.screens, declared, payload, { requireLatest: false }).ok);
70932
71024
  const drifting = nondeterministicWorkers(outcome.workers);
70933
- const staleEvidence = evidence.staleRuns > 0 ? `${evidence.staleRuns} retained attestation(s) carry this release id, but none describes the content being published` : null;
71025
+ const staleEvidence = evidence.staleRuns > 0 ? `${evidence.staleRuns} retained attestation(s) were recorded at this commit, but none describes the content being published` : null;
70934
71026
  const attestationErrors = [...evidence.historyErrors, ...staleEvidence ? [staleEvidence] : []];
70935
71027
  if (attestationErrors.length > 0 && !force) {
70936
71028
  const remedy = `\`himi test --content ${contentArg}\` (or \`himi render <screenId> --content ${contentArg}\`)`;
@@ -70939,7 +71031,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70939
71031
  ok: false,
70940
71032
  pushed: false,
70941
71033
  errors: attestationErrors,
70942
- hint: corrupted ? `attestation history in .himi/attestations/ is corrupt, so the retained runs cannot be trusted \u2014 a fresh run does not remove the bad entry. Delete .himi/attestations/, re-attest with ${remedy}, then re-run \`himi push\`. Only if you intend to publish WITHOUT that evidence, \`himi push --force\`.` : `no retained attestation for this release passed live verification${drifting.length ? `. ${releaseDriftCause(drifting, "Re-attesting", "an attestation")}, then re-attest` : " \u2014 a release id is the HEAD sha, so runs recorded before your latest edits still carry it while describing a tree that no longer exists. Re-attest"} the CURRENT release with ${remedy}, then re-run \`himi push\`. Only if you intend to publish WITHOUT that evidence, \`himi push --force\`.`
71034
+ hint: corrupted ? `attestation history in .himi/attestations/ is corrupt, so the retained runs cannot be trusted \u2014 a fresh run does not remove the bad entry. Delete .himi/attestations/, re-attest with ${remedy}, then re-run \`himi push\`. Only if you intend to publish WITHOUT that evidence, \`himi push --force\`.` : `no retained attestation for this release passed live verification${drifting.length ? `. ${releaseDriftCause(drifting, "Re-attesting", "an attestation")}, then re-attest` : " \u2014 runs recorded at this commit before your latest edits describe a tree that no longer exists. Re-attest"} the CURRENT release with ${remedy}, then re-run \`himi push\`. Only if you intend to publish WITHOUT that evidence, \`himi push --force\`.`
70943
71035
  });
70944
71036
  return 1;
70945
71037
  }
@@ -72404,7 +72496,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72404
72496
  });
72405
72497
  }
72406
72498
  const bytes2 = readFileSync75(file);
72407
- const sha = createHash27("sha256").update(bytes2).digest("hex");
72499
+ const sha = createHash28("sha256").update(bytes2).digest("hex");
72408
72500
  const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
72409
72501
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
72410
72502
  io.error(JSON.stringify({ ok: false, error: "no_rebind_takes_no_value", message: "--no-rebind is a switch and takes no value; pass it bare.", usage: PATCH_USAGE }));
@@ -72740,7 +72832,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72740
72832
  const { preflightApp: preflightApp2 } = await Promise.resolve().then(() => (init_preflight_app(), preflight_app_exports));
72741
72833
  const { resolveStoreCredentials: resolveStoreCredentials2 } = await Promise.resolve().then(() => (init_credential_profile(), credential_profile_exports));
72742
72834
  const { readIosVersion: readIosVersion2, bumpVersion: bumpVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
72743
- const { iosProjectYml: iosYmlPath, hasRepoCheckout: hasRepoCheckout2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
72835
+ const { iosProjectYml: iosYmlPath, hasRepoCheckout: hasRepoCheckout2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
72744
72836
  const { ascStatus: ascStatus2 } = await Promise.resolve().then(() => (init_asc_status(), asc_status_exports));
72745
72837
  const { readFileSync: rf, readdirSync: rd, existsSync: ex, statSync: st } = await import("node:fs");
72746
72838
  const { execFile: execFile2 } = await import("node:child_process");