@wix/himalaya-cli 0.1055.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.
- package/dist/cli.mjs +1305 -1217
- package/dist/preview-assets.generated.bin +0 -0
- 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
|
-
//
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
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
|
-
//
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
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
|
-
|
|
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
|
|
2196
|
-
|
|
2197
|
-
|
|
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
|
|
2200
|
-
|
|
2201
|
-
return void 0;
|
|
2226
|
+
function diffHash(diff) {
|
|
2227
|
+
return createHash("sha256").update(diff).digest("hex");
|
|
2202
2228
|
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
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
|
-
|
|
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
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
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
|
|
2227
|
-
const
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
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
|
-
|
|
2244
|
-
return out;
|
|
2276
|
+
return records.sort().join("");
|
|
2245
2277
|
}
|
|
2246
|
-
function
|
|
2247
|
-
const
|
|
2248
|
-
|
|
2249
|
-
const
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
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
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
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
|
-
|
|
2269
|
-
return out;
|
|
2301
|
+
return records.sort().join("");
|
|
2270
2302
|
}
|
|
2271
|
-
function
|
|
2272
|
-
|
|
2273
|
-
const
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
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
|
|
2281
|
-
|
|
2282
|
-
|
|
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
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
return
|
|
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
|
|
2291
|
-
|
|
2292
|
-
|
|
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
|
|
2322
|
-
const
|
|
2323
|
-
const
|
|
2324
|
-
|
|
2325
|
-
|
|
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
|
|
2328
|
-
const
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
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
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
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
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
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
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
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
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2404
|
-
|
|
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
|
-
//
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
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
|
-
|
|
2470
|
-
|
|
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
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
"
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
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
|
|
2518
|
-
const
|
|
2519
|
-
|
|
2643
|
+
function storedContentDir(contentDir2) {
|
|
2644
|
+
const absolute = resolve6(contentDir2);
|
|
2645
|
+
const root = repoRoot(absolute);
|
|
2646
|
+
return root ? relative(root, absolute) || "." : ".";
|
|
2520
2647
|
}
|
|
2521
|
-
function
|
|
2522
|
-
|
|
2648
|
+
function currentGitDiffHash(contentDir2) {
|
|
2649
|
+
const root = repoRoot(resolve6(contentDir2));
|
|
2650
|
+
if (!root) return "unavailable";
|
|
2651
|
+
return validationDiffHash(root);
|
|
2523
2652
|
}
|
|
2524
|
-
function
|
|
2525
|
-
const
|
|
2526
|
-
|
|
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
|
|
2529
|
-
|
|
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
|
|
2532
|
-
if (
|
|
2533
|
-
|
|
2534
|
-
const
|
|
2535
|
-
|
|
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
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
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
|
|
2544
|
-
|
|
2545
|
-
if (
|
|
2546
|
-
|
|
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
|
|
2549
|
-
const
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
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
|
-
|
|
2574
|
-
|
|
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
|
|
2578
|
-
const
|
|
2579
|
-
if (
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
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
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
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
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
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
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
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
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
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
|
|
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
|
-
|
|
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";
|
|
@@ -14629,11 +15406,12 @@ function uxEvidenceForRelease(content, release) {
|
|
|
14629
15406
|
const failedPlatforms = forRelease.filter(([, entry]) => entry.status === "failed").map(([platform]) => platform);
|
|
14630
15407
|
return failedPlatforms.length > 0 ? { ...base, state: "failed", platforms, failedPlatforms, malformed } : { ...base, state: "recorded", platforms, malformed };
|
|
14631
15408
|
}
|
|
14632
|
-
|
|
15409
|
+
const sameCommit = recorded.filter(([, entry]) => sameReleaseCommit(entry.releaseId, releaseId));
|
|
15410
|
+
if (sameCommit.length > 0) {
|
|
14633
15411
|
return {
|
|
14634
15412
|
...base,
|
|
14635
15413
|
state: "outdated",
|
|
14636
|
-
platforms:
|
|
15414
|
+
platforms: sameCommit.map(([platform]) => platform),
|
|
14637
15415
|
malformed
|
|
14638
15416
|
};
|
|
14639
15417
|
}
|
|
@@ -14650,6 +15428,7 @@ var NATIVE_PLATFORMS, NATIVE_AUDIT_STATUSES;
|
|
|
14650
15428
|
var init_ux_evidence = __esm({
|
|
14651
15429
|
"src/ux-evidence.ts"() {
|
|
14652
15430
|
init_mobile_ux_contract();
|
|
15431
|
+
init_attestation();
|
|
14653
15432
|
NATIVE_PLATFORMS = ["ios", "android"];
|
|
14654
15433
|
NATIVE_AUDIT_STATUSES = ["passed", "failed", "unavailable"];
|
|
14655
15434
|
}
|
|
@@ -15947,7 +16726,7 @@ function generationAllowed(app, configTs, opts = {}) {
|
|
|
15947
16726
|
var DEFAULT_ACCENT_TOKEN, DEFAULT_GLYPH_SCALE, GLYPH_SCALE_MIN, GLYPH_SCALE_MAX, LEGACY_PLACEHOLDER_SHA256;
|
|
15948
16727
|
var init_plan2 = __esm({
|
|
15949
16728
|
"../app-icons/plan.ts"() {
|
|
15950
|
-
|
|
16729
|
+
init_paths2();
|
|
15951
16730
|
init_glyphs();
|
|
15952
16731
|
DEFAULT_ACCENT_TOKEN = "accentPrimary";
|
|
15953
16732
|
DEFAULT_GLYPH_SCALE = 0.62;
|
|
@@ -16015,7 +16794,7 @@ var ANDROID_ICON_BACKGROUND_RES, ANDROID_ADAPTIVE_FILES, ANDROID_ICON_BACKGROUND
|
|
|
16015
16794
|
var init_icon2 = __esm({
|
|
16016
16795
|
"../native-config/icon.ts"() {
|
|
16017
16796
|
init_plan2();
|
|
16018
|
-
|
|
16797
|
+
init_paths2();
|
|
16019
16798
|
ANDROID_ICON_BACKGROUND_RES = "ic_launcher_background";
|
|
16020
16799
|
ANDROID_ADAPTIVE_FILES = ["ic_launcher.xml", "ic_launcher_round.xml"];
|
|
16021
16800
|
ANDROID_ICON_BACKGROUND_FILE = "ic_launcher_background.xml";
|
|
@@ -16524,7 +17303,7 @@ var init_gen_native_config = __esm({
|
|
|
16524
17303
|
"../native-config/gen-native-config.ts"() {
|
|
16525
17304
|
init_src2();
|
|
16526
17305
|
init_src2();
|
|
16527
|
-
|
|
17306
|
+
init_paths2();
|
|
16528
17307
|
init_splash();
|
|
16529
17308
|
init_icon2();
|
|
16530
17309
|
init_live_activity();
|
|
@@ -22033,7 +22812,6 @@ function shouldRevealError(args) {
|
|
|
22033
22812
|
var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
|
|
22034
22813
|
var init_validation = __esm({
|
|
22035
22814
|
"../../core/ts/src/validation.ts"() {
|
|
22036
|
-
"use strict";
|
|
22037
22815
|
EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
|
|
22038
22816
|
URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
|
|
22039
22817
|
INTEGER_RE = /^[+-]?[0-9]+$/;
|
|
@@ -25634,7 +26412,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
|
|
|
25634
26412
|
var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
|
|
25635
26413
|
var init_worker_subtree = __esm({
|
|
25636
26414
|
"../../core/ts/src/worker-subtree.ts"() {
|
|
25637
|
-
"use strict";
|
|
25638
26415
|
init_worker_subtree_safe_types_generated();
|
|
25639
26416
|
WORKER_SUBTREE_CEILINGS = Object.freeze({
|
|
25640
26417
|
maxEncodedBytes: 65536,
|
|
@@ -26948,7 +27725,7 @@ function ensureInitialFocusRuntime() {
|
|
|
26948
27725
|
initialFocusScheduled = false;
|
|
26949
27726
|
const targets = Array.from(document.querySelectorAll("[data-himalaya-initial-focus]"));
|
|
26950
27727
|
const active = document.activeElement;
|
|
26951
|
-
const unplaced = active == null || active === document.body;
|
|
27728
|
+
const unplaced = active == null || active === document.body || active.hasAttribute?.("data-himalaya-focus-landing") === true;
|
|
26952
27729
|
const fresh = targets.filter((t) => !initialFocusSeen.has(t));
|
|
26953
27730
|
let presented;
|
|
26954
27731
|
let presentedScope = null;
|
|
@@ -37209,6 +37986,44 @@ var init_src6 = __esm({
|
|
|
37209
37986
|
}
|
|
37210
37987
|
});
|
|
37211
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
|
+
|
|
37212
38027
|
// ../../core/dev-server/src/net-scenario.ts
|
|
37213
38028
|
function resolveScenario(rules, req, upstream) {
|
|
37214
38029
|
const ctx = {
|
|
@@ -39922,11 +40737,11 @@ var init_dependencies = __esm({
|
|
|
39922
40737
|
});
|
|
39923
40738
|
|
|
39924
40739
|
// ../../core/functions-build/src/sbom.ts
|
|
39925
|
-
import { createHash as
|
|
40740
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
39926
40741
|
function createFunctionSbom(packages) {
|
|
39927
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 }));
|
|
39928
40743
|
const body = JSON.stringify({ format: "himalaya-sbom-v1", components: components2 });
|
|
39929
|
-
return { format: "himalaya-sbom-v1", components: components2, hash:
|
|
40744
|
+
return { format: "himalaya-sbom-v1", components: components2, hash: createHash18("sha256").update(body).digest("hex") };
|
|
39930
40745
|
}
|
|
39931
40746
|
var init_sbom = __esm({
|
|
39932
40747
|
"../../core/functions-build/src/sbom.ts"() {
|
|
@@ -39934,12 +40749,12 @@ var init_sbom = __esm({
|
|
|
39934
40749
|
});
|
|
39935
40750
|
|
|
39936
40751
|
// ../../core/functions-build/src/build.ts
|
|
39937
|
-
import { createHash as
|
|
40752
|
+
import { createHash as createHash19 } from "node:crypto";
|
|
39938
40753
|
function sha2563(value) {
|
|
39939
|
-
return
|
|
40754
|
+
return createHash19("sha256").update(value).digest("hex");
|
|
39940
40755
|
}
|
|
39941
40756
|
function sri(value) {
|
|
39942
|
-
return `sha256-${
|
|
40757
|
+
return `sha256-${createHash19("sha256").update(value).digest("base64")}`;
|
|
39943
40758
|
}
|
|
39944
40759
|
function schemaHash(schema) {
|
|
39945
40760
|
if (!schema) return "";
|
|
@@ -40120,6 +40935,7 @@ __export(build_exports2, {
|
|
|
40120
40935
|
pinBakedServerBaseUrl: () => pinBakedServerBaseUrl,
|
|
40121
40936
|
pinnedReleaseFromEnv: () => pinnedReleaseFromEnv,
|
|
40122
40937
|
publishableFontFaces: () => publishableFontFaces,
|
|
40938
|
+
rebindReleaseId: () => rebindReleaseId,
|
|
40123
40939
|
releaseIdFor: () => releaseIdFor,
|
|
40124
40940
|
servicesFromBundleJs: () => servicesFromBundleJs,
|
|
40125
40941
|
shellSha: () => shellSha,
|
|
@@ -40129,7 +40945,7 @@ __export(build_exports2, {
|
|
|
40129
40945
|
withStoreNavBundle: () => withStoreNavBundle,
|
|
40130
40946
|
withoutDevOnlyAppConfig: () => withoutDevOnlyAppConfig
|
|
40131
40947
|
});
|
|
40132
|
-
import { createHash as
|
|
40948
|
+
import { createHash as createHash20 } from "node:crypto";
|
|
40133
40949
|
import { readFileSync as readFileSync42, readdirSync as readdirSync21, existsSync as existsSync39 } from "node:fs";
|
|
40134
40950
|
import { dirname as dirname23, join as join37, resolve as resolve32 } from "node:path";
|
|
40135
40951
|
import { fileURLToPath as fileURLToPath19 } from "node:url";
|
|
@@ -40199,10 +41015,10 @@ async function bakeCatalogs(app, locale, fetchCatalog, putDoc) {
|
|
|
40199
41015
|
return out;
|
|
40200
41016
|
}
|
|
40201
41017
|
function sha2564(buf) {
|
|
40202
|
-
return
|
|
41018
|
+
return createHash20("sha256").update(buf).digest("hex");
|
|
40203
41019
|
}
|
|
40204
41020
|
function sri2(buf) {
|
|
40205
|
-
return "sha256-" +
|
|
41021
|
+
return "sha256-" + createHash20("sha256").update(buf).digest("base64");
|
|
40206
41022
|
}
|
|
40207
41023
|
async function widgetFailureDetail(res) {
|
|
40208
41024
|
try {
|
|
@@ -40298,14 +41114,14 @@ function pinBakedServerBaseUrl(json5, base) {
|
|
|
40298
41114
|
const boundary = /:\d+$/.test(base) ? "(?!\\d)" : "(?![\\w.-])(?!:\\d)";
|
|
40299
41115
|
return json5.replace(new RegExp(`${escaped}${boundary}`, "g"), BAKED_SERVER_BASE_URL);
|
|
40300
41116
|
}
|
|
40301
|
-
function
|
|
41117
|
+
function stableStringify2(v) {
|
|
40302
41118
|
if (v === null || typeof v !== "object") return JSON.stringify(v);
|
|
40303
|
-
if (Array.isArray(v)) return "[" + v.map(
|
|
41119
|
+
if (Array.isArray(v)) return "[" + v.map(stableStringify2).join(",") + "]";
|
|
40304
41120
|
const o = v;
|
|
40305
|
-
return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" +
|
|
41121
|
+
return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + stableStringify2(o[k])).join(",") + "}";
|
|
40306
41122
|
}
|
|
40307
41123
|
function contentReleaseId(content) {
|
|
40308
|
-
return sha2564(
|
|
41124
|
+
return sha2564(stableStringify2(content));
|
|
40309
41125
|
}
|
|
40310
41126
|
function servicesFromBundleJs(bundleJs, input = {}) {
|
|
40311
41127
|
try {
|
|
@@ -40320,7 +41136,13 @@ function hydrationServices(tier5Dir, bundleName, input = {}) {
|
|
|
40320
41136
|
return js ? servicesFromBundleJs(js, input) : null;
|
|
40321
41137
|
}
|
|
40322
41138
|
function releaseIdFor(sha, content) {
|
|
40323
|
-
|
|
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) };
|
|
40324
41146
|
}
|
|
40325
41147
|
function shellSha(root = REPO_ROOT7) {
|
|
40326
41148
|
if (!isMonorepoCheckout(root)) return "";
|
|
@@ -41265,6 +42087,7 @@ var init_build3 = __esm({
|
|
|
41265
42087
|
init_src6();
|
|
41266
42088
|
init_check();
|
|
41267
42089
|
init_file_store();
|
|
42090
|
+
init_release_patch();
|
|
41268
42091
|
init_validate_descriptor();
|
|
41269
42092
|
init_wix_member_login();
|
|
41270
42093
|
init_wix_placeholders();
|
|
@@ -41927,7 +42750,7 @@ __export(studio_exports, {
|
|
|
41927
42750
|
renderStudioHtml: () => renderStudioHtml,
|
|
41928
42751
|
writeStudioRecord: () => writeStudioRecord
|
|
41929
42752
|
});
|
|
41930
|
-
import { createHash as
|
|
42753
|
+
import { createHash as createHash21 } from "node:crypto";
|
|
41931
42754
|
import { existsSync as existsSync41, readdirSync as readdirSync22, readFileSync as readFileSync44, realpathSync as realpathSync5, statSync as statSync8 } from "node:fs";
|
|
41932
42755
|
import { basename as basename10, extname as extname3, join as join39, relative as relative7, resolve as resolve33, sep as sep9 } from "node:path";
|
|
41933
42756
|
import { pathToFileURL as pathToFileURL7 } from "node:url";
|
|
@@ -42165,7 +42988,7 @@ function buildView(dir, memory, now) {
|
|
|
42165
42988
|
reveal: memory.reveal ?? null
|
|
42166
42989
|
};
|
|
42167
42990
|
const { generatedAt: _ignored, timelineFrames: _count, ...stable } = view;
|
|
42168
|
-
const v =
|
|
42991
|
+
const v = createHash21("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
|
|
42169
42992
|
return { v, ...view };
|
|
42170
42993
|
}
|
|
42171
42994
|
function writeAtomic(file, content) {
|
|
@@ -42663,7 +43486,7 @@ function patchHonesty(opts) {
|
|
|
42663
43486
|
return { attestation, source };
|
|
42664
43487
|
}
|
|
42665
43488
|
var SELECTOR_KINDS;
|
|
42666
|
-
var
|
|
43489
|
+
var init_release_patch2 = __esm({
|
|
42667
43490
|
"src/release-patch.ts"() {
|
|
42668
43491
|
init_widget_identity();
|
|
42669
43492
|
SELECTOR_KINDS = ["changed", "screen:<id|glob>", "bundle:<name>", "tokens", "app-config"];
|
|
@@ -45409,7 +46232,7 @@ __export(pack_exports, {
|
|
|
45409
46232
|
unpackTo: () => unpackTo,
|
|
45410
46233
|
writeZip: () => writeZip
|
|
45411
46234
|
});
|
|
45412
|
-
import { createHash as
|
|
46235
|
+
import { createHash as createHash22 } from "node:crypto";
|
|
45413
46236
|
import { deflateRawSync, inflateRawSync } from "node:zlib";
|
|
45414
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";
|
|
45415
46238
|
import { dirname as dirname26, join as join42, resolve as resolve37 } from "node:path";
|
|
@@ -45559,11 +46382,11 @@ function gatherReleaseEntries(appDir2) {
|
|
|
45559
46382
|
return { entries, manifest, skipped };
|
|
45560
46383
|
}
|
|
45561
46384
|
function sha256Hex4(bytes2) {
|
|
45562
|
-
return
|
|
46385
|
+
return createHash22("sha256").update(bytes2).digest("hex");
|
|
45563
46386
|
}
|
|
45564
46387
|
function computeContentHash(entries) {
|
|
45565
46388
|
const input = packageHashInput(entries.map((e) => ({ path: e.path, hash: sha256Hex4(e.bytes) })));
|
|
45566
|
-
return "sha256-" +
|
|
46389
|
+
return "sha256-" + createHash22("sha256").update(input).digest("base64");
|
|
45567
46390
|
}
|
|
45568
46391
|
function buildEntries(appDir2, app, opts) {
|
|
45569
46392
|
const { entries: released, manifest, skipped } = gatherReleaseEntries(appDir2);
|
|
@@ -47772,10 +48595,10 @@ var init_calls = __esm({
|
|
|
47772
48595
|
});
|
|
47773
48596
|
|
|
47774
48597
|
// ../../core/functions-runtime/src/queue.ts
|
|
47775
|
-
import { createHash as
|
|
48598
|
+
import { createHash as createHash23 } from "node:crypto";
|
|
47776
48599
|
function inputSchemaHash(definition) {
|
|
47777
48600
|
const schema = definition.input;
|
|
47778
|
-
return schema ?
|
|
48601
|
+
return schema ? createHash23("sha256").update(JSON.stringify(schema.descriptor ?? { kind: schema.kind, optional: schema.optional === true })).digest("hex") : "";
|
|
47779
48602
|
}
|
|
47780
48603
|
function createFunctionEnqueueCapability(options) {
|
|
47781
48604
|
return {
|
|
@@ -47841,14 +48664,14 @@ var init_queue = __esm({
|
|
|
47841
48664
|
});
|
|
47842
48665
|
|
|
47843
48666
|
// ../../core/data-resources/src/plan.ts
|
|
47844
|
-
import { createHash as
|
|
48667
|
+
import { createHash as createHash24 } from "node:crypto";
|
|
47845
48668
|
function canonical2(value) {
|
|
47846
48669
|
if (Array.isArray(value)) return `[${value.map(canonical2).join(",")}]`;
|
|
47847
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(",")}}`;
|
|
47848
48671
|
return JSON.stringify(value);
|
|
47849
48672
|
}
|
|
47850
48673
|
function hashResourcePlan(plan) {
|
|
47851
|
-
return
|
|
48674
|
+
return createHash24("sha256").update(canonical2(plan)).digest("hex");
|
|
47852
48675
|
}
|
|
47853
48676
|
function finalize(input) {
|
|
47854
48677
|
return { ...input, hash: hashResourcePlan(input) };
|
|
@@ -48784,7 +49607,7 @@ __export(studio_reel_exports, {
|
|
|
48784
49607
|
thin: () => thin,
|
|
48785
49608
|
utcDay: () => utcDay
|
|
48786
49609
|
});
|
|
48787
|
-
import { createHash as
|
|
49610
|
+
import { createHash as createHash25 } from "node:crypto";
|
|
48788
49611
|
import { lstatSync as lstatSync9, readFileSync as readFileSync55 } from "node:fs";
|
|
48789
49612
|
import { basename as basename12, join as join49 } from "node:path";
|
|
48790
49613
|
function loadMemory2(dir) {
|
|
@@ -48872,7 +49695,7 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
|
|
|
48872
49695
|
const st = lstatSync9(path2);
|
|
48873
49696
|
if (!st.isFile() || st.nlink !== 1) return null;
|
|
48874
49697
|
const bytes2 = readFileSync55(path2);
|
|
48875
|
-
return
|
|
49698
|
+
return createHash25("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
|
|
48876
49699
|
} catch {
|
|
48877
49700
|
return null;
|
|
48878
49701
|
}
|
|
@@ -49292,7 +50115,7 @@ __export(browser_authoring_exports, {
|
|
|
49292
50115
|
sdkSubpathReader: () => sdkSubpathReader
|
|
49293
50116
|
});
|
|
49294
50117
|
import { createServer as createServer3 } from "node:http";
|
|
49295
|
-
import { createHash as
|
|
50118
|
+
import { createHash as createHash26, randomBytes as randomBytes4 } from "node:crypto";
|
|
49296
50119
|
import { existsSync as existsSync52, readdirSync as readdirSync26, readFileSync as readFileSync57, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
|
|
49297
50120
|
import { join as join51, posix as posix2, relative as relative9, sep as sep12 } from "node:path";
|
|
49298
50121
|
function humanMs(ms) {
|
|
@@ -49391,7 +50214,7 @@ function encodeJob(job) {
|
|
|
49391
50214
|
return Buffer.from(JSON.stringify(job), "utf8").toString("base64");
|
|
49392
50215
|
}
|
|
49393
50216
|
function mintToken2(payload, nonce = randomBytes4(16).toString("hex")) {
|
|
49394
|
-
const digest2 =
|
|
50217
|
+
const digest2 = createHash26("sha256").update(payload, "utf8").digest("base64url");
|
|
49395
50218
|
return `${nonce}.${digest2}`;
|
|
49396
50219
|
}
|
|
49397
50220
|
function defaultOpenBrowser2(url) {
|
|
@@ -50314,7 +51137,7 @@ function makeServeHandler(opts) {
|
|
|
50314
51137
|
}
|
|
50315
51138
|
const PINNED_MANIFEST_CACHE_CAP = 100;
|
|
50316
51139
|
const pinnedManifestCache = /* @__PURE__ */ new Map();
|
|
50317
|
-
const
|
|
51140
|
+
const sha256hex2 = (b) => sha256Hex(b);
|
|
50318
51141
|
function resolvePin(request, url) {
|
|
50319
51142
|
return request.headers.get("x-himalaya-release") || url.searchParams.get("release") || null;
|
|
50320
51143
|
}
|
|
@@ -50367,7 +51190,7 @@ ${releaseId}`;
|
|
|
50367
51190
|
const doc = store.getDoc ? await store.getDoc(app, hash) : null;
|
|
50368
51191
|
if (doc) return doc;
|
|
50369
51192
|
const bytes2 = await slot();
|
|
50370
|
-
return bytes2 &&
|
|
51193
|
+
return bytes2 && sha256hex2(bytes2) === hash ? bytes2 : null;
|
|
50371
51194
|
}
|
|
50372
51195
|
function callbackUrlFor(url) {
|
|
50373
51196
|
const base = ((typeof opts.publicBaseUrl === "function" ? opts.publicBaseUrl(url) : opts.publicBaseUrl) ?? url.origin).replace(/\/$/, "");
|
|
@@ -51031,7 +51854,7 @@ ${releaseId}`;
|
|
|
51031
51854
|
}
|
|
51032
51855
|
}
|
|
51033
51856
|
}
|
|
51034
|
-
const cfgTag = ringPinned ?
|
|
51857
|
+
const cfgTag = ringPinned ? sha256hex2(JSON.stringify(appConfig)) : mm.appConfigHash ?? "";
|
|
51035
51858
|
const etag = strongEtag(`${mm.releaseId}:${cfgTag}:${mm.tokensHash ?? ""}:${screenHash}:${homeBundle?.sha256 ?? ""}`);
|
|
51036
51859
|
const cache = pin && !ringPinned ? PINNED_CACHE_CONTROL : DOC_CACHE_CONTROL;
|
|
51037
51860
|
const vary = pin ? { vary: ringPinned ? `x-himalaya-release, ${RELEASE_SOURCE_HEADER}` : "x-himalaya-release" } : {};
|
|
@@ -51634,21 +52457,21 @@ var init_preview_assets_generated = __esm({
|
|
|
51634
52457
|
"../../packages/serve/src/preview-assets.generated.ts"() {
|
|
51635
52458
|
init_preview_assets_format();
|
|
51636
52459
|
PREVIEW_ASSET_MANIFEST = [
|
|
51637
|
-
{ key: "assets/index-
|
|
51638
|
-
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset:
|
|
51639
|
-
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset:
|
|
51640
|
-
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset:
|
|
51641
|
-
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
51642
|
-
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset:
|
|
51643
|
-
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset:
|
|
51644
|
-
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
51645
|
-
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset:
|
|
51646
|
-
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
51647
|
-
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset:
|
|
51648
|
-
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset:
|
|
51649
|
-
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
51650
|
-
{ key: "index.html", contentType: "text/html; charset=utf-8", offset:
|
|
51651
|
-
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset:
|
|
52460
|
+
{ key: "assets/index-BpZ7D4Bh.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1058524, sha256: "cc7f9edeb0a351f0d1fbc18d622044afaf3112c567893f04135a5cf846e2c4d2" },
|
|
52461
|
+
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1058532, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
|
|
52462
|
+
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1063142, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
|
|
52463
|
+
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1551726, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
|
|
52464
|
+
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1642650, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
|
|
52465
|
+
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1733634, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
|
|
52466
|
+
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1824614, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
|
|
52467
|
+
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1915486, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
|
|
52468
|
+
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2006466, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
|
|
52469
|
+
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2115102, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
|
|
52470
|
+
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2224186, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
|
|
52471
|
+
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2333234, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
|
|
52472
|
+
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2440934, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
|
|
52473
|
+
{ key: "index.html", contentType: "text/html; charset=utf-8", offset: 2529042, length: 4947, sha256: "bed413299e95d197f054aae695914a18403bef8f893a8ed89a87068cbd0cab80" },
|
|
52474
|
+
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2533989, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
|
|
51652
52475
|
];
|
|
51653
52476
|
}
|
|
51654
52477
|
});
|
|
@@ -52778,7 +53601,7 @@ function loadStoreConfig(app) {
|
|
|
52778
53601
|
}
|
|
52779
53602
|
var init_preflight = __esm({
|
|
52780
53603
|
"../store-release/preflight.ts"() {
|
|
52781
|
-
|
|
53604
|
+
init_paths2();
|
|
52782
53605
|
}
|
|
52783
53606
|
});
|
|
52784
53607
|
|
|
@@ -52918,7 +53741,7 @@ __export(preflight_app_exports, {
|
|
|
52918
53741
|
profileAccount: () => profileAccount
|
|
52919
53742
|
});
|
|
52920
53743
|
import { readFileSync as readFileSync66, existsSync as existsSync60, readdirSync as readdirSync28, statSync as statSync15 } from "node:fs";
|
|
52921
|
-
import { createHash as
|
|
53744
|
+
import { createHash as createHash27 } from "node:crypto";
|
|
52922
53745
|
import { join as join57 } from "node:path";
|
|
52923
53746
|
function analyze(projectYml, storeConfig, icon, account) {
|
|
52924
53747
|
const f = [];
|
|
@@ -53207,7 +54030,7 @@ function placeholderHashes2() {
|
|
|
53207
54030
|
const p = join57(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
|
|
53208
54031
|
try {
|
|
53209
54032
|
if (!statSync15(p).isFile()) continue;
|
|
53210
|
-
return [LEGACY_PLACEHOLDER_SHA2562,
|
|
54033
|
+
return [LEGACY_PLACEHOLDER_SHA2562, createHash27("sha256").update(readFileSync66(p)).digest("hex")];
|
|
53211
54034
|
} catch {
|
|
53212
54035
|
}
|
|
53213
54036
|
}
|
|
@@ -53244,7 +54067,7 @@ async function appIconState(iosDir) {
|
|
|
53244
54067
|
try {
|
|
53245
54068
|
if (!statSync15(p).isFile()) continue;
|
|
53246
54069
|
const bytes2 = readFileSync66(p);
|
|
53247
|
-
const hash =
|
|
54070
|
+
const hash = createHash27("sha256").update(bytes2).digest("hex");
|
|
53248
54071
|
const pixels = await pixelIssues(bytes2);
|
|
53249
54072
|
return {
|
|
53250
54073
|
exists: true,
|
|
@@ -53279,7 +54102,7 @@ ${app}: ${errors.length} error(s), ${warns.length} warning(s).`);
|
|
|
53279
54102
|
var APP_STORE_SIZES, PNG_COLOR_TYPE_RGB, PLAY_FEATURE_GRAPHIC, LEGACY_PLACEHOLDER_SHA2562;
|
|
53280
54103
|
var init_preflight_app = __esm({
|
|
53281
54104
|
"../store-release/preflight-app.ts"() {
|
|
53282
|
-
|
|
54105
|
+
init_paths2();
|
|
53283
54106
|
init_preflight();
|
|
53284
54107
|
init_credential_profile();
|
|
53285
54108
|
init_app_icon_rules();
|
|
@@ -53386,7 +54209,7 @@ function bumpVersion(app, opts) {
|
|
|
53386
54209
|
}
|
|
53387
54210
|
var init_version = __esm({
|
|
53388
54211
|
"../store-release/version.ts"() {
|
|
53389
|
-
|
|
54212
|
+
init_paths2();
|
|
53390
54213
|
}
|
|
53391
54214
|
});
|
|
53392
54215
|
|
|
@@ -53445,7 +54268,7 @@ async function main2() {
|
|
|
53445
54268
|
var ASC_API;
|
|
53446
54269
|
var init_register_bundle = __esm({
|
|
53447
54270
|
"../store-release/register-bundle.ts"() {
|
|
53448
|
-
|
|
54271
|
+
init_paths2();
|
|
53449
54272
|
init_preflight();
|
|
53450
54273
|
ASC_API = "https://api.appstoreconnect.apple.com";
|
|
53451
54274
|
if (runningAsScript("store-release/register-bundle.ts")) {
|
|
@@ -53510,7 +54333,7 @@ async function main3() {
|
|
|
53510
54333
|
var ASC;
|
|
53511
54334
|
var init_asc_status = __esm({
|
|
53512
54335
|
"../store-release/asc-status.ts"() {
|
|
53513
|
-
|
|
54336
|
+
init_paths2();
|
|
53514
54337
|
init_register_bundle();
|
|
53515
54338
|
init_preflight();
|
|
53516
54339
|
ASC = "https://api.appstoreconnect.apple.com";
|
|
@@ -63450,7 +64273,7 @@ var init_context = __esm({
|
|
|
63450
64273
|
// src/cli.ts
|
|
63451
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";
|
|
63452
64275
|
import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
|
|
63453
|
-
import { createHash as
|
|
64276
|
+
import { createHash as createHash28, randomUUID as randomUUID5 } from "node:crypto";
|
|
63454
64277
|
import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
|
|
63455
64278
|
import { join as join61, resolve as resolve47, relative as relative11, basename as basename15, dirname as dirname34 } from "node:path";
|
|
63456
64279
|
import { fileURLToPath as fileURLToPath24 } from "node:url";
|
|
@@ -64050,743 +64873,7 @@ init_store();
|
|
|
64050
64873
|
init_contract();
|
|
64051
64874
|
init_app_link();
|
|
64052
64875
|
init_himi_report();
|
|
64053
|
-
|
|
64054
|
-
// src/attestation.ts
|
|
64055
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
64056
|
-
import { createHash as createHash3, randomBytes } from "node:crypto";
|
|
64057
|
-
import {
|
|
64058
|
-
existsSync as existsSync3,
|
|
64059
|
-
lstatSync as lstatSync3,
|
|
64060
|
-
mkdirSync as mkdirSync5,
|
|
64061
|
-
realpathSync,
|
|
64062
|
-
readlinkSync as readlinkSync2,
|
|
64063
|
-
readFileSync as readFileSync5,
|
|
64064
|
-
readdirSync as readdirSync3,
|
|
64065
|
-
unlinkSync,
|
|
64066
|
-
writeFileSync as writeFileSync5
|
|
64067
|
-
} from "node:fs";
|
|
64068
|
-
import { basename as basename2, join as join6, relative, resolve as resolve6 } from "node:path";
|
|
64069
|
-
|
|
64070
|
-
// ../verify-gate/diff-hash.mjs
|
|
64071
|
-
import {
|
|
64072
|
-
lstatSync as lstatSync2,
|
|
64073
|
-
mkdirSync as mkdirSync4,
|
|
64074
|
-
mkdtempSync,
|
|
64075
|
-
readFileSync as readFileSync4,
|
|
64076
|
-
readlinkSync,
|
|
64077
|
-
rmSync as rmSync2,
|
|
64078
|
-
writeFileSync as writeFileSync4
|
|
64079
|
-
} from "node:fs";
|
|
64080
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
64081
|
-
import { tmpdir } from "node:os";
|
|
64082
|
-
import { basename, dirname as dirname5, join as join5, resolve as resolve5 } from "node:path";
|
|
64083
|
-
import { pathToFileURL } from "node:url";
|
|
64084
|
-
|
|
64085
|
-
// ../pre-pr-review/scope.mjs
|
|
64086
|
-
import { execFileSync } from "node:child_process";
|
|
64087
|
-
import { createHash } from "node:crypto";
|
|
64088
|
-
import { resolve as resolve4, dirname as dirname4, isAbsolute, join as join4 } from "node:path";
|
|
64089
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
64090
|
-
|
|
64091
|
-
// ../ai-review-gate/paths.mjs
|
|
64092
|
-
import { resolve as resolve3, dirname as dirname3 } from "node:path";
|
|
64093
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
64094
|
-
var REPO_ROOT = resolve3(dirname3(fileURLToPath2(import.meta.url)), "../..");
|
|
64095
|
-
|
|
64096
|
-
// ../pre-pr-review/command.mjs
|
|
64097
|
-
var SHELL_NAMES = "sh|bash|zsh|ksh|dash|ash|fish|csh|tcsh|pwsh|xonsh|nu";
|
|
64098
|
-
var SHELL_TOKEN = new RegExp(`^(?:.*/)?(?:${SHELL_NAMES})$`);
|
|
64099
|
-
|
|
64100
|
-
// ../pre-pr-review/scope.mjs
|
|
64101
|
-
var REPO_ROOT2 = resolve4(dirname4(fileURLToPath3(import.meta.url)), "../..");
|
|
64102
|
-
var BASE_REF = process.env.PRE_PR_REVIEW_BASE || "origin/master";
|
|
64103
|
-
function resolveRepoRoot(cwd = process.cwd()) {
|
|
64104
|
-
let top;
|
|
64105
|
-
try {
|
|
64106
|
-
top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
64107
|
-
cwd,
|
|
64108
|
-
encoding: "utf8",
|
|
64109
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
64110
|
-
}).toString().trim();
|
|
64111
|
-
} catch (err) {
|
|
64112
|
-
throw new Error(`cannot resolve a repo root from ${cwd}: ${err?.message || err}`);
|
|
64113
|
-
}
|
|
64114
|
-
if (!top) throw new Error(`cannot resolve a repo root from ${cwd}: git named no toplevel`);
|
|
64115
|
-
return top;
|
|
64116
|
-
}
|
|
64117
|
-
function git(args, opts = {}) {
|
|
64118
|
-
return execFileSync("git", args, {
|
|
64119
|
-
cwd: opts.cwd || REPO_ROOT2,
|
|
64120
|
-
encoding: "utf8",
|
|
64121
|
-
// A full PR diff can be megabytes; the default 1MB cap would truncate mid-hunk and the
|
|
64122
|
-
// delta parser would then read a malformed diff as "unsupported shape".
|
|
64123
|
-
maxBuffer: opts.maxBuffer ?? 256 * 1024 * 1024,
|
|
64124
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
64125
|
-
}).toString();
|
|
64126
|
-
}
|
|
64127
|
-
function diffHash(diff) {
|
|
64128
|
-
return createHash("sha256").update(diff).digest("hex");
|
|
64129
|
-
}
|
|
64130
|
-
|
|
64131
|
-
// ../verify-gate/diff-hash.mjs
|
|
64132
|
-
var VALIDATION_METADATA = /* @__PURE__ */ new Set([
|
|
64133
|
-
".claude/.validation-baseline.json",
|
|
64134
|
-
".claude/.validation-evidence.json"
|
|
64135
|
-
]);
|
|
64136
|
-
function isValidationMetadata(path2) {
|
|
64137
|
-
return VALIDATION_METADATA.has(path2) || /(^|[\\/])\.himi([\\/]|$)/.test(path2);
|
|
64138
|
-
}
|
|
64139
|
-
function tryGit(args, root) {
|
|
64140
|
-
try {
|
|
64141
|
-
return git(args, { cwd: root });
|
|
64142
|
-
} catch {
|
|
64143
|
-
return null;
|
|
64144
|
-
}
|
|
64145
|
-
}
|
|
64146
|
-
function committedRecords(raw) {
|
|
64147
|
-
const fields = raw.split("\0");
|
|
64148
|
-
if (fields.length > 0 && fields[fields.length - 1] === "") fields.pop();
|
|
64149
|
-
if (fields.length % 2 !== 0) return null;
|
|
64150
|
-
const records = [];
|
|
64151
|
-
for (let i = 0; i < fields.length; i += 2) {
|
|
64152
|
-
const metadata2 = fields[i];
|
|
64153
|
-
const path2 = fields[i + 1];
|
|
64154
|
-
if (!metadata2.startsWith(":") || path2 === "") return null;
|
|
64155
|
-
if (isValidationMetadata(path2)) continue;
|
|
64156
|
-
records.push(`${Buffer.byteLength(path2)}:${path2}\0${metadata2}\0`);
|
|
64157
|
-
}
|
|
64158
|
-
return records.sort().join("");
|
|
64159
|
-
}
|
|
64160
|
-
function committedIdentity(root) {
|
|
64161
|
-
const head = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root)?.trim();
|
|
64162
|
-
if (!head) return "unborn\0";
|
|
64163
|
-
const base = tryGit(["merge-base", BASE_REF, "HEAD"], root)?.trim();
|
|
64164
|
-
if (base) {
|
|
64165
|
-
const raw = tryGit(["diff", "--raw", "--no-renames", "-z", "--no-abbrev", base, "HEAD"], root);
|
|
64166
|
-
const records = raw === null ? null : committedRecords(raw);
|
|
64167
|
-
if (records !== null) return `base\0${base}\0${records}`;
|
|
64168
|
-
}
|
|
64169
|
-
const listing = tryGit(["ls-tree", "-rz", "--full-tree", "HEAD"], root);
|
|
64170
|
-
const tree = listing === null ? null : treeRecords(listing);
|
|
64171
|
-
return tree !== null ? `tree-listing\0${tree}` : `commit\0${head}\0`;
|
|
64172
|
-
}
|
|
64173
|
-
function treeRecords(listing) {
|
|
64174
|
-
const records = [];
|
|
64175
|
-
for (const record2 of listing.split("\0")) {
|
|
64176
|
-
if (record2 === "") continue;
|
|
64177
|
-
const tab = record2.indexOf(" ");
|
|
64178
|
-
if (tab <= 0 || tab === record2.length - 1) return null;
|
|
64179
|
-
const path2 = record2.slice(tab + 1);
|
|
64180
|
-
if (isValidationMetadata(path2)) continue;
|
|
64181
|
-
records.push(`${Buffer.byteLength(path2)}:${path2}\0${record2.slice(0, tab)}\0`);
|
|
64182
|
-
}
|
|
64183
|
-
return records.sort().join("");
|
|
64184
|
-
}
|
|
64185
|
-
function treeEntries(output, index = false) {
|
|
64186
|
-
const entries = /* @__PURE__ */ new Map();
|
|
64187
|
-
for (const record2 of output.split("\0").filter(Boolean)) {
|
|
64188
|
-
const tab = record2.indexOf(" ");
|
|
64189
|
-
if (tab === -1) continue;
|
|
64190
|
-
const metadata2 = record2.slice(0, tab).split(" ");
|
|
64191
|
-
if (index && metadata2[2] !== "0") continue;
|
|
64192
|
-
entries.set(record2.slice(tab + 1), {
|
|
64193
|
-
mode: metadata2[0],
|
|
64194
|
-
oid: index ? metadata2[1] : metadata2[2]
|
|
64195
|
-
});
|
|
64196
|
-
}
|
|
64197
|
-
return entries;
|
|
64198
|
-
}
|
|
64199
|
-
function sameEntry(left, right) {
|
|
64200
|
-
if (!left || !right) return left === right;
|
|
64201
|
-
return left.mode === right.mode && left.oid === right.oid;
|
|
64202
|
-
}
|
|
64203
|
-
function stagedIdentity(head, index, worktree) {
|
|
64204
|
-
if (sameEntry(index, head) || sameEntry(index, worktree)) return "";
|
|
64205
|
-
if (!index) return head ? "deleted" : "";
|
|
64206
|
-
return `${index.mode}:${index.oid}`;
|
|
64207
|
-
}
|
|
64208
|
-
function blobOid(contents, algorithm) {
|
|
64209
|
-
const body = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
|
|
64210
|
-
return createHash2(algorithm).update(Buffer.from(`blob ${body.byteLength}\0`)).update(body).digest("hex");
|
|
64211
|
-
}
|
|
64212
|
-
function validationDiffHash(cwd = process.cwd()) {
|
|
64213
|
-
const root = resolveRepoRoot(cwd);
|
|
64214
|
-
const born = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root) !== null;
|
|
64215
|
-
const staged = git(["diff", "--cached", "--name-only", "--no-renames", "-z", ...born ? ["HEAD"] : []], {
|
|
64216
|
-
cwd: root
|
|
64217
|
-
}).split("\0").filter(Boolean);
|
|
64218
|
-
const unstaged = git(["diff", "--name-only", "--no-renames", "-z"], { cwd: root }).split("\0").filter(Boolean);
|
|
64219
|
-
const untracked = git(["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
64220
|
-
cwd: root
|
|
64221
|
-
}).split("\0").filter(Boolean);
|
|
64222
|
-
const headEntries = born ? treeEntries(git(["ls-tree", "-rz", "--full-tree", "HEAD"], { cwd: root })) : /* @__PURE__ */ new Map();
|
|
64223
|
-
const indexEntries = treeEntries(git(["ls-files", "--stage", "-z"], { cwd: root }), true);
|
|
64224
|
-
const objectAlgorithm = git(["rev-parse", "--show-object-format"], { cwd: root }).trim();
|
|
64225
|
-
let trustFilesystemMode = true;
|
|
64226
|
-
try {
|
|
64227
|
-
trustFilesystemMode = git(["config", "--bool", "--get", "core.fileMode"], { cwd: root }).trim() !== "false";
|
|
64228
|
-
} catch {
|
|
64229
|
-
}
|
|
64230
|
-
const paths = [.../* @__PURE__ */ new Set([...staged, ...unstaged, ...untracked])].filter((path2) => !isValidationMetadata(path2)).sort();
|
|
64231
|
-
const identity = paths.map((path2) => {
|
|
64232
|
-
const absolute = resolve5(root, path2);
|
|
64233
|
-
const headEntry = headEntries.get(path2);
|
|
64234
|
-
const indexEntry = indexEntries.get(path2);
|
|
64235
|
-
const deletedRecord = () => [`${Buffer.byteLength(path2)}:${path2}`, "deleted", stagedIdentity(headEntry, indexEntry, void 0), ""].join("\0");
|
|
64236
|
-
let stat2;
|
|
64237
|
-
try {
|
|
64238
|
-
stat2 = lstatSync2(absolute);
|
|
64239
|
-
} catch (error) {
|
|
64240
|
-
if (error?.code === "ENOENT") return deletedRecord();
|
|
64241
|
-
throw error;
|
|
64242
|
-
}
|
|
64243
|
-
try {
|
|
64244
|
-
if (stat2.isSymbolicLink()) {
|
|
64245
|
-
const target = readlinkSync(absolute);
|
|
64246
|
-
const worktreeEntry = { mode: "120000", oid: blobOid(target, objectAlgorithm) };
|
|
64247
|
-
const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
|
|
64248
|
-
return [
|
|
64249
|
-
`${Buffer.byteLength(path2)}:${path2}`,
|
|
64250
|
-
"symlink",
|
|
64251
|
-
"120000",
|
|
64252
|
-
diffHash(target),
|
|
64253
|
-
stagedState2,
|
|
64254
|
-
""
|
|
64255
|
-
].join("\0");
|
|
64256
|
-
}
|
|
64257
|
-
if (stat2.isFile()) {
|
|
64258
|
-
const filesystemMode = stat2.mode & 73 ? "100755" : "100644";
|
|
64259
|
-
const worktreeMode = trustFilesystemMode ? filesystemMode : indexEntry?.mode ?? headEntry?.mode ?? "100644";
|
|
64260
|
-
const worktreeOid = git(["hash-object", `--path=${path2}`, "--", path2], { cwd: root }).trim();
|
|
64261
|
-
const worktreeEntry = { mode: worktreeMode, oid: worktreeOid };
|
|
64262
|
-
const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
|
|
64263
|
-
return [
|
|
64264
|
-
`${Buffer.byteLength(path2)}:${path2}`,
|
|
64265
|
-
"file",
|
|
64266
|
-
worktreeMode,
|
|
64267
|
-
diffHash(readFileSync4(absolute)),
|
|
64268
|
-
stagedState2,
|
|
64269
|
-
""
|
|
64270
|
-
].join("\0");
|
|
64271
|
-
}
|
|
64272
|
-
} catch (error) {
|
|
64273
|
-
try {
|
|
64274
|
-
lstatSync2(absolute);
|
|
64275
|
-
} catch (recheck) {
|
|
64276
|
-
if (recheck?.code === "ENOENT") return deletedRecord();
|
|
64277
|
-
}
|
|
64278
|
-
throw error;
|
|
64279
|
-
}
|
|
64280
|
-
const type = stat2.isDirectory() ? "directory" : "other";
|
|
64281
|
-
const stagedState = stagedIdentity(headEntry, indexEntry, void 0);
|
|
64282
|
-
return [
|
|
64283
|
-
`${Buffer.byteLength(path2)}:${path2}`,
|
|
64284
|
-
type,
|
|
64285
|
-
stat2.mode.toString(8),
|
|
64286
|
-
stagedState,
|
|
64287
|
-
""
|
|
64288
|
-
].join("\0");
|
|
64289
|
-
}).join("");
|
|
64290
|
-
const committed = committedIdentity(root);
|
|
64291
|
-
return diffHash(
|
|
64292
|
-
`validation-change-v3\0${Buffer.byteLength(committed)}\0${committed}${identity}`
|
|
64293
|
-
);
|
|
64294
|
-
}
|
|
64295
|
-
function seed(repo, commands) {
|
|
64296
|
-
for (const args of commands) git(args, { cwd: repo });
|
|
64297
|
-
}
|
|
64298
|
-
function write(repo, rel, body) {
|
|
64299
|
-
const absolute = resolve5(repo, rel);
|
|
64300
|
-
mkdirSync4(dirname5(absolute), { recursive: true });
|
|
64301
|
-
writeFileSync4(absolute, body);
|
|
64302
|
-
}
|
|
64303
|
-
function selfTestRepo({ withBase }) {
|
|
64304
|
-
const repo = mkdtempSync(join5(tmpdir(), "diff-hash-self-test-"));
|
|
64305
|
-
seed(repo, [
|
|
64306
|
-
["init", "-q", repo],
|
|
64307
|
-
["-C", repo, "config", "user.email", "self-test@test.invalid"],
|
|
64308
|
-
["-C", repo, "config", "user.name", "diff-hash-self-test"]
|
|
64309
|
-
]);
|
|
64310
|
-
write(repo, "src/base.txt", "base\n");
|
|
64311
|
-
seed(repo, [["-C", repo, "add", "-A"], ["-C", repo, "commit", "-qm", "base"]]);
|
|
64312
|
-
const base = git(["-C", repo, "rev-parse", "HEAD"], { cwd: repo }).trim();
|
|
64313
|
-
if (withBase) seed(repo, [["-C", repo, "update-ref", "refs/remotes/origin/master", base]]);
|
|
64314
|
-
return { repo, base };
|
|
64315
|
-
}
|
|
64316
|
-
function commitChange(repo, base, branch, rel, body) {
|
|
64317
|
-
seed(repo, [["-C", repo, "checkout", "-q", base], ["-C", repo, "checkout", "-q", "-b", branch]]);
|
|
64318
|
-
write(repo, rel, body);
|
|
64319
|
-
seed(repo, [["-C", repo, "add", rel], ["-C", repo, "commit", "-qm", `commit ${rel}`]]);
|
|
64320
|
-
return validationDiffHash(repo);
|
|
64321
|
-
}
|
|
64322
|
-
var SELF_TEST_CASES = [
|
|
64323
|
-
{
|
|
64324
|
-
name: "committed diffs are distinguished (the #1831 regression)",
|
|
64325
|
-
reason: "two unrelated COMMITTED diffs share one identity \u2014 a record for either would satisfy the gate for the other",
|
|
64326
|
-
run: () => {
|
|
64327
|
-
const { repo, base } = selfTestRepo({ withBase: true });
|
|
64328
|
-
try {
|
|
64329
|
-
return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
|
|
64330
|
-
} finally {
|
|
64331
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64332
|
-
}
|
|
64333
|
-
}
|
|
64334
|
-
},
|
|
64335
|
-
{
|
|
64336
|
-
name: "committed diffs are distinguished with NO base ref (fallback ladder)",
|
|
64337
|
-
reason: "without origin/master the committed identity collapses to a constant",
|
|
64338
|
-
run: () => {
|
|
64339
|
-
const { repo, base } = selfTestRepo({ withBase: false });
|
|
64340
|
-
try {
|
|
64341
|
-
return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
|
|
64342
|
-
} finally {
|
|
64343
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64344
|
-
}
|
|
64345
|
-
}
|
|
64346
|
-
},
|
|
64347
|
-
{
|
|
64348
|
-
name: "a clean tree is not the digest of an empty path list",
|
|
64349
|
-
reason: 'a clean tree still hashes to sha256("validation-worktree-v2\\0"), the constant measured in #1831',
|
|
64350
|
-
run: () => {
|
|
64351
|
-
const { repo } = selfTestRepo({ withBase: true });
|
|
64352
|
-
try {
|
|
64353
|
-
return validationDiffHash(repo) !== createHash2("sha256").update("validation-worktree-v2\0").digest("hex");
|
|
64354
|
-
} finally {
|
|
64355
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64356
|
-
}
|
|
64357
|
-
}
|
|
64358
|
-
},
|
|
64359
|
-
{
|
|
64360
|
-
name: "an uncommitted edit still changes the identity",
|
|
64361
|
-
reason: "the working-tree half of the identity stopped responding to edits",
|
|
64362
|
-
run: () => {
|
|
64363
|
-
const { repo } = selfTestRepo({ withBase: true });
|
|
64364
|
-
try {
|
|
64365
|
-
const before = validationDiffHash(repo);
|
|
64366
|
-
write(repo, "src/base.txt", "edited in the working tree\n");
|
|
64367
|
-
return validationDiffHash(repo) !== before;
|
|
64368
|
-
} finally {
|
|
64369
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64370
|
-
}
|
|
64371
|
-
}
|
|
64372
|
-
},
|
|
64373
|
-
{
|
|
64374
|
-
// The "can pass" half. Without it every case above is satisfied by a hash that simply
|
|
64375
|
-
// changes every call, which would identify nothing and invalidate every record.
|
|
64376
|
-
name: "one unchanged state hashes to one value",
|
|
64377
|
-
reason: "the identity is not stable for an unchanged state \u2014 every record would read stale",
|
|
64378
|
-
run: () => {
|
|
64379
|
-
const { repo } = selfTestRepo({ withBase: true });
|
|
64380
|
-
try {
|
|
64381
|
-
write(repo, "src/base.txt", "edited in the working tree\n");
|
|
64382
|
-
return validationDiffHash(repo) === validationDiffHash(repo);
|
|
64383
|
-
} finally {
|
|
64384
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64385
|
-
}
|
|
64386
|
-
}
|
|
64387
|
-
}
|
|
64388
|
-
];
|
|
64389
|
-
function runSelfTest() {
|
|
64390
|
-
let failed2 = 0;
|
|
64391
|
-
for (const testCase of SELF_TEST_CASES) {
|
|
64392
|
-
let ok = false;
|
|
64393
|
-
let thrown = null;
|
|
64394
|
-
try {
|
|
64395
|
-
ok = testCase.run() === true;
|
|
64396
|
-
} catch (error) {
|
|
64397
|
-
thrown = error?.message || String(error);
|
|
64398
|
-
}
|
|
64399
|
-
if (ok) {
|
|
64400
|
-
process.stdout.write(` \u2713 ${testCase.name}
|
|
64401
|
-
`);
|
|
64402
|
-
continue;
|
|
64403
|
-
}
|
|
64404
|
-
failed2++;
|
|
64405
|
-
process.stderr.write(` \u2717 ${testCase.name}
|
|
64406
|
-
${thrown ?? testCase.reason}
|
|
64407
|
-
`);
|
|
64408
|
-
}
|
|
64409
|
-
return failed2;
|
|
64410
|
-
}
|
|
64411
|
-
if (process.argv[1] && basename(process.argv[1]) === "diff-hash.mjs" && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
64412
|
-
if (process.argv.slice(2).includes("--self-test")) {
|
|
64413
|
-
process.stdout.write("\u25B8 diff-hash --self-test: proving the identity can change, and can hold\n");
|
|
64414
|
-
const failed2 = runSelfTest();
|
|
64415
|
-
if (failed2) {
|
|
64416
|
-
process.stderr.write(
|
|
64417
|
-
`\u2717 diff-hash self-test FAILED (${failed2} case(s)) \u2014 the evidence record's diff identity cannot be trusted
|
|
64418
|
-
`
|
|
64419
|
-
);
|
|
64420
|
-
process.exit(1);
|
|
64421
|
-
}
|
|
64422
|
-
process.stdout.write(`\u2713 diff-hash self-test passed (${SELF_TEST_CASES.length} cases)
|
|
64423
|
-
`);
|
|
64424
|
-
} else {
|
|
64425
|
-
process.stdout.write(`${validationDiffHash()}
|
|
64426
|
-
`);
|
|
64427
|
-
}
|
|
64428
|
-
}
|
|
64429
|
-
|
|
64430
|
-
// src/attestation.ts
|
|
64431
|
-
init_source_deny();
|
|
64432
|
-
init_widget_identity();
|
|
64433
|
-
var DEFAULT_MAX_RUNS = 20;
|
|
64434
|
-
var ATTESTATION_VERSION = 2;
|
|
64435
|
-
function attestationVersionOf(value) {
|
|
64436
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
64437
|
-
const version2 = value.attestation;
|
|
64438
|
-
return typeof version2 === "number" && Number.isInteger(version2) && version2 > 0 ? version2 : null;
|
|
64439
|
-
}
|
|
64440
|
-
function classifyRetainedRun(dir, name) {
|
|
64441
|
-
let value;
|
|
64442
|
-
try {
|
|
64443
|
-
value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
|
|
64444
|
-
} catch {
|
|
64445
|
-
return { kind: "unreadable" };
|
|
64446
|
-
}
|
|
64447
|
-
if (isAttestationV2(value)) return { kind: "current", run: value };
|
|
64448
|
-
const version2 = attestationVersionOf(value);
|
|
64449
|
-
if (version2 === null || version2 === ATTESTATION_VERSION) return { kind: "malformed" };
|
|
64450
|
-
return { kind: version2 < ATTESTATION_VERSION ? "legacy" : "future" };
|
|
64451
|
-
}
|
|
64452
|
-
function screenshotArtifactPath(contentDir2, hash) {
|
|
64453
|
-
return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
|
|
64454
|
-
}
|
|
64455
|
-
var GIT_HASH_MAX_BUFFER = 128 * 1024 * 1024;
|
|
64456
|
-
var sha2562 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
|
|
64457
|
-
function canonical(value) {
|
|
64458
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
64459
|
-
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
64460
|
-
const record2 = value;
|
|
64461
|
-
return `{${Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${canonical(record2[key2])}`).join(",")}}`;
|
|
64462
|
-
}
|
|
64463
|
-
function releasePayloadHash(payload) {
|
|
64464
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha2562(canonical(payload));
|
|
64465
|
-
const record2 = payload;
|
|
64466
|
-
const manifest = record2.manifest;
|
|
64467
|
-
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha2562(canonical(payload));
|
|
64468
|
-
const manifestRecord = manifest;
|
|
64469
|
-
const widgets = manifestRecord.widgets;
|
|
64470
|
-
if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha2562(canonical(payload));
|
|
64471
|
-
return sha2562(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
|
|
64472
|
-
}
|
|
64473
|
-
function withoutSelfHash(attestation) {
|
|
64474
|
-
const { selfHash: _selfHash, ...body } = attestation;
|
|
64475
|
-
return body;
|
|
64476
|
-
}
|
|
64477
|
-
function selfHashFor(attestation) {
|
|
64478
|
-
return sha2562(canonical(withoutSelfHash(attestation)));
|
|
64479
|
-
}
|
|
64480
|
-
function repoRoot(contentDir2) {
|
|
64481
|
-
try {
|
|
64482
|
-
const root = execFileSync2("git", ["-C", contentDir2, "rev-parse", "--show-toplevel"], {
|
|
64483
|
-
encoding: "utf8",
|
|
64484
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
64485
|
-
}).trim();
|
|
64486
|
-
if (!root) return null;
|
|
64487
|
-
execFileSync2("git", ["-C", root, "rev-parse", "--verify", "--quiet", "HEAD^{commit}"], { stdio: "ignore" });
|
|
64488
|
-
return root;
|
|
64489
|
-
} catch {
|
|
64490
|
-
return null;
|
|
64491
|
-
}
|
|
64492
|
-
}
|
|
64493
|
-
function storedContentDir(contentDir2) {
|
|
64494
|
-
const absolute = resolve6(contentDir2);
|
|
64495
|
-
const root = repoRoot(absolute);
|
|
64496
|
-
return root ? relative(root, absolute) || "." : ".";
|
|
64497
|
-
}
|
|
64498
|
-
function currentGitDiffHash(contentDir2) {
|
|
64499
|
-
const root = repoRoot(resolve6(contentDir2));
|
|
64500
|
-
if (!root) return "unavailable";
|
|
64501
|
-
return validationDiffHash(root);
|
|
64502
|
-
}
|
|
64503
|
-
function currentGitUntrackedHash(contentDir2) {
|
|
64504
|
-
const unresolved = resolve6(contentDir2);
|
|
64505
|
-
const absolute = realpathSync.native(unresolved);
|
|
64506
|
-
const root = repoRoot(absolute);
|
|
64507
|
-
if (!root) return sourceTreeHash(absolute);
|
|
64508
|
-
const scope = relative(root, absolute) || ".";
|
|
64509
|
-
const listed = execFileSync2("git", ["-C", root, "ls-files", "--others", "--exclude-standard", "-z", "--", scope], {
|
|
64510
|
-
encoding: "utf8",
|
|
64511
|
-
maxBuffer: GIT_HASH_MAX_BUFFER
|
|
64512
|
-
});
|
|
64513
|
-
const hash = createHash3("sha256");
|
|
64514
|
-
for (const path2 of listed.split("\0").filter(Boolean).sort()) {
|
|
64515
|
-
const local = relative(absolute, join6(root, path2));
|
|
64516
|
-
const segs = local.split(/[\\/]/);
|
|
64517
|
-
if (NOT_ATTESTED_DIRS.has(segs[0])) continue;
|
|
64518
|
-
if (segs.length === 1 && AGENT_TOOLING_ROOT_FILES.includes(segs[0])) continue;
|
|
64519
|
-
const full = join6(root, path2);
|
|
64520
|
-
const stats = lstatSync3(full, { throwIfNoEntry: false });
|
|
64521
|
-
if (!stats) continue;
|
|
64522
|
-
const kind = stats.isSymbolicLink() ? "link" : stats.isDirectory() ? "dir" : stats.isFile() ? "file" : null;
|
|
64523
|
-
if (!kind) continue;
|
|
64524
|
-
const payload = kind === "link" ? Buffer.from(readlinkSync2(full)) : kind === "file" ? readFileSync5(full) : Buffer.alloc(0);
|
|
64525
|
-
const pathBytes = Buffer.from(path2);
|
|
64526
|
-
hash.update(`${kind} ${pathBytes.length} ${payload.length}
|
|
64527
|
-
`).update(pathBytes).update(payload);
|
|
64528
|
-
}
|
|
64529
|
-
return hash.digest("hex");
|
|
64530
|
-
}
|
|
64531
|
-
var NOT_ATTESTED_DIRS = /* @__PURE__ */ new Set([".himi", ...AGENT_TOOLING_DIRS]);
|
|
64532
|
-
function sourceTreeHash(root) {
|
|
64533
|
-
const hash = createHash3("sha256");
|
|
64534
|
-
const visit = (dir) => {
|
|
64535
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
64536
|
-
if (entry.isDirectory() && (entry.name === ".git" || entry.name === "node_modules" || NOT_ATTESTED_DIRS.has(entry.name))) continue;
|
|
64537
|
-
const path2 = join6(dir, entry.name);
|
|
64538
|
-
const local = relative(root, path2);
|
|
64539
|
-
if (dir === root && entry.isFile() && AGENT_TOOLING_ROOT_FILES.includes(entry.name)) continue;
|
|
64540
|
-
if (entry.isDirectory()) visit(path2);
|
|
64541
|
-
else if (entry.isFile()) hash.update(local).update("\0").update(readFileSync5(path2)).update("\0");
|
|
64542
|
-
else if (entry.isSymbolicLink()) hash.update(local).update("\0link\0").update(readlinkSync2(path2)).update("\0");
|
|
64543
|
-
}
|
|
64544
|
-
};
|
|
64545
|
-
visit(root);
|
|
64546
|
-
return hash.digest("hex");
|
|
64547
|
-
}
|
|
64548
|
-
var stringRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
|
|
64549
|
-
function isAttestationV2(value) {
|
|
64550
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
64551
|
-
const a = value;
|
|
64552
|
-
const coverage = a.coverage;
|
|
64553
|
-
const report2 = a.report;
|
|
64554
|
-
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");
|
|
64555
|
-
}
|
|
64556
|
-
function readLatestAttestation(contentDir2) {
|
|
64557
|
-
try {
|
|
64558
|
-
const parsed = JSON.parse(readFileSync5(join6(resolve6(contentDir2), ".himi", "attestations", "latest.json"), "utf8"));
|
|
64559
|
-
return isAttestationV2(parsed) ? parsed : null;
|
|
64560
|
-
} catch {
|
|
64561
|
-
return null;
|
|
64562
|
-
}
|
|
64563
|
-
}
|
|
64564
|
-
function readRetainedAttestations(contentDir2) {
|
|
64565
|
-
const dir = join6(resolve6(contentDir2), ".himi", "attestations");
|
|
64566
|
-
if (!existsSync3(dir)) return { runs: [], errors: [] };
|
|
64567
|
-
try {
|
|
64568
|
-
const names = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
|
|
64569
|
-
const parsedRuns = [];
|
|
64570
|
-
const errors = [];
|
|
64571
|
-
for (const name of names) {
|
|
64572
|
-
const classified = classifyRetainedRun(dir, name);
|
|
64573
|
-
if (classified.kind === "current") parsedRuns.push(classified.run);
|
|
64574
|
-
else if (classified.kind === "legacy") {
|
|
64575
|
-
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`);
|
|
64576
|
-
} else if (classified.kind === "future") {
|
|
64577
|
-
errors.push(`attestation history contains retained run ${name} from a newer attestation version \u2014 upgrade himi rather than deleting it`);
|
|
64578
|
-
} else if (classified.kind === "unreadable") {
|
|
64579
|
-
errors.push(`attestation history contains unreadable retained run ${name}`);
|
|
64580
|
-
} else errors.push(`attestation history contains malformed retained run ${name}`);
|
|
64581
|
-
}
|
|
64582
|
-
const latest = readLatestAttestation(contentDir2);
|
|
64583
|
-
if (existsSync3(join6(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
|
|
64584
|
-
else if (names.length > 0 && !latest) errors.push("attestation history has no latest.json");
|
|
64585
|
-
const byHash = new Map(parsedRuns.map((run2) => [run2.selfHash, run2]));
|
|
64586
|
-
if (byHash.size !== parsedRuns.length) errors.push("attestation history contains duplicate self hashes");
|
|
64587
|
-
for (const run2 of parsedRuns) {
|
|
64588
|
-
if (selfHashFor(run2) !== run2.selfHash) errors.push(`selfHash does not match retained run ${run2.runId}`);
|
|
64589
|
-
for (const expected of Object.values(run2.screenshots)) {
|
|
64590
|
-
const path2 = screenshotArtifactPath(contentDir2, expected);
|
|
64591
|
-
if (!existsSync3(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename2(path2)}`);
|
|
64592
|
-
else if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
|
|
64593
|
-
}
|
|
64594
|
-
}
|
|
64595
|
-
const runs = [];
|
|
64596
|
-
const seen = /* @__PURE__ */ new Set();
|
|
64597
|
-
let cursor = latest ?? void 0;
|
|
64598
|
-
while (cursor && !seen.has(cursor.selfHash)) {
|
|
64599
|
-
runs.push(cursor);
|
|
64600
|
-
seen.add(cursor.selfHash);
|
|
64601
|
-
cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
|
|
64602
|
-
}
|
|
64603
|
-
if (latest && !byHash.has(latest.selfHash)) errors.push("latest.json has no matching retained run");
|
|
64604
|
-
if (seen.size !== parsedRuns.length) errors.push("attestation hash chain does not reach every retained run");
|
|
64605
|
-
runs.push(...parsedRuns.filter((run2) => !seen.has(run2.selfHash)).sort((a, b) => a.runId < b.runId ? 1 : -1));
|
|
64606
|
-
return {
|
|
64607
|
-
runs,
|
|
64608
|
-
errors
|
|
64609
|
-
};
|
|
64610
|
-
} catch {
|
|
64611
|
-
return { runs: [], errors: ["attestation history could not be read"] };
|
|
64612
|
-
}
|
|
64613
|
-
}
|
|
64614
|
-
function selectAttestationForPush(runs) {
|
|
64615
|
-
const newestBehavioral = runs.find((run2) => run2.tool === "himi-test");
|
|
64616
|
-
if (!newestBehavioral) return runs[0];
|
|
64617
|
-
const behavioralIndex = runs.indexOf(newestBehavioral);
|
|
64618
|
-
const newerFailure = runs.slice(0, behavioralIndex).find((run2) => run2.report.ok === false);
|
|
64619
|
-
return newerFailure ?? newestBehavioral;
|
|
64620
|
-
}
|
|
64621
|
-
function pushEvidence(contentDir2, releaseId, verify) {
|
|
64622
|
-
const retained = readRetainedAttestations(contentDir2);
|
|
64623
|
-
const forRelease = retained.runs.filter((candidate) => candidate.releaseId === releaseId);
|
|
64624
|
-
const verified = forRelease.filter(verify);
|
|
64625
|
-
const selected = selectAttestationForPush(verified);
|
|
64626
|
-
return {
|
|
64627
|
-
...selected ? { selected } : {},
|
|
64628
|
-
historyErrors: retained.errors,
|
|
64629
|
-
staleRuns: verified.length === 0 ? forRelease.length : 0
|
|
64630
|
-
};
|
|
64631
|
-
}
|
|
64632
|
-
function summaryOf(attestation) {
|
|
64633
|
-
return {
|
|
64634
|
-
runId: attestation.runId,
|
|
64635
|
-
selfHash: attestation.selfHash,
|
|
64636
|
-
tool: attestation.tool,
|
|
64637
|
-
screenIds: Object.keys(attestation.descriptorHashes).sort(),
|
|
64638
|
-
coverage: attestation.coverage,
|
|
64639
|
-
report: { ok: attestation.report.ok, counts: attestation.report.counts },
|
|
64640
|
-
gitDiffHash: attestation.gitDiffHash,
|
|
64641
|
-
releaseHash: attestation.releaseHash
|
|
64642
|
-
};
|
|
64643
|
-
}
|
|
64644
|
-
function writeAttestation(input, opts = {}) {
|
|
64645
|
-
const contentDir2 = resolve6(input.contentDir);
|
|
64646
|
-
const dir = join6(contentDir2, ".himi", "attestations");
|
|
64647
|
-
mkdirSync5(dir, { recursive: true });
|
|
64648
|
-
const previous = readLatestAttestation(contentDir2);
|
|
64649
|
-
const runId = `${(opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()}-${(opts.randomSuffix ?? (() => randomBytes(3).toString("hex")))()}`;
|
|
64650
|
-
const descriptorHashes = Object.fromEntries(
|
|
64651
|
-
Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha2562(bytes2)])
|
|
64652
|
-
);
|
|
64653
|
-
const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a], [b]) => a.localeCompare(b)).filter(([, path2]) => existsSync3(path2)).map(([screen, path2]) => {
|
|
64654
|
-
const bytes2 = readFileSync5(path2);
|
|
64655
|
-
return { screen, bytes: bytes2, hash: sha2562(bytes2) };
|
|
64656
|
-
});
|
|
64657
|
-
const screenshots = Object.fromEntries(screenshotEntries.map(({ screen, hash }) => [screen, hash]));
|
|
64658
|
-
for (const { bytes: bytes2, hash } of screenshotEntries) {
|
|
64659
|
-
const artifact = screenshotArtifactPath(contentDir2, hash);
|
|
64660
|
-
mkdirSync5(join6(dir, "artifacts"), { recursive: true });
|
|
64661
|
-
if (!existsSync3(artifact)) writeFileSync5(artifact, bytes2);
|
|
64662
|
-
}
|
|
64663
|
-
const unsigned = {
|
|
64664
|
-
attestation: 2,
|
|
64665
|
-
runId,
|
|
64666
|
-
tool: input.tool,
|
|
64667
|
-
app: input.app,
|
|
64668
|
-
contentDir: storedContentDir(contentDir2),
|
|
64669
|
-
gitDiffHash: input.gitDiffHash,
|
|
64670
|
-
releaseId: input.releaseId,
|
|
64671
|
-
releaseHash: releasePayloadHash(input.releasePayload),
|
|
64672
|
-
gitUntrackedHash: currentGitUntrackedHash(contentDir2),
|
|
64673
|
-
descriptorHashes,
|
|
64674
|
-
screenshots,
|
|
64675
|
-
coverage: input.coverage,
|
|
64676
|
-
report: input.report,
|
|
64677
|
-
prev: previous?.selfHash ?? null
|
|
64678
|
-
};
|
|
64679
|
-
const attestation = { ...unsigned, selfHash: "" };
|
|
64680
|
-
attestation.selfHash = selfHashFor(attestation);
|
|
64681
|
-
const json5 = JSON.stringify(attestation, null, 2) + "\n";
|
|
64682
|
-
const runFile = `${runId.replace(/:/g, "-")}.json`;
|
|
64683
|
-
writeFileSync5(join6(dir, runFile), json5);
|
|
64684
|
-
writeFileSync5(join6(dir, "latest.json"), json5);
|
|
64685
|
-
const keep = Math.max(1, opts.maxRuns ?? DEFAULT_MAX_RUNS);
|
|
64686
|
-
const allPriorRuns = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").filter((name) => name !== runFile).sort().reverse();
|
|
64687
|
-
const priorRuns = allPriorRuns.filter((name) => {
|
|
64688
|
-
const kind = classifyRetainedRun(dir, name).kind;
|
|
64689
|
-
if (kind === "legacy") {
|
|
64690
|
-
unlinkSync(join6(dir, name));
|
|
64691
|
-
return false;
|
|
64692
|
-
}
|
|
64693
|
-
return kind !== "future";
|
|
64694
|
-
});
|
|
64695
|
-
for (const name of priorRuns.slice(Math.max(0, keep - 1))) unlinkSync(join6(dir, name));
|
|
64696
|
-
const retainedFiles = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
|
|
64697
|
-
const retainedRuns = retainedFiles.map((name) => {
|
|
64698
|
-
const classified = classifyRetainedRun(dir, name);
|
|
64699
|
-
return classified.kind === "current" ? classified.run : null;
|
|
64700
|
-
});
|
|
64701
|
-
if (retainedRuns.every((run2) => run2 !== null)) {
|
|
64702
|
-
const retainedScreenshotHashes = new Set(
|
|
64703
|
-
retainedRuns.flatMap((run2) => Object.values(run2.screenshots))
|
|
64704
|
-
);
|
|
64705
|
-
const artifactDir = join6(dir, "artifacts");
|
|
64706
|
-
if (existsSync3(artifactDir)) {
|
|
64707
|
-
for (const name of readdirSync3(artifactDir).filter((candidate) => candidate.endsWith(".png"))) {
|
|
64708
|
-
if (!retainedScreenshotHashes.has(name.slice(0, -4))) unlinkSync(join6(artifactDir, name));
|
|
64709
|
-
}
|
|
64710
|
-
}
|
|
64711
|
-
}
|
|
64712
|
-
return attestation;
|
|
64713
|
-
}
|
|
64714
|
-
function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, releasePayload, opts = {}) {
|
|
64715
|
-
const errors = [];
|
|
64716
|
-
if (selfHashFor(attestation) !== attestation.selfHash) errors.push("selfHash does not match the attestation bytes");
|
|
64717
|
-
try {
|
|
64718
|
-
const dir = join6(resolve6(contentDir2), ".himi", "attestations");
|
|
64719
|
-
const latest = readLatestAttestation(contentDir2);
|
|
64720
|
-
if (!latest || opts.requireLatest !== false && latest.selfHash !== attestation.selfHash) {
|
|
64721
|
-
errors.push("attestation does not match latest.json");
|
|
64722
|
-
} else {
|
|
64723
|
-
const classified = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").map((name) => classifyRetainedRun(dir, name)).filter((entry) => entry.kind !== "future");
|
|
64724
|
-
const retained = classified.map((entry) => entry.kind === "current" ? entry.run : null);
|
|
64725
|
-
if (retained.some((run2) => run2 === null)) {
|
|
64726
|
-
errors.push("attestation history contains a malformed retained run");
|
|
64727
|
-
} else {
|
|
64728
|
-
const runs = retained;
|
|
64729
|
-
const byHash = new Map(runs.map((run2) => [run2.selfHash, run2]));
|
|
64730
|
-
if (!byHash.has(attestation.selfHash)) errors.push("attestation has no matching retained run");
|
|
64731
|
-
if (!byHash.has(latest.selfHash)) {
|
|
64732
|
-
errors.push("latest.json has no matching retained run");
|
|
64733
|
-
} else {
|
|
64734
|
-
const seen = /* @__PURE__ */ new Set();
|
|
64735
|
-
let cursor = latest;
|
|
64736
|
-
while (cursor && !seen.has(cursor.selfHash)) {
|
|
64737
|
-
seen.add(cursor.selfHash);
|
|
64738
|
-
if (selfHashFor(cursor) !== cursor.selfHash) {
|
|
64739
|
-
errors.push(`selfHash does not match retained run ${cursor.runId}`);
|
|
64740
|
-
break;
|
|
64741
|
-
}
|
|
64742
|
-
cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
|
|
64743
|
-
}
|
|
64744
|
-
if (seen.size !== runs.length) errors.push("attestation hash chain does not reach every retained run");
|
|
64745
|
-
}
|
|
64746
|
-
}
|
|
64747
|
-
}
|
|
64748
|
-
} catch {
|
|
64749
|
-
errors.push("attestation history could not be verified");
|
|
64750
|
-
}
|
|
64751
|
-
if (attestation.app !== expectedApp) errors.push(`attestation app does not match ${expectedApp}`);
|
|
64752
|
-
if (attestation.contentDir !== storedContentDir(contentDir2)) errors.push("attestation contentDir does not match the requested content directory");
|
|
64753
|
-
if (releasePayloadHash(releasePayload) !== attestation.releaseHash) {
|
|
64754
|
-
errors.push("releaseHash does not match the current release payload");
|
|
64755
|
-
}
|
|
64756
|
-
try {
|
|
64757
|
-
const liveDiffHash = currentGitDiffHash(contentDir2);
|
|
64758
|
-
if (liveDiffHash !== "unavailable" && liveDiffHash !== attestation.gitDiffHash) {
|
|
64759
|
-
errors.push("gitDiffHash does not match the live git diff");
|
|
64760
|
-
}
|
|
64761
|
-
} catch {
|
|
64762
|
-
errors.push("gitDiffHash could not be recomputed from the live git diff");
|
|
64763
|
-
}
|
|
64764
|
-
try {
|
|
64765
|
-
const liveUntrackedHash = currentGitUntrackedHash(contentDir2);
|
|
64766
|
-
if (liveUntrackedHash !== "unavailable" && liveUntrackedHash !== attestation.gitUntrackedHash) {
|
|
64767
|
-
errors.push("gitUntrackedHash does not match the live untracked source bytes");
|
|
64768
|
-
}
|
|
64769
|
-
} catch {
|
|
64770
|
-
errors.push("gitUntrackedHash could not be recomputed from the live untracked source bytes");
|
|
64771
|
-
}
|
|
64772
|
-
for (const [screen, expected] of Object.entries(attestation.descriptorHashes)) {
|
|
64773
|
-
const bytes2 = descriptors[screen];
|
|
64774
|
-
if (bytes2 === void 0) {
|
|
64775
|
-
errors.push(`missing baked descriptor for ${screen}`);
|
|
64776
|
-
continue;
|
|
64777
|
-
}
|
|
64778
|
-
if (sha2562(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
|
|
64779
|
-
}
|
|
64780
|
-
for (const [screen, expected] of Object.entries(attestation.screenshots)) {
|
|
64781
|
-
const path2 = screenshotArtifactPath(contentDir2, expected);
|
|
64782
|
-
if (!existsSync3(path2)) {
|
|
64783
|
-
errors.push(`missing screenshot for ${screen}: ${basename2(path2)}`);
|
|
64784
|
-
continue;
|
|
64785
|
-
}
|
|
64786
|
-
if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
|
|
64787
|
-
}
|
|
64788
|
-
return { ok: errors.length === 0, errors };
|
|
64789
|
-
}
|
|
64876
|
+
init_attestation();
|
|
64790
64877
|
|
|
64791
64878
|
// ../../core/admin-web/src/utils/qr-terminal.ts
|
|
64792
64879
|
init_qr();
|
|
@@ -68005,7 +68092,7 @@ function nativeConfigTokenNotice(configSource, optedIn) {
|
|
|
68005
68092
|
}
|
|
68006
68093
|
async function nativeConfigAdvisories(app, configSource) {
|
|
68007
68094
|
try {
|
|
68008
|
-
const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (
|
|
68095
|
+
const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
|
|
68009
68096
|
const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
|
|
68010
68097
|
const iosYmlPath = iosProjectYml2(app);
|
|
68011
68098
|
const iosYml = existsSync63(iosYmlPath) ? readFileSync75(iosYmlPath, "utf8") : "";
|
|
@@ -68526,7 +68613,7 @@ function uxHandoverHint(uxEvidence, ctx) {
|
|
|
68526
68613
|
if (uxEvidence.state === "failed") {
|
|
68527
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\`.`;
|
|
68528
68615
|
}
|
|
68529
|
-
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
|
|
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)` : ""}`;
|
|
68530
68617
|
const drifting = ctx.nondeterministicWorkers ?? [];
|
|
68531
68618
|
if (drifting.length > 0) {
|
|
68532
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\`.`;
|
|
@@ -68761,7 +68848,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
68761
68848
|
};
|
|
68762
68849
|
const runReleasePatch = async (opts) => {
|
|
68763
68850
|
const { app, plan, draft } = opts;
|
|
68764
|
-
const { regressionDiff: regressionDiff2, patchHonesty: patchHonesty2 } = await Promise.resolve().then(() => (
|
|
68851
|
+
const { regressionDiff: regressionDiff2, patchHonesty: patchHonesty2 } = await Promise.resolve().then(() => (init_release_patch2(), release_patch_exports));
|
|
68765
68852
|
const target = targetName();
|
|
68766
68853
|
const baseFound = await fetchReleaseManifest(client2, app, opts.fromRelease);
|
|
68767
68854
|
if ("error" in baseFound) {
|
|
@@ -69170,12 +69257,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
69170
69257
|
try {
|
|
69171
69258
|
const bytes2 = readFileSync75(iconPath);
|
|
69172
69259
|
payload.icon = bytes2.toString("base64");
|
|
69173
|
-
iconHash =
|
|
69260
|
+
iconHash = createHash28("sha256").update(bytes2).digest("hex");
|
|
69174
69261
|
} catch {
|
|
69175
69262
|
}
|
|
69176
69263
|
}
|
|
69177
69264
|
payload.app = templateAppId;
|
|
69178
|
-
|
|
69265
|
+
const { rebindReleaseId: rebindReleaseId2 } = await Promise.resolve().then(() => (init_build3(), build_exports2));
|
|
69266
|
+
payload.manifest = rebindReleaseId2({ ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} });
|
|
69179
69267
|
const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
|
|
69180
69268
|
if (Object.keys(files).length === 0) {
|
|
69181
69269
|
io.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
|
|
@@ -70801,7 +70889,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70801
70889
|
const contentArg = shellQuote2(contentRel);
|
|
70802
70890
|
if (onlyRaw !== void 0) {
|
|
70803
70891
|
const onlyDraft = flags.draft === true;
|
|
70804
|
-
const { parseOnlySelectors: parseOnlySelectors2, planFromPayload: planFromPayload2, notCarriedDiff: notCarriedDiff2 } = await Promise.resolve().then(() => (
|
|
70892
|
+
const { parseOnlySelectors: parseOnlySelectors2, planFromPayload: planFromPayload2, notCarriedDiff: notCarriedDiff2 } = await Promise.resolve().then(() => (init_release_patch2(), release_patch_exports));
|
|
70805
70893
|
const parsed = parseOnlySelectors2(onlyRaw, {
|
|
70806
70894
|
screens: Object.keys(manifest.screens ?? {}),
|
|
70807
70895
|
bundles: Object.keys(manifest.bundles ?? {})
|
|
@@ -70859,7 +70947,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70859
70947
|
let localSourceHash;
|
|
70860
70948
|
try {
|
|
70861
70949
|
const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
|
|
70862
|
-
localSourceHash =
|
|
70950
|
+
localSourceHash = createHash28("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
|
|
70863
70951
|
} catch {
|
|
70864
70952
|
}
|
|
70865
70953
|
return runReleasePatch({
|
|
@@ -70934,7 +71022,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70934
71022
|
}
|
|
70935
71023
|
const evidence = pushEvidence(dir, manifest.releaseId, (candidate) => verifyAttestation(candidate, dir, payload.screens, declared, payload, { requireLatest: false }).ok);
|
|
70936
71024
|
const drifting = nondeterministicWorkers(outcome.workers);
|
|
70937
|
-
const staleEvidence = evidence.staleRuns > 0 ? `${evidence.staleRuns} retained attestation(s)
|
|
71025
|
+
const staleEvidence = evidence.staleRuns > 0 ? `${evidence.staleRuns} retained attestation(s) were recorded at this commit, but none describes the content being published` : null;
|
|
70938
71026
|
const attestationErrors = [...evidence.historyErrors, ...staleEvidence ? [staleEvidence] : []];
|
|
70939
71027
|
if (attestationErrors.length > 0 && !force) {
|
|
70940
71028
|
const remedy = `\`himi test --content ${contentArg}\` (or \`himi render <screenId> --content ${contentArg}\`)`;
|
|
@@ -70943,7 +71031,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70943
71031
|
ok: false,
|
|
70944
71032
|
pushed: false,
|
|
70945
71033
|
errors: attestationErrors,
|
|
70946
|
-
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
|
|
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\`.`
|
|
70947
71035
|
});
|
|
70948
71036
|
return 1;
|
|
70949
71037
|
}
|
|
@@ -72408,7 +72496,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
72408
72496
|
});
|
|
72409
72497
|
}
|
|
72410
72498
|
const bytes2 = readFileSync75(file);
|
|
72411
|
-
const sha =
|
|
72499
|
+
const sha = createHash28("sha256").update(bytes2).digest("hex");
|
|
72412
72500
|
const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
72413
72501
|
if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
|
|
72414
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 }));
|
|
@@ -72744,7 +72832,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
72744
72832
|
const { preflightApp: preflightApp2 } = await Promise.resolve().then(() => (init_preflight_app(), preflight_app_exports));
|
|
72745
72833
|
const { resolveStoreCredentials: resolveStoreCredentials2 } = await Promise.resolve().then(() => (init_credential_profile(), credential_profile_exports));
|
|
72746
72834
|
const { readIosVersion: readIosVersion2, bumpVersion: bumpVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
72747
|
-
const { iosProjectYml: iosYmlPath, hasRepoCheckout: hasRepoCheckout2 } = await Promise.resolve().then(() => (
|
|
72835
|
+
const { iosProjectYml: iosYmlPath, hasRepoCheckout: hasRepoCheckout2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
|
|
72748
72836
|
const { ascStatus: ascStatus2 } = await Promise.resolve().then(() => (init_asc_status(), asc_status_exports));
|
|
72749
72837
|
const { readFileSync: rf, readdirSync: rd, existsSync: ex, statSync: st } = await import("node:fs");
|
|
72750
72838
|
const { execFile: execFile2 } = await import("node:child_process");
|