@wix/himalaya-cli 0.1056.0 → 0.1058.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 +1325 -1222
- 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";
|
|
@@ -5880,6 +6657,7 @@ function walk(node, registry, path2, out) {
|
|
|
5880
6657
|
var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA;
|
|
5881
6658
|
var init_tokens = __esm({
|
|
5882
6659
|
"../../core/ts/src/tokens.ts"() {
|
|
6660
|
+
"use strict";
|
|
5883
6661
|
SYSTEM_FONT_FAMILY = "$system";
|
|
5884
6662
|
TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
|
|
5885
6663
|
}
|
|
@@ -5906,6 +6684,7 @@ function resolveTranslation(value, catalog, sourceCatalog) {
|
|
|
5906
6684
|
var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
|
|
5907
6685
|
var init_bindings = __esm({
|
|
5908
6686
|
"../../core/ts/src/bindings.ts"() {
|
|
6687
|
+
"use strict";
|
|
5909
6688
|
BINDING_SIGILS = ["service", "flag", "item", "t"];
|
|
5910
6689
|
BINDING_SIGIL_RE = new RegExp(
|
|
5911
6690
|
`^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
|
|
@@ -14627,11 +15406,12 @@ function uxEvidenceForRelease(content, release) {
|
|
|
14627
15406
|
const failedPlatforms = forRelease.filter(([, entry]) => entry.status === "failed").map(([platform]) => platform);
|
|
14628
15407
|
return failedPlatforms.length > 0 ? { ...base, state: "failed", platforms, failedPlatforms, malformed } : { ...base, state: "recorded", platforms, malformed };
|
|
14629
15408
|
}
|
|
14630
|
-
|
|
15409
|
+
const sameCommit = recorded.filter(([, entry]) => sameReleaseCommit(entry.releaseId, releaseId));
|
|
15410
|
+
if (sameCommit.length > 0) {
|
|
14631
15411
|
return {
|
|
14632
15412
|
...base,
|
|
14633
15413
|
state: "outdated",
|
|
14634
|
-
platforms:
|
|
15414
|
+
platforms: sameCommit.map(([platform]) => platform),
|
|
14635
15415
|
malformed
|
|
14636
15416
|
};
|
|
14637
15417
|
}
|
|
@@ -14648,6 +15428,7 @@ var NATIVE_PLATFORMS, NATIVE_AUDIT_STATUSES;
|
|
|
14648
15428
|
var init_ux_evidence = __esm({
|
|
14649
15429
|
"src/ux-evidence.ts"() {
|
|
14650
15430
|
init_mobile_ux_contract();
|
|
15431
|
+
init_attestation();
|
|
14651
15432
|
NATIVE_PLATFORMS = ["ios", "android"];
|
|
14652
15433
|
NATIVE_AUDIT_STATUSES = ["passed", "failed", "unavailable"];
|
|
14653
15434
|
}
|
|
@@ -15945,7 +16726,7 @@ function generationAllowed(app, configTs, opts = {}) {
|
|
|
15945
16726
|
var DEFAULT_ACCENT_TOKEN, DEFAULT_GLYPH_SCALE, GLYPH_SCALE_MIN, GLYPH_SCALE_MAX, LEGACY_PLACEHOLDER_SHA256;
|
|
15946
16727
|
var init_plan2 = __esm({
|
|
15947
16728
|
"../app-icons/plan.ts"() {
|
|
15948
|
-
|
|
16729
|
+
init_paths2();
|
|
15949
16730
|
init_glyphs();
|
|
15950
16731
|
DEFAULT_ACCENT_TOKEN = "accentPrimary";
|
|
15951
16732
|
DEFAULT_GLYPH_SCALE = 0.62;
|
|
@@ -16013,7 +16794,7 @@ var ANDROID_ICON_BACKGROUND_RES, ANDROID_ADAPTIVE_FILES, ANDROID_ICON_BACKGROUND
|
|
|
16013
16794
|
var init_icon2 = __esm({
|
|
16014
16795
|
"../native-config/icon.ts"() {
|
|
16015
16796
|
init_plan2();
|
|
16016
|
-
|
|
16797
|
+
init_paths2();
|
|
16017
16798
|
ANDROID_ICON_BACKGROUND_RES = "ic_launcher_background";
|
|
16018
16799
|
ANDROID_ADAPTIVE_FILES = ["ic_launcher.xml", "ic_launcher_round.xml"];
|
|
16019
16800
|
ANDROID_ICON_BACKGROUND_FILE = "ic_launcher_background.xml";
|
|
@@ -16522,7 +17303,7 @@ var init_gen_native_config = __esm({
|
|
|
16522
17303
|
"../native-config/gen-native-config.ts"() {
|
|
16523
17304
|
init_src2();
|
|
16524
17305
|
init_src2();
|
|
16525
|
-
|
|
17306
|
+
init_paths2();
|
|
16526
17307
|
init_splash();
|
|
16527
17308
|
init_icon2();
|
|
16528
17309
|
init_live_activity();
|
|
@@ -22031,6 +22812,7 @@ function shouldRevealError(args) {
|
|
|
22031
22812
|
var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
|
|
22032
22813
|
var init_validation = __esm({
|
|
22033
22814
|
"../../core/ts/src/validation.ts"() {
|
|
22815
|
+
"use strict";
|
|
22034
22816
|
EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
|
|
22035
22817
|
URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
|
|
22036
22818
|
INTEGER_RE = /^[+-]?[0-9]+$/;
|
|
@@ -25631,6 +26413,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
|
|
|
25631
26413
|
var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
|
|
25632
26414
|
var init_worker_subtree = __esm({
|
|
25633
26415
|
"../../core/ts/src/worker-subtree.ts"() {
|
|
26416
|
+
"use strict";
|
|
25634
26417
|
init_worker_subtree_safe_types_generated();
|
|
25635
26418
|
WORKER_SUBTREE_CEILINGS = Object.freeze({
|
|
25636
26419
|
maxEncodedBytes: 65536,
|
|
@@ -27598,7 +28381,10 @@ function renderInner(node, ctx) {
|
|
|
27598
28381
|
case "Badge": {
|
|
27599
28382
|
const _bt = boundTokenName(props.tokenPath, props.token, ctx);
|
|
27600
28383
|
const c = _bt ? C(_bt, "#116DFF") : sevColor(ctx, props.style ?? "info");
|
|
27601
|
-
|
|
28384
|
+
const liveCount = typeof props.text === "string" && /^\$(?:service|flag)\b/.test(props.text);
|
|
28385
|
+
const badgeLabel = modifiers?.accessibilityLabel;
|
|
28386
|
+
const badgeRole = liveCount ? "status" : typeof badgeLabel === "string" && badgeLabel.trim() !== "" ? "img" : void 0;
|
|
28387
|
+
return /* @__PURE__ */ jsx6("span", { role: badgeRole, style: { display: "inline-flex", alignItems: "center", padding: "3px 9px", borderRadius: 999, fontSize: 12, fontWeight: 700, fontFamily: bodyFont(ctx), background: `color-mix(in srgb, ${c} 16%, ${C("surfacePrimary", "#fff")})`, color: c, ...wrap }, children: ctx.empty ? "" : txt(props.text, ctx) || "Badge" });
|
|
27602
28388
|
}
|
|
27603
28389
|
case "Tag": {
|
|
27604
28390
|
const c = C(boundTokenName(props.tokenPath, props.token, ctx) ?? "accentPrimary", "#116DFF");
|
|
@@ -29517,21 +30303,22 @@ var init_renderNode = __esm({
|
|
|
29517
30303
|
opacity: disabled(at) ? 0.4 : 1,
|
|
29518
30304
|
userSelect: "none"
|
|
29519
30305
|
});
|
|
30306
|
+
const named = { [ROLE_WHEN_NAMED]: "group" };
|
|
29520
30307
|
if (ctx.empty) {
|
|
29521
|
-
return /* @__PURE__ */ jsxs3("div", { style: box, children: [
|
|
30308
|
+
return /* @__PURE__ */ jsxs3("div", { ...named, style: box, children: [
|
|
29522
30309
|
/* @__PURE__ */ jsx6("span", { style: btn("min"), children: "\u2212" }),
|
|
29523
30310
|
/* @__PURE__ */ jsx6("b", { style: { fontSize: 16, minWidth: 24, textAlign: "center", color: C("textPrimary", "#111") }, children: "\u2014" }),
|
|
29524
30311
|
/* @__PURE__ */ jsx6("span", { style: btn("max"), children: "+" })
|
|
29525
30312
|
] });
|
|
29526
30313
|
}
|
|
29527
30314
|
if (!ctx.onAction) {
|
|
29528
|
-
return /* @__PURE__ */ jsxs3("div", { style: box, children: [
|
|
30315
|
+
return /* @__PURE__ */ jsxs3("div", { ...named, style: box, children: [
|
|
29529
30316
|
/* @__PURE__ */ jsx6("span", { style: btn("min"), children: "\u2212" }),
|
|
29530
30317
|
/* @__PURE__ */ jsx6("b", { style: { fontSize: 16, minWidth: 24, textAlign: "center", color: C("textPrimary", "#111") }, children: value }),
|
|
29531
30318
|
/* @__PURE__ */ jsx6("span", { style: btn("max"), children: "+" })
|
|
29532
30319
|
] });
|
|
29533
30320
|
}
|
|
29534
|
-
return /* @__PURE__ */ jsxs3("div", { style: box, children: [
|
|
30321
|
+
return /* @__PURE__ */ jsxs3("div", { ...named, style: box, children: [
|
|
29535
30322
|
/* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Decrease", disabled: disabled("min"), onClick: () => bump(-step), style: { ...btn("min"), font: "inherit" }, children: "\u2212" }),
|
|
29536
30323
|
/* @__PURE__ */ jsx6("b", { style: { fontSize: 16, minWidth: 24, textAlign: "center", color: C("textPrimary", "#111"), fontVariantNumeric: "tabular-nums" }, children: value }),
|
|
29537
30324
|
/* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Increase", disabled: disabled("max"), onClick: () => bump(step), style: { ...btn("max"), font: "inherit" }, children: "+" })
|
|
@@ -29744,10 +30531,11 @@ var init_renderNode = __esm({
|
|
|
29744
30531
|
const imageUrl = isBinding(props.imageUrl) ? boundUrl : props.imageUrl;
|
|
29745
30532
|
const initials = isBinding(props.initials) ? boundInitials : props.initials;
|
|
29746
30533
|
const size = props.size ?? 48;
|
|
30534
|
+
const a11y = { [ROLE_WHEN_NAMED]: "img", [SELF_RENDERED_LEAF]: true };
|
|
29747
30535
|
if (typeof imageUrl === "string" && imageUrl) {
|
|
29748
|
-
return /* @__PURE__ */ jsx6("img", { src: resolveAssetUrl(imageUrl, ctx.assetBaseUrl), alt: "", style: { width: size, height: size, borderRadius: "50%", objectFit: "cover", flex: "none", ...wrap } });
|
|
30536
|
+
return /* @__PURE__ */ jsx6("img", { ...a11y, src: resolveAssetUrl(imageUrl, ctx.assetBaseUrl), alt: "", style: { width: size, height: size, borderRadius: "50%", objectFit: "cover", flex: "none", ...wrap } });
|
|
29749
30537
|
}
|
|
29750
|
-
return /* @__PURE__ */ jsx6("div", { style: { width: size, height: size, borderRadius: "50%", background: C("accentPrimary", "#116DFF"), color: C("textOnAccent", "#fff"), display: "grid", placeItems: "center", fontWeight: 600, fontSize: size * 0.4, fontFamily: bodyFont(ctx), flex: "none", ...wrap }, children: (asText2(initials) || "?").slice(0, 2).toUpperCase() });
|
|
30538
|
+
return /* @__PURE__ */ jsx6("div", { ...a11y, style: { width: size, height: size, borderRadius: "50%", background: C("accentPrimary", "#116DFF"), color: C("textOnAccent", "#fff"), display: "grid", placeItems: "center", fontWeight: 600, fontSize: size * 0.4, fontFamily: bodyFont(ctx), flex: "none", ...wrap }, children: (asText2(initials) || "?").slice(0, 2).toUpperCase() });
|
|
29751
30539
|
});
|
|
29752
30540
|
IconNode = withExtras("IconNode", ({ props, ctx, wrap }) => {
|
|
29753
30541
|
const C = ctx.color;
|
|
@@ -30052,7 +30840,11 @@ var init_renderNode = __esm({
|
|
|
30052
30840
|
const shown2 = avatars.slice(0, maxVisible);
|
|
30053
30841
|
const overflow = props.overflowCount || Math.max(0, avatars.length - maxVisible);
|
|
30054
30842
|
const shift = Math.round(size * (1 - (props.overlapRatio ?? 0.67)));
|
|
30055
|
-
|
|
30843
|
+
const caption = useBoundString(props.caption, ctx).trim();
|
|
30844
|
+
const count = shown2.length + overflow;
|
|
30845
|
+
const countText = count > 0 ? `${count} ${count === 1 ? "person" : "people"}` : "";
|
|
30846
|
+
const groupName = [caption, countText].filter(Boolean).join(", ");
|
|
30847
|
+
return /* @__PURE__ */ jsxs3("div", { role: "group", "aria-label": groupName || void 0, "data-a11y-value": countText || void 0, ...{ [SELF_RENDERED_LEAF]: true }, style: { width: "100%", ...wrap }, children: [
|
|
30056
30848
|
/* @__PURE__ */ jsxs3("div", { style: { display: "flex" }, children: [
|
|
30057
30849
|
shown2.map((a, i) => {
|
|
30058
30850
|
const ring = props.ringGradient ? ctx.gradient(props.ringGradient) : void 0;
|
|
@@ -30881,7 +31673,7 @@ var init_renderNode = __esm({
|
|
|
30881
31673
|
/* @__PURE__ */ jsxs3("div", { style: { position: "relative", height: Math.min(props.expandedHeight ?? 120, 160), borderRadius: ctx.radius("md", 10), overflow: "hidden", background: imageUrl ? void 0 : `linear-gradient(135deg, ${C("accentPrimary", "#116DFF")}, ${C("accentSecondary", "#0E5FE3")})` }, children: [
|
|
30882
31674
|
imageUrl && /* @__PURE__ */ jsx6("img", { src: imageUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "cover" } }),
|
|
30883
31675
|
/* @__PURE__ */ jsxs3("div", { style: { position: "absolute", left: 14, bottom: 12, color: "#fff", textShadow: "0 1px 4px rgba(0,0,0,.5)" }, children: [
|
|
30884
|
-
/* @__PURE__ */ jsx6("div", { style: { fontFamily: ctx.font("title1").family || ctx.idiom && ctx.idiom !== "base" ? fontStack(ctx.font("title1").family, ctx.idiom) : '"Wix Madefor Display", system-ui', fontWeight: 700, fontSize: 20 }, children: txt(props.title, ctx) }),
|
|
31676
|
+
/* @__PURE__ */ jsx6("div", { role: "heading", "aria-level": 2, style: { fontFamily: ctx.font("title1").family || ctx.idiom && ctx.idiom !== "base" ? fontStack(ctx.font("title1").family, ctx.idiom) : '"Wix Madefor Display", system-ui', fontWeight: 700, fontSize: 20 }, children: txt(props.title, ctx) }),
|
|
30885
31677
|
props.subtitle && /* @__PURE__ */ jsx6("div", { style: { fontSize: 13, opacity: 0.9 }, children: txt(props.subtitle, ctx) })
|
|
30886
31678
|
] })
|
|
30887
31679
|
] }),
|
|
@@ -37205,6 +37997,44 @@ var init_src6 = __esm({
|
|
|
37205
37997
|
}
|
|
37206
37998
|
});
|
|
37207
37999
|
|
|
38000
|
+
// ../../core/serve-storage/src/release-patch.ts
|
|
38001
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
38002
|
+
function sha256hex(b) {
|
|
38003
|
+
return createHash17("sha256").update(b).digest("hex");
|
|
38004
|
+
}
|
|
38005
|
+
function stableStringify(value) {
|
|
38006
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
38007
|
+
if (Array.isArray(value)) return "[" + value.map(stableStringify).join(",") + "]";
|
|
38008
|
+
const object = value;
|
|
38009
|
+
return "{" + Object.keys(object).sort().map((key2) => JSON.stringify(key2) + ":" + stableStringify(object[key2])).join(",") + "}";
|
|
38010
|
+
}
|
|
38011
|
+
function manifestContent(m) {
|
|
38012
|
+
const {
|
|
38013
|
+
releaseId: _releaseId,
|
|
38014
|
+
gitSha: _gitSha,
|
|
38015
|
+
shellSha: _shellSha,
|
|
38016
|
+
label: _label,
|
|
38017
|
+
notes: _notes,
|
|
38018
|
+
publishedAt: _publishedAt,
|
|
38019
|
+
publishedBy: _publishedBy,
|
|
38020
|
+
attestation: _attestation,
|
|
38021
|
+
forcedPastErrors: _forcedPastErrors,
|
|
38022
|
+
...content
|
|
38023
|
+
} = m;
|
|
38024
|
+
return content;
|
|
38025
|
+
}
|
|
38026
|
+
function releaseContentId(m) {
|
|
38027
|
+
const content = manifestContent(m);
|
|
38028
|
+
const widgets = content.widgets;
|
|
38029
|
+
if (widgets && typeof widgets === "object" && !Array.isArray(widgets)) content.widgets = stableWidgets(widgets);
|
|
38030
|
+
return sha256hex(stableStringify(content));
|
|
38031
|
+
}
|
|
38032
|
+
var init_release_patch = __esm({
|
|
38033
|
+
"../../core/serve-storage/src/release-patch.ts"() {
|
|
38034
|
+
init_widget_identity();
|
|
38035
|
+
}
|
|
38036
|
+
});
|
|
38037
|
+
|
|
37208
38038
|
// ../../core/dev-server/src/net-scenario.ts
|
|
37209
38039
|
function resolveScenario(rules, req, upstream) {
|
|
37210
38040
|
const ctx = {
|
|
@@ -39918,11 +40748,11 @@ var init_dependencies = __esm({
|
|
|
39918
40748
|
});
|
|
39919
40749
|
|
|
39920
40750
|
// ../../core/functions-build/src/sbom.ts
|
|
39921
|
-
import { createHash as
|
|
40751
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
39922
40752
|
function createFunctionSbom(packages) {
|
|
39923
40753
|
const components2 = [...packages].sort((a, b) => a.name.localeCompare(b.name)).map(({ name, version: version2, integrity: integrity2, license }) => ({ name, version: version2, integrity: integrity2, license }));
|
|
39924
40754
|
const body = JSON.stringify({ format: "himalaya-sbom-v1", components: components2 });
|
|
39925
|
-
return { format: "himalaya-sbom-v1", components: components2, hash:
|
|
40755
|
+
return { format: "himalaya-sbom-v1", components: components2, hash: createHash18("sha256").update(body).digest("hex") };
|
|
39926
40756
|
}
|
|
39927
40757
|
var init_sbom = __esm({
|
|
39928
40758
|
"../../core/functions-build/src/sbom.ts"() {
|
|
@@ -39930,12 +40760,12 @@ var init_sbom = __esm({
|
|
|
39930
40760
|
});
|
|
39931
40761
|
|
|
39932
40762
|
// ../../core/functions-build/src/build.ts
|
|
39933
|
-
import { createHash as
|
|
40763
|
+
import { createHash as createHash19 } from "node:crypto";
|
|
39934
40764
|
function sha2563(value) {
|
|
39935
|
-
return
|
|
40765
|
+
return createHash19("sha256").update(value).digest("hex");
|
|
39936
40766
|
}
|
|
39937
40767
|
function sri(value) {
|
|
39938
|
-
return `sha256-${
|
|
40768
|
+
return `sha256-${createHash19("sha256").update(value).digest("base64")}`;
|
|
39939
40769
|
}
|
|
39940
40770
|
function schemaHash(schema) {
|
|
39941
40771
|
if (!schema) return "";
|
|
@@ -40116,6 +40946,7 @@ __export(build_exports2, {
|
|
|
40116
40946
|
pinBakedServerBaseUrl: () => pinBakedServerBaseUrl,
|
|
40117
40947
|
pinnedReleaseFromEnv: () => pinnedReleaseFromEnv,
|
|
40118
40948
|
publishableFontFaces: () => publishableFontFaces,
|
|
40949
|
+
rebindReleaseId: () => rebindReleaseId,
|
|
40119
40950
|
releaseIdFor: () => releaseIdFor,
|
|
40120
40951
|
servicesFromBundleJs: () => servicesFromBundleJs,
|
|
40121
40952
|
shellSha: () => shellSha,
|
|
@@ -40125,7 +40956,7 @@ __export(build_exports2, {
|
|
|
40125
40956
|
withStoreNavBundle: () => withStoreNavBundle,
|
|
40126
40957
|
withoutDevOnlyAppConfig: () => withoutDevOnlyAppConfig
|
|
40127
40958
|
});
|
|
40128
|
-
import { createHash as
|
|
40959
|
+
import { createHash as createHash20 } from "node:crypto";
|
|
40129
40960
|
import { readFileSync as readFileSync42, readdirSync as readdirSync21, existsSync as existsSync39 } from "node:fs";
|
|
40130
40961
|
import { dirname as dirname23, join as join37, resolve as resolve32 } from "node:path";
|
|
40131
40962
|
import { fileURLToPath as fileURLToPath19 } from "node:url";
|
|
@@ -40195,10 +41026,10 @@ async function bakeCatalogs(app, locale, fetchCatalog, putDoc) {
|
|
|
40195
41026
|
return out;
|
|
40196
41027
|
}
|
|
40197
41028
|
function sha2564(buf) {
|
|
40198
|
-
return
|
|
41029
|
+
return createHash20("sha256").update(buf).digest("hex");
|
|
40199
41030
|
}
|
|
40200
41031
|
function sri2(buf) {
|
|
40201
|
-
return "sha256-" +
|
|
41032
|
+
return "sha256-" + createHash20("sha256").update(buf).digest("base64");
|
|
40202
41033
|
}
|
|
40203
41034
|
async function widgetFailureDetail(res) {
|
|
40204
41035
|
try {
|
|
@@ -40294,14 +41125,14 @@ function pinBakedServerBaseUrl(json5, base) {
|
|
|
40294
41125
|
const boundary = /:\d+$/.test(base) ? "(?!\\d)" : "(?![\\w.-])(?!:\\d)";
|
|
40295
41126
|
return json5.replace(new RegExp(`${escaped}${boundary}`, "g"), BAKED_SERVER_BASE_URL);
|
|
40296
41127
|
}
|
|
40297
|
-
function
|
|
41128
|
+
function stableStringify2(v) {
|
|
40298
41129
|
if (v === null || typeof v !== "object") return JSON.stringify(v);
|
|
40299
|
-
if (Array.isArray(v)) return "[" + v.map(
|
|
41130
|
+
if (Array.isArray(v)) return "[" + v.map(stableStringify2).join(",") + "]";
|
|
40300
41131
|
const o = v;
|
|
40301
|
-
return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" +
|
|
41132
|
+
return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + stableStringify2(o[k])).join(",") + "}";
|
|
40302
41133
|
}
|
|
40303
41134
|
function contentReleaseId(content) {
|
|
40304
|
-
return sha2564(
|
|
41135
|
+
return sha2564(stableStringify2(content));
|
|
40305
41136
|
}
|
|
40306
41137
|
function servicesFromBundleJs(bundleJs, input = {}) {
|
|
40307
41138
|
try {
|
|
@@ -40316,7 +41147,13 @@ function hydrationServices(tier5Dir, bundleName, input = {}) {
|
|
|
40316
41147
|
return js ? servicesFromBundleJs(js, input) : null;
|
|
40317
41148
|
}
|
|
40318
41149
|
function releaseIdFor(sha, content) {
|
|
40319
|
-
|
|
41150
|
+
if (sha === "dev") return contentReleaseId(content);
|
|
41151
|
+
const stored = JSON.parse(JSON.stringify(content));
|
|
41152
|
+
return `${sha}-${releaseContentId(stored).slice(0, 12)}`;
|
|
41153
|
+
}
|
|
41154
|
+
function rebindReleaseId(manifest) {
|
|
41155
|
+
const { releaseId: _releaseId, gitSha: gitSha2, shellSha: _shellSha, ...content } = manifest;
|
|
41156
|
+
return { ...manifest, releaseId: releaseIdFor(gitSha2, content) };
|
|
40320
41157
|
}
|
|
40321
41158
|
function shellSha(root = REPO_ROOT7) {
|
|
40322
41159
|
if (!isMonorepoCheckout(root)) return "";
|
|
@@ -41261,6 +42098,7 @@ var init_build3 = __esm({
|
|
|
41261
42098
|
init_src6();
|
|
41262
42099
|
init_check();
|
|
41263
42100
|
init_file_store();
|
|
42101
|
+
init_release_patch();
|
|
41264
42102
|
init_validate_descriptor();
|
|
41265
42103
|
init_wix_member_login();
|
|
41266
42104
|
init_wix_placeholders();
|
|
@@ -41923,7 +42761,7 @@ __export(studio_exports, {
|
|
|
41923
42761
|
renderStudioHtml: () => renderStudioHtml,
|
|
41924
42762
|
writeStudioRecord: () => writeStudioRecord
|
|
41925
42763
|
});
|
|
41926
|
-
import { createHash as
|
|
42764
|
+
import { createHash as createHash21 } from "node:crypto";
|
|
41927
42765
|
import { existsSync as existsSync41, readdirSync as readdirSync22, readFileSync as readFileSync44, realpathSync as realpathSync5, statSync as statSync8 } from "node:fs";
|
|
41928
42766
|
import { basename as basename10, extname as extname3, join as join39, relative as relative7, resolve as resolve33, sep as sep9 } from "node:path";
|
|
41929
42767
|
import { pathToFileURL as pathToFileURL7 } from "node:url";
|
|
@@ -42161,7 +42999,7 @@ function buildView(dir, memory, now) {
|
|
|
42161
42999
|
reveal: memory.reveal ?? null
|
|
42162
43000
|
};
|
|
42163
43001
|
const { generatedAt: _ignored, timelineFrames: _count, ...stable } = view;
|
|
42164
|
-
const v =
|
|
43002
|
+
const v = createHash21("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
|
|
42165
43003
|
return { v, ...view };
|
|
42166
43004
|
}
|
|
42167
43005
|
function writeAtomic(file, content) {
|
|
@@ -42659,7 +43497,7 @@ function patchHonesty(opts) {
|
|
|
42659
43497
|
return { attestation, source };
|
|
42660
43498
|
}
|
|
42661
43499
|
var SELECTOR_KINDS;
|
|
42662
|
-
var
|
|
43500
|
+
var init_release_patch2 = __esm({
|
|
42663
43501
|
"src/release-patch.ts"() {
|
|
42664
43502
|
init_widget_identity();
|
|
42665
43503
|
SELECTOR_KINDS = ["changed", "screen:<id|glob>", "bundle:<name>", "tokens", "app-config"];
|
|
@@ -45405,7 +46243,7 @@ __export(pack_exports, {
|
|
|
45405
46243
|
unpackTo: () => unpackTo,
|
|
45406
46244
|
writeZip: () => writeZip
|
|
45407
46245
|
});
|
|
45408
|
-
import { createHash as
|
|
46246
|
+
import { createHash as createHash22 } from "node:crypto";
|
|
45409
46247
|
import { deflateRawSync, inflateRawSync } from "node:zlib";
|
|
45410
46248
|
import { existsSync as existsSync44, mkdirSync as mkdirSync21, readFileSync as readFileSync48, readdirSync as readdirSync24, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
|
|
45411
46249
|
import { dirname as dirname26, join as join42, resolve as resolve37 } from "node:path";
|
|
@@ -45555,11 +46393,11 @@ function gatherReleaseEntries(appDir2) {
|
|
|
45555
46393
|
return { entries, manifest, skipped };
|
|
45556
46394
|
}
|
|
45557
46395
|
function sha256Hex4(bytes2) {
|
|
45558
|
-
return
|
|
46396
|
+
return createHash22("sha256").update(bytes2).digest("hex");
|
|
45559
46397
|
}
|
|
45560
46398
|
function computeContentHash(entries) {
|
|
45561
46399
|
const input = packageHashInput(entries.map((e) => ({ path: e.path, hash: sha256Hex4(e.bytes) })));
|
|
45562
|
-
return "sha256-" +
|
|
46400
|
+
return "sha256-" + createHash22("sha256").update(input).digest("base64");
|
|
45563
46401
|
}
|
|
45564
46402
|
function buildEntries(appDir2, app, opts) {
|
|
45565
46403
|
const { entries: released, manifest, skipped } = gatherReleaseEntries(appDir2);
|
|
@@ -47768,10 +48606,10 @@ var init_calls = __esm({
|
|
|
47768
48606
|
});
|
|
47769
48607
|
|
|
47770
48608
|
// ../../core/functions-runtime/src/queue.ts
|
|
47771
|
-
import { createHash as
|
|
48609
|
+
import { createHash as createHash23 } from "node:crypto";
|
|
47772
48610
|
function inputSchemaHash(definition) {
|
|
47773
48611
|
const schema = definition.input;
|
|
47774
|
-
return schema ?
|
|
48612
|
+
return schema ? createHash23("sha256").update(JSON.stringify(schema.descriptor ?? { kind: schema.kind, optional: schema.optional === true })).digest("hex") : "";
|
|
47775
48613
|
}
|
|
47776
48614
|
function createFunctionEnqueueCapability(options) {
|
|
47777
48615
|
return {
|
|
@@ -47837,14 +48675,14 @@ var init_queue = __esm({
|
|
|
47837
48675
|
});
|
|
47838
48676
|
|
|
47839
48677
|
// ../../core/data-resources/src/plan.ts
|
|
47840
|
-
import { createHash as
|
|
48678
|
+
import { createHash as createHash24 } from "node:crypto";
|
|
47841
48679
|
function canonical2(value) {
|
|
47842
48680
|
if (Array.isArray(value)) return `[${value.map(canonical2).join(",")}]`;
|
|
47843
48681
|
if (value && typeof value === "object") return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key2, item]) => `${JSON.stringify(key2)}:${canonical2(item)}`).join(",")}}`;
|
|
47844
48682
|
return JSON.stringify(value);
|
|
47845
48683
|
}
|
|
47846
48684
|
function hashResourcePlan(plan) {
|
|
47847
|
-
return
|
|
48685
|
+
return createHash24("sha256").update(canonical2(plan)).digest("hex");
|
|
47848
48686
|
}
|
|
47849
48687
|
function finalize(input) {
|
|
47850
48688
|
return { ...input, hash: hashResourcePlan(input) };
|
|
@@ -48780,7 +49618,7 @@ __export(studio_reel_exports, {
|
|
|
48780
49618
|
thin: () => thin,
|
|
48781
49619
|
utcDay: () => utcDay
|
|
48782
49620
|
});
|
|
48783
|
-
import { createHash as
|
|
49621
|
+
import { createHash as createHash25 } from "node:crypto";
|
|
48784
49622
|
import { lstatSync as lstatSync9, readFileSync as readFileSync55 } from "node:fs";
|
|
48785
49623
|
import { basename as basename12, join as join49 } from "node:path";
|
|
48786
49624
|
function loadMemory2(dir) {
|
|
@@ -48868,7 +49706,7 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
|
|
|
48868
49706
|
const st = lstatSync9(path2);
|
|
48869
49707
|
if (!st.isFile() || st.nlink !== 1) return null;
|
|
48870
49708
|
const bytes2 = readFileSync55(path2);
|
|
48871
|
-
return
|
|
49709
|
+
return createHash25("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
|
|
48872
49710
|
} catch {
|
|
48873
49711
|
return null;
|
|
48874
49712
|
}
|
|
@@ -49288,7 +50126,7 @@ __export(browser_authoring_exports, {
|
|
|
49288
50126
|
sdkSubpathReader: () => sdkSubpathReader
|
|
49289
50127
|
});
|
|
49290
50128
|
import { createServer as createServer3 } from "node:http";
|
|
49291
|
-
import { createHash as
|
|
50129
|
+
import { createHash as createHash26, randomBytes as randomBytes4 } from "node:crypto";
|
|
49292
50130
|
import { existsSync as existsSync52, readdirSync as readdirSync26, readFileSync as readFileSync57, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
|
|
49293
50131
|
import { join as join51, posix as posix2, relative as relative9, sep as sep12 } from "node:path";
|
|
49294
50132
|
function humanMs(ms) {
|
|
@@ -49387,7 +50225,7 @@ function encodeJob(job) {
|
|
|
49387
50225
|
return Buffer.from(JSON.stringify(job), "utf8").toString("base64");
|
|
49388
50226
|
}
|
|
49389
50227
|
function mintToken2(payload, nonce = randomBytes4(16).toString("hex")) {
|
|
49390
|
-
const digest2 =
|
|
50228
|
+
const digest2 = createHash26("sha256").update(payload, "utf8").digest("base64url");
|
|
49391
50229
|
return `${nonce}.${digest2}`;
|
|
49392
50230
|
}
|
|
49393
50231
|
function defaultOpenBrowser2(url) {
|
|
@@ -50310,7 +51148,7 @@ function makeServeHandler(opts) {
|
|
|
50310
51148
|
}
|
|
50311
51149
|
const PINNED_MANIFEST_CACHE_CAP = 100;
|
|
50312
51150
|
const pinnedManifestCache = /* @__PURE__ */ new Map();
|
|
50313
|
-
const
|
|
51151
|
+
const sha256hex2 = (b) => sha256Hex(b);
|
|
50314
51152
|
function resolvePin(request, url) {
|
|
50315
51153
|
return request.headers.get("x-himalaya-release") || url.searchParams.get("release") || null;
|
|
50316
51154
|
}
|
|
@@ -50363,7 +51201,7 @@ ${releaseId}`;
|
|
|
50363
51201
|
const doc = store.getDoc ? await store.getDoc(app, hash) : null;
|
|
50364
51202
|
if (doc) return doc;
|
|
50365
51203
|
const bytes2 = await slot();
|
|
50366
|
-
return bytes2 &&
|
|
51204
|
+
return bytes2 && sha256hex2(bytes2) === hash ? bytes2 : null;
|
|
50367
51205
|
}
|
|
50368
51206
|
function callbackUrlFor(url) {
|
|
50369
51207
|
const base = ((typeof opts.publicBaseUrl === "function" ? opts.publicBaseUrl(url) : opts.publicBaseUrl) ?? url.origin).replace(/\/$/, "");
|
|
@@ -51027,7 +51865,7 @@ ${releaseId}`;
|
|
|
51027
51865
|
}
|
|
51028
51866
|
}
|
|
51029
51867
|
}
|
|
51030
|
-
const cfgTag = ringPinned ?
|
|
51868
|
+
const cfgTag = ringPinned ? sha256hex2(JSON.stringify(appConfig)) : mm.appConfigHash ?? "";
|
|
51031
51869
|
const etag = strongEtag(`${mm.releaseId}:${cfgTag}:${mm.tokensHash ?? ""}:${screenHash}:${homeBundle?.sha256 ?? ""}`);
|
|
51032
51870
|
const cache = pin && !ringPinned ? PINNED_CACHE_CONTROL : DOC_CACHE_CONTROL;
|
|
51033
51871
|
const vary = pin ? { vary: ringPinned ? `x-himalaya-release, ${RELEASE_SOURCE_HEADER}` : "x-himalaya-release" } : {};
|
|
@@ -51630,21 +52468,21 @@ var init_preview_assets_generated = __esm({
|
|
|
51630
52468
|
"../../packages/serve/src/preview-assets.generated.ts"() {
|
|
51631
52469
|
init_preview_assets_format();
|
|
51632
52470
|
PREVIEW_ASSET_MANIFEST = [
|
|
51633
|
-
{ key: "assets/index-
|
|
51634
|
-
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset:
|
|
51635
|
-
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset:
|
|
51636
|
-
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset:
|
|
51637
|
-
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
51638
|
-
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset:
|
|
51639
|
-
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset:
|
|
51640
|
-
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
51641
|
-
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset:
|
|
51642
|
-
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
51643
|
-
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset:
|
|
51644
|
-
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset:
|
|
51645
|
-
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
51646
|
-
{ key: "index.html", contentType: "text/html; charset=utf-8", offset:
|
|
51647
|
-
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset:
|
|
52471
|
+
{ key: "assets/index-zgMUMszy.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1058954, sha256: "f7d0a27ec7c0241f1ab3bbda5a83b3d0195f95c0cefbda2125289ce966b6f2da" },
|
|
52472
|
+
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1058962, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
|
|
52473
|
+
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1063572, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
|
|
52474
|
+
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1552156, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
|
|
52475
|
+
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1643080, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
|
|
52476
|
+
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1734064, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
|
|
52477
|
+
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1825044, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
|
|
52478
|
+
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1915916, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
|
|
52479
|
+
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2006896, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
|
|
52480
|
+
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2115532, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
|
|
52481
|
+
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2224616, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
|
|
52482
|
+
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2333664, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
|
|
52483
|
+
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2441364, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
|
|
52484
|
+
{ key: "index.html", contentType: "text/html; charset=utf-8", offset: 2529472, length: 4947, sha256: "b2015f4f6ddcb285a96284836207d8e60d0bf4de727b072c4a3b48e919f5067a" },
|
|
52485
|
+
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2534419, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
|
|
51648
52486
|
];
|
|
51649
52487
|
}
|
|
51650
52488
|
});
|
|
@@ -52774,7 +53612,7 @@ function loadStoreConfig(app) {
|
|
|
52774
53612
|
}
|
|
52775
53613
|
var init_preflight = __esm({
|
|
52776
53614
|
"../store-release/preflight.ts"() {
|
|
52777
|
-
|
|
53615
|
+
init_paths2();
|
|
52778
53616
|
}
|
|
52779
53617
|
});
|
|
52780
53618
|
|
|
@@ -52914,7 +53752,7 @@ __export(preflight_app_exports, {
|
|
|
52914
53752
|
profileAccount: () => profileAccount
|
|
52915
53753
|
});
|
|
52916
53754
|
import { readFileSync as readFileSync66, existsSync as existsSync60, readdirSync as readdirSync28, statSync as statSync15 } from "node:fs";
|
|
52917
|
-
import { createHash as
|
|
53755
|
+
import { createHash as createHash27 } from "node:crypto";
|
|
52918
53756
|
import { join as join57 } from "node:path";
|
|
52919
53757
|
function analyze(projectYml, storeConfig, icon, account) {
|
|
52920
53758
|
const f = [];
|
|
@@ -53203,7 +54041,7 @@ function placeholderHashes2() {
|
|
|
53203
54041
|
const p = join57(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
|
|
53204
54042
|
try {
|
|
53205
54043
|
if (!statSync15(p).isFile()) continue;
|
|
53206
|
-
return [LEGACY_PLACEHOLDER_SHA2562,
|
|
54044
|
+
return [LEGACY_PLACEHOLDER_SHA2562, createHash27("sha256").update(readFileSync66(p)).digest("hex")];
|
|
53207
54045
|
} catch {
|
|
53208
54046
|
}
|
|
53209
54047
|
}
|
|
@@ -53240,7 +54078,7 @@ async function appIconState(iosDir) {
|
|
|
53240
54078
|
try {
|
|
53241
54079
|
if (!statSync15(p).isFile()) continue;
|
|
53242
54080
|
const bytes2 = readFileSync66(p);
|
|
53243
|
-
const hash =
|
|
54081
|
+
const hash = createHash27("sha256").update(bytes2).digest("hex");
|
|
53244
54082
|
const pixels = await pixelIssues(bytes2);
|
|
53245
54083
|
return {
|
|
53246
54084
|
exists: true,
|
|
@@ -53275,7 +54113,7 @@ ${app}: ${errors.length} error(s), ${warns.length} warning(s).`);
|
|
|
53275
54113
|
var APP_STORE_SIZES, PNG_COLOR_TYPE_RGB, PLAY_FEATURE_GRAPHIC, LEGACY_PLACEHOLDER_SHA2562;
|
|
53276
54114
|
var init_preflight_app = __esm({
|
|
53277
54115
|
"../store-release/preflight-app.ts"() {
|
|
53278
|
-
|
|
54116
|
+
init_paths2();
|
|
53279
54117
|
init_preflight();
|
|
53280
54118
|
init_credential_profile();
|
|
53281
54119
|
init_app_icon_rules();
|
|
@@ -53382,7 +54220,7 @@ function bumpVersion(app, opts) {
|
|
|
53382
54220
|
}
|
|
53383
54221
|
var init_version = __esm({
|
|
53384
54222
|
"../store-release/version.ts"() {
|
|
53385
|
-
|
|
54223
|
+
init_paths2();
|
|
53386
54224
|
}
|
|
53387
54225
|
});
|
|
53388
54226
|
|
|
@@ -53441,7 +54279,7 @@ async function main2() {
|
|
|
53441
54279
|
var ASC_API;
|
|
53442
54280
|
var init_register_bundle = __esm({
|
|
53443
54281
|
"../store-release/register-bundle.ts"() {
|
|
53444
|
-
|
|
54282
|
+
init_paths2();
|
|
53445
54283
|
init_preflight();
|
|
53446
54284
|
ASC_API = "https://api.appstoreconnect.apple.com";
|
|
53447
54285
|
if (runningAsScript("store-release/register-bundle.ts")) {
|
|
@@ -53506,7 +54344,7 @@ async function main3() {
|
|
|
53506
54344
|
var ASC;
|
|
53507
54345
|
var init_asc_status = __esm({
|
|
53508
54346
|
"../store-release/asc-status.ts"() {
|
|
53509
|
-
|
|
54347
|
+
init_paths2();
|
|
53510
54348
|
init_register_bundle();
|
|
53511
54349
|
init_preflight();
|
|
53512
54350
|
ASC = "https://api.appstoreconnect.apple.com";
|
|
@@ -63446,7 +64284,7 @@ var init_context = __esm({
|
|
|
63446
64284
|
// src/cli.ts
|
|
63447
64285
|
import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync75, mkdirSync as mkdirSync31, writeFileSync as writeFileSync29, existsSync as existsSync63, rmSync as rmSync14, readdirSync as readdirSync29, statSync as statSync17, lstatSync as lstatSync11, accessSync, constants as constants2, renameSync as renameSync7, cpSync as cpSync3 } from "node:fs";
|
|
63448
64286
|
import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
|
|
63449
|
-
import { createHash as
|
|
64287
|
+
import { createHash as createHash28, randomUUID as randomUUID5 } from "node:crypto";
|
|
63450
64288
|
import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
|
|
63451
64289
|
import { join as join61, resolve as resolve47, relative as relative11, basename as basename15, dirname as dirname34 } from "node:path";
|
|
63452
64290
|
import { fileURLToPath as fileURLToPath24 } from "node:url";
|
|
@@ -64046,743 +64884,7 @@ init_store();
|
|
|
64046
64884
|
init_contract();
|
|
64047
64885
|
init_app_link();
|
|
64048
64886
|
init_himi_report();
|
|
64049
|
-
|
|
64050
|
-
// src/attestation.ts
|
|
64051
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
64052
|
-
import { createHash as createHash3, randomBytes } from "node:crypto";
|
|
64053
|
-
import {
|
|
64054
|
-
existsSync as existsSync3,
|
|
64055
|
-
lstatSync as lstatSync3,
|
|
64056
|
-
mkdirSync as mkdirSync5,
|
|
64057
|
-
realpathSync,
|
|
64058
|
-
readlinkSync as readlinkSync2,
|
|
64059
|
-
readFileSync as readFileSync5,
|
|
64060
|
-
readdirSync as readdirSync3,
|
|
64061
|
-
unlinkSync,
|
|
64062
|
-
writeFileSync as writeFileSync5
|
|
64063
|
-
} from "node:fs";
|
|
64064
|
-
import { basename as basename2, join as join6, relative, resolve as resolve6 } from "node:path";
|
|
64065
|
-
|
|
64066
|
-
// ../verify-gate/diff-hash.mjs
|
|
64067
|
-
import {
|
|
64068
|
-
lstatSync as lstatSync2,
|
|
64069
|
-
mkdirSync as mkdirSync4,
|
|
64070
|
-
mkdtempSync,
|
|
64071
|
-
readFileSync as readFileSync4,
|
|
64072
|
-
readlinkSync,
|
|
64073
|
-
rmSync as rmSync2,
|
|
64074
|
-
writeFileSync as writeFileSync4
|
|
64075
|
-
} from "node:fs";
|
|
64076
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
64077
|
-
import { tmpdir } from "node:os";
|
|
64078
|
-
import { basename, dirname as dirname5, join as join5, resolve as resolve5 } from "node:path";
|
|
64079
|
-
import { pathToFileURL } from "node:url";
|
|
64080
|
-
|
|
64081
|
-
// ../pre-pr-review/scope.mjs
|
|
64082
|
-
import { execFileSync } from "node:child_process";
|
|
64083
|
-
import { createHash } from "node:crypto";
|
|
64084
|
-
import { resolve as resolve4, dirname as dirname4, isAbsolute, join as join4 } from "node:path";
|
|
64085
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
64086
|
-
|
|
64087
|
-
// ../ai-review-gate/paths.mjs
|
|
64088
|
-
import { resolve as resolve3, dirname as dirname3 } from "node:path";
|
|
64089
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
64090
|
-
var REPO_ROOT = resolve3(dirname3(fileURLToPath2(import.meta.url)), "../..");
|
|
64091
|
-
|
|
64092
|
-
// ../pre-pr-review/command.mjs
|
|
64093
|
-
var SHELL_NAMES = "sh|bash|zsh|ksh|dash|ash|fish|csh|tcsh|pwsh|xonsh|nu";
|
|
64094
|
-
var SHELL_TOKEN = new RegExp(`^(?:.*/)?(?:${SHELL_NAMES})$`);
|
|
64095
|
-
|
|
64096
|
-
// ../pre-pr-review/scope.mjs
|
|
64097
|
-
var REPO_ROOT2 = resolve4(dirname4(fileURLToPath3(import.meta.url)), "../..");
|
|
64098
|
-
var BASE_REF = process.env.PRE_PR_REVIEW_BASE || "origin/master";
|
|
64099
|
-
function resolveRepoRoot(cwd = process.cwd()) {
|
|
64100
|
-
let top;
|
|
64101
|
-
try {
|
|
64102
|
-
top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
64103
|
-
cwd,
|
|
64104
|
-
encoding: "utf8",
|
|
64105
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
64106
|
-
}).toString().trim();
|
|
64107
|
-
} catch (err) {
|
|
64108
|
-
throw new Error(`cannot resolve a repo root from ${cwd}: ${err?.message || err}`);
|
|
64109
|
-
}
|
|
64110
|
-
if (!top) throw new Error(`cannot resolve a repo root from ${cwd}: git named no toplevel`);
|
|
64111
|
-
return top;
|
|
64112
|
-
}
|
|
64113
|
-
function git(args, opts = {}) {
|
|
64114
|
-
return execFileSync("git", args, {
|
|
64115
|
-
cwd: opts.cwd || REPO_ROOT2,
|
|
64116
|
-
encoding: "utf8",
|
|
64117
|
-
// A full PR diff can be megabytes; the default 1MB cap would truncate mid-hunk and the
|
|
64118
|
-
// delta parser would then read a malformed diff as "unsupported shape".
|
|
64119
|
-
maxBuffer: opts.maxBuffer ?? 256 * 1024 * 1024,
|
|
64120
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
64121
|
-
}).toString();
|
|
64122
|
-
}
|
|
64123
|
-
function diffHash(diff) {
|
|
64124
|
-
return createHash("sha256").update(diff).digest("hex");
|
|
64125
|
-
}
|
|
64126
|
-
|
|
64127
|
-
// ../verify-gate/diff-hash.mjs
|
|
64128
|
-
var VALIDATION_METADATA = /* @__PURE__ */ new Set([
|
|
64129
|
-
".claude/.validation-baseline.json",
|
|
64130
|
-
".claude/.validation-evidence.json"
|
|
64131
|
-
]);
|
|
64132
|
-
function isValidationMetadata(path2) {
|
|
64133
|
-
return VALIDATION_METADATA.has(path2) || /(^|[\\/])\.himi([\\/]|$)/.test(path2);
|
|
64134
|
-
}
|
|
64135
|
-
function tryGit(args, root) {
|
|
64136
|
-
try {
|
|
64137
|
-
return git(args, { cwd: root });
|
|
64138
|
-
} catch {
|
|
64139
|
-
return null;
|
|
64140
|
-
}
|
|
64141
|
-
}
|
|
64142
|
-
function committedRecords(raw) {
|
|
64143
|
-
const fields = raw.split("\0");
|
|
64144
|
-
if (fields.length > 0 && fields[fields.length - 1] === "") fields.pop();
|
|
64145
|
-
if (fields.length % 2 !== 0) return null;
|
|
64146
|
-
const records = [];
|
|
64147
|
-
for (let i = 0; i < fields.length; i += 2) {
|
|
64148
|
-
const metadata2 = fields[i];
|
|
64149
|
-
const path2 = fields[i + 1];
|
|
64150
|
-
if (!metadata2.startsWith(":") || path2 === "") return null;
|
|
64151
|
-
if (isValidationMetadata(path2)) continue;
|
|
64152
|
-
records.push(`${Buffer.byteLength(path2)}:${path2}\0${metadata2}\0`);
|
|
64153
|
-
}
|
|
64154
|
-
return records.sort().join("");
|
|
64155
|
-
}
|
|
64156
|
-
function committedIdentity(root) {
|
|
64157
|
-
const head = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root)?.trim();
|
|
64158
|
-
if (!head) return "unborn\0";
|
|
64159
|
-
const base = tryGit(["merge-base", BASE_REF, "HEAD"], root)?.trim();
|
|
64160
|
-
if (base) {
|
|
64161
|
-
const raw = tryGit(["diff", "--raw", "--no-renames", "-z", "--no-abbrev", base, "HEAD"], root);
|
|
64162
|
-
const records = raw === null ? null : committedRecords(raw);
|
|
64163
|
-
if (records !== null) return `base\0${base}\0${records}`;
|
|
64164
|
-
}
|
|
64165
|
-
const listing = tryGit(["ls-tree", "-rz", "--full-tree", "HEAD"], root);
|
|
64166
|
-
const tree = listing === null ? null : treeRecords(listing);
|
|
64167
|
-
return tree !== null ? `tree-listing\0${tree}` : `commit\0${head}\0`;
|
|
64168
|
-
}
|
|
64169
|
-
function treeRecords(listing) {
|
|
64170
|
-
const records = [];
|
|
64171
|
-
for (const record2 of listing.split("\0")) {
|
|
64172
|
-
if (record2 === "") continue;
|
|
64173
|
-
const tab = record2.indexOf(" ");
|
|
64174
|
-
if (tab <= 0 || tab === record2.length - 1) return null;
|
|
64175
|
-
const path2 = record2.slice(tab + 1);
|
|
64176
|
-
if (isValidationMetadata(path2)) continue;
|
|
64177
|
-
records.push(`${Buffer.byteLength(path2)}:${path2}\0${record2.slice(0, tab)}\0`);
|
|
64178
|
-
}
|
|
64179
|
-
return records.sort().join("");
|
|
64180
|
-
}
|
|
64181
|
-
function treeEntries(output, index = false) {
|
|
64182
|
-
const entries = /* @__PURE__ */ new Map();
|
|
64183
|
-
for (const record2 of output.split("\0").filter(Boolean)) {
|
|
64184
|
-
const tab = record2.indexOf(" ");
|
|
64185
|
-
if (tab === -1) continue;
|
|
64186
|
-
const metadata2 = record2.slice(0, tab).split(" ");
|
|
64187
|
-
if (index && metadata2[2] !== "0") continue;
|
|
64188
|
-
entries.set(record2.slice(tab + 1), {
|
|
64189
|
-
mode: metadata2[0],
|
|
64190
|
-
oid: index ? metadata2[1] : metadata2[2]
|
|
64191
|
-
});
|
|
64192
|
-
}
|
|
64193
|
-
return entries;
|
|
64194
|
-
}
|
|
64195
|
-
function sameEntry(left, right) {
|
|
64196
|
-
if (!left || !right) return left === right;
|
|
64197
|
-
return left.mode === right.mode && left.oid === right.oid;
|
|
64198
|
-
}
|
|
64199
|
-
function stagedIdentity(head, index, worktree) {
|
|
64200
|
-
if (sameEntry(index, head) || sameEntry(index, worktree)) return "";
|
|
64201
|
-
if (!index) return head ? "deleted" : "";
|
|
64202
|
-
return `${index.mode}:${index.oid}`;
|
|
64203
|
-
}
|
|
64204
|
-
function blobOid(contents, algorithm) {
|
|
64205
|
-
const body = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
|
|
64206
|
-
return createHash2(algorithm).update(Buffer.from(`blob ${body.byteLength}\0`)).update(body).digest("hex");
|
|
64207
|
-
}
|
|
64208
|
-
function validationDiffHash(cwd = process.cwd()) {
|
|
64209
|
-
const root = resolveRepoRoot(cwd);
|
|
64210
|
-
const born = tryGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], root) !== null;
|
|
64211
|
-
const staged = git(["diff", "--cached", "--name-only", "--no-renames", "-z", ...born ? ["HEAD"] : []], {
|
|
64212
|
-
cwd: root
|
|
64213
|
-
}).split("\0").filter(Boolean);
|
|
64214
|
-
const unstaged = git(["diff", "--name-only", "--no-renames", "-z"], { cwd: root }).split("\0").filter(Boolean);
|
|
64215
|
-
const untracked = git(["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
64216
|
-
cwd: root
|
|
64217
|
-
}).split("\0").filter(Boolean);
|
|
64218
|
-
const headEntries = born ? treeEntries(git(["ls-tree", "-rz", "--full-tree", "HEAD"], { cwd: root })) : /* @__PURE__ */ new Map();
|
|
64219
|
-
const indexEntries = treeEntries(git(["ls-files", "--stage", "-z"], { cwd: root }), true);
|
|
64220
|
-
const objectAlgorithm = git(["rev-parse", "--show-object-format"], { cwd: root }).trim();
|
|
64221
|
-
let trustFilesystemMode = true;
|
|
64222
|
-
try {
|
|
64223
|
-
trustFilesystemMode = git(["config", "--bool", "--get", "core.fileMode"], { cwd: root }).trim() !== "false";
|
|
64224
|
-
} catch {
|
|
64225
|
-
}
|
|
64226
|
-
const paths = [.../* @__PURE__ */ new Set([...staged, ...unstaged, ...untracked])].filter((path2) => !isValidationMetadata(path2)).sort();
|
|
64227
|
-
const identity = paths.map((path2) => {
|
|
64228
|
-
const absolute = resolve5(root, path2);
|
|
64229
|
-
const headEntry = headEntries.get(path2);
|
|
64230
|
-
const indexEntry = indexEntries.get(path2);
|
|
64231
|
-
const deletedRecord = () => [`${Buffer.byteLength(path2)}:${path2}`, "deleted", stagedIdentity(headEntry, indexEntry, void 0), ""].join("\0");
|
|
64232
|
-
let stat2;
|
|
64233
|
-
try {
|
|
64234
|
-
stat2 = lstatSync2(absolute);
|
|
64235
|
-
} catch (error) {
|
|
64236
|
-
if (error?.code === "ENOENT") return deletedRecord();
|
|
64237
|
-
throw error;
|
|
64238
|
-
}
|
|
64239
|
-
try {
|
|
64240
|
-
if (stat2.isSymbolicLink()) {
|
|
64241
|
-
const target = readlinkSync(absolute);
|
|
64242
|
-
const worktreeEntry = { mode: "120000", oid: blobOid(target, objectAlgorithm) };
|
|
64243
|
-
const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
|
|
64244
|
-
return [
|
|
64245
|
-
`${Buffer.byteLength(path2)}:${path2}`,
|
|
64246
|
-
"symlink",
|
|
64247
|
-
"120000",
|
|
64248
|
-
diffHash(target),
|
|
64249
|
-
stagedState2,
|
|
64250
|
-
""
|
|
64251
|
-
].join("\0");
|
|
64252
|
-
}
|
|
64253
|
-
if (stat2.isFile()) {
|
|
64254
|
-
const filesystemMode = stat2.mode & 73 ? "100755" : "100644";
|
|
64255
|
-
const worktreeMode = trustFilesystemMode ? filesystemMode : indexEntry?.mode ?? headEntry?.mode ?? "100644";
|
|
64256
|
-
const worktreeOid = git(["hash-object", `--path=${path2}`, "--", path2], { cwd: root }).trim();
|
|
64257
|
-
const worktreeEntry = { mode: worktreeMode, oid: worktreeOid };
|
|
64258
|
-
const stagedState2 = stagedIdentity(headEntry, indexEntry, worktreeEntry);
|
|
64259
|
-
return [
|
|
64260
|
-
`${Buffer.byteLength(path2)}:${path2}`,
|
|
64261
|
-
"file",
|
|
64262
|
-
worktreeMode,
|
|
64263
|
-
diffHash(readFileSync4(absolute)),
|
|
64264
|
-
stagedState2,
|
|
64265
|
-
""
|
|
64266
|
-
].join("\0");
|
|
64267
|
-
}
|
|
64268
|
-
} catch (error) {
|
|
64269
|
-
try {
|
|
64270
|
-
lstatSync2(absolute);
|
|
64271
|
-
} catch (recheck) {
|
|
64272
|
-
if (recheck?.code === "ENOENT") return deletedRecord();
|
|
64273
|
-
}
|
|
64274
|
-
throw error;
|
|
64275
|
-
}
|
|
64276
|
-
const type = stat2.isDirectory() ? "directory" : "other";
|
|
64277
|
-
const stagedState = stagedIdentity(headEntry, indexEntry, void 0);
|
|
64278
|
-
return [
|
|
64279
|
-
`${Buffer.byteLength(path2)}:${path2}`,
|
|
64280
|
-
type,
|
|
64281
|
-
stat2.mode.toString(8),
|
|
64282
|
-
stagedState,
|
|
64283
|
-
""
|
|
64284
|
-
].join("\0");
|
|
64285
|
-
}).join("");
|
|
64286
|
-
const committed = committedIdentity(root);
|
|
64287
|
-
return diffHash(
|
|
64288
|
-
`validation-change-v3\0${Buffer.byteLength(committed)}\0${committed}${identity}`
|
|
64289
|
-
);
|
|
64290
|
-
}
|
|
64291
|
-
function seed(repo, commands) {
|
|
64292
|
-
for (const args of commands) git(args, { cwd: repo });
|
|
64293
|
-
}
|
|
64294
|
-
function write(repo, rel, body) {
|
|
64295
|
-
const absolute = resolve5(repo, rel);
|
|
64296
|
-
mkdirSync4(dirname5(absolute), { recursive: true });
|
|
64297
|
-
writeFileSync4(absolute, body);
|
|
64298
|
-
}
|
|
64299
|
-
function selfTestRepo({ withBase }) {
|
|
64300
|
-
const repo = mkdtempSync(join5(tmpdir(), "diff-hash-self-test-"));
|
|
64301
|
-
seed(repo, [
|
|
64302
|
-
["init", "-q", repo],
|
|
64303
|
-
["-C", repo, "config", "user.email", "self-test@test.invalid"],
|
|
64304
|
-
["-C", repo, "config", "user.name", "diff-hash-self-test"]
|
|
64305
|
-
]);
|
|
64306
|
-
write(repo, "src/base.txt", "base\n");
|
|
64307
|
-
seed(repo, [["-C", repo, "add", "-A"], ["-C", repo, "commit", "-qm", "base"]]);
|
|
64308
|
-
const base = git(["-C", repo, "rev-parse", "HEAD"], { cwd: repo }).trim();
|
|
64309
|
-
if (withBase) seed(repo, [["-C", repo, "update-ref", "refs/remotes/origin/master", base]]);
|
|
64310
|
-
return { repo, base };
|
|
64311
|
-
}
|
|
64312
|
-
function commitChange(repo, base, branch, rel, body) {
|
|
64313
|
-
seed(repo, [["-C", repo, "checkout", "-q", base], ["-C", repo, "checkout", "-q", "-b", branch]]);
|
|
64314
|
-
write(repo, rel, body);
|
|
64315
|
-
seed(repo, [["-C", repo, "add", rel], ["-C", repo, "commit", "-qm", `commit ${rel}`]]);
|
|
64316
|
-
return validationDiffHash(repo);
|
|
64317
|
-
}
|
|
64318
|
-
var SELF_TEST_CASES = [
|
|
64319
|
-
{
|
|
64320
|
-
name: "committed diffs are distinguished (the #1831 regression)",
|
|
64321
|
-
reason: "two unrelated COMMITTED diffs share one identity \u2014 a record for either would satisfy the gate for the other",
|
|
64322
|
-
run: () => {
|
|
64323
|
-
const { repo, base } = selfTestRepo({ withBase: true });
|
|
64324
|
-
try {
|
|
64325
|
-
return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
|
|
64326
|
-
} finally {
|
|
64327
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64328
|
-
}
|
|
64329
|
-
}
|
|
64330
|
-
},
|
|
64331
|
-
{
|
|
64332
|
-
name: "committed diffs are distinguished with NO base ref (fallback ladder)",
|
|
64333
|
-
reason: "without origin/master the committed identity collapses to a constant",
|
|
64334
|
-
run: () => {
|
|
64335
|
-
const { repo, base } = selfTestRepo({ withBase: false });
|
|
64336
|
-
try {
|
|
64337
|
-
return commitChange(repo, base, "a", "src/alpha.txt", "alpha\n") !== commitChange(repo, base, "b", "src/beta.txt", "a different change\n");
|
|
64338
|
-
} finally {
|
|
64339
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64340
|
-
}
|
|
64341
|
-
}
|
|
64342
|
-
},
|
|
64343
|
-
{
|
|
64344
|
-
name: "a clean tree is not the digest of an empty path list",
|
|
64345
|
-
reason: 'a clean tree still hashes to sha256("validation-worktree-v2\\0"), the constant measured in #1831',
|
|
64346
|
-
run: () => {
|
|
64347
|
-
const { repo } = selfTestRepo({ withBase: true });
|
|
64348
|
-
try {
|
|
64349
|
-
return validationDiffHash(repo) !== createHash2("sha256").update("validation-worktree-v2\0").digest("hex");
|
|
64350
|
-
} finally {
|
|
64351
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64352
|
-
}
|
|
64353
|
-
}
|
|
64354
|
-
},
|
|
64355
|
-
{
|
|
64356
|
-
name: "an uncommitted edit still changes the identity",
|
|
64357
|
-
reason: "the working-tree half of the identity stopped responding to edits",
|
|
64358
|
-
run: () => {
|
|
64359
|
-
const { repo } = selfTestRepo({ withBase: true });
|
|
64360
|
-
try {
|
|
64361
|
-
const before = validationDiffHash(repo);
|
|
64362
|
-
write(repo, "src/base.txt", "edited in the working tree\n");
|
|
64363
|
-
return validationDiffHash(repo) !== before;
|
|
64364
|
-
} finally {
|
|
64365
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64366
|
-
}
|
|
64367
|
-
}
|
|
64368
|
-
},
|
|
64369
|
-
{
|
|
64370
|
-
// The "can pass" half. Without it every case above is satisfied by a hash that simply
|
|
64371
|
-
// changes every call, which would identify nothing and invalidate every record.
|
|
64372
|
-
name: "one unchanged state hashes to one value",
|
|
64373
|
-
reason: "the identity is not stable for an unchanged state \u2014 every record would read stale",
|
|
64374
|
-
run: () => {
|
|
64375
|
-
const { repo } = selfTestRepo({ withBase: true });
|
|
64376
|
-
try {
|
|
64377
|
-
write(repo, "src/base.txt", "edited in the working tree\n");
|
|
64378
|
-
return validationDiffHash(repo) === validationDiffHash(repo);
|
|
64379
|
-
} finally {
|
|
64380
|
-
rmSync2(repo, { recursive: true, force: true });
|
|
64381
|
-
}
|
|
64382
|
-
}
|
|
64383
|
-
}
|
|
64384
|
-
];
|
|
64385
|
-
function runSelfTest() {
|
|
64386
|
-
let failed2 = 0;
|
|
64387
|
-
for (const testCase of SELF_TEST_CASES) {
|
|
64388
|
-
let ok = false;
|
|
64389
|
-
let thrown = null;
|
|
64390
|
-
try {
|
|
64391
|
-
ok = testCase.run() === true;
|
|
64392
|
-
} catch (error) {
|
|
64393
|
-
thrown = error?.message || String(error);
|
|
64394
|
-
}
|
|
64395
|
-
if (ok) {
|
|
64396
|
-
process.stdout.write(` \u2713 ${testCase.name}
|
|
64397
|
-
`);
|
|
64398
|
-
continue;
|
|
64399
|
-
}
|
|
64400
|
-
failed2++;
|
|
64401
|
-
process.stderr.write(` \u2717 ${testCase.name}
|
|
64402
|
-
${thrown ?? testCase.reason}
|
|
64403
|
-
`);
|
|
64404
|
-
}
|
|
64405
|
-
return failed2;
|
|
64406
|
-
}
|
|
64407
|
-
if (process.argv[1] && basename(process.argv[1]) === "diff-hash.mjs" && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
64408
|
-
if (process.argv.slice(2).includes("--self-test")) {
|
|
64409
|
-
process.stdout.write("\u25B8 diff-hash --self-test: proving the identity can change, and can hold\n");
|
|
64410
|
-
const failed2 = runSelfTest();
|
|
64411
|
-
if (failed2) {
|
|
64412
|
-
process.stderr.write(
|
|
64413
|
-
`\u2717 diff-hash self-test FAILED (${failed2} case(s)) \u2014 the evidence record's diff identity cannot be trusted
|
|
64414
|
-
`
|
|
64415
|
-
);
|
|
64416
|
-
process.exit(1);
|
|
64417
|
-
}
|
|
64418
|
-
process.stdout.write(`\u2713 diff-hash self-test passed (${SELF_TEST_CASES.length} cases)
|
|
64419
|
-
`);
|
|
64420
|
-
} else {
|
|
64421
|
-
process.stdout.write(`${validationDiffHash()}
|
|
64422
|
-
`);
|
|
64423
|
-
}
|
|
64424
|
-
}
|
|
64425
|
-
|
|
64426
|
-
// src/attestation.ts
|
|
64427
|
-
init_source_deny();
|
|
64428
|
-
init_widget_identity();
|
|
64429
|
-
var DEFAULT_MAX_RUNS = 20;
|
|
64430
|
-
var ATTESTATION_VERSION = 2;
|
|
64431
|
-
function attestationVersionOf(value) {
|
|
64432
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
64433
|
-
const version2 = value.attestation;
|
|
64434
|
-
return typeof version2 === "number" && Number.isInteger(version2) && version2 > 0 ? version2 : null;
|
|
64435
|
-
}
|
|
64436
|
-
function classifyRetainedRun(dir, name) {
|
|
64437
|
-
let value;
|
|
64438
|
-
try {
|
|
64439
|
-
value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
|
|
64440
|
-
} catch {
|
|
64441
|
-
return { kind: "unreadable" };
|
|
64442
|
-
}
|
|
64443
|
-
if (isAttestationV2(value)) return { kind: "current", run: value };
|
|
64444
|
-
const version2 = attestationVersionOf(value);
|
|
64445
|
-
if (version2 === null || version2 === ATTESTATION_VERSION) return { kind: "malformed" };
|
|
64446
|
-
return { kind: version2 < ATTESTATION_VERSION ? "legacy" : "future" };
|
|
64447
|
-
}
|
|
64448
|
-
function screenshotArtifactPath(contentDir2, hash) {
|
|
64449
|
-
return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
|
|
64450
|
-
}
|
|
64451
|
-
var GIT_HASH_MAX_BUFFER = 128 * 1024 * 1024;
|
|
64452
|
-
var sha2562 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
|
|
64453
|
-
function canonical(value) {
|
|
64454
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
64455
|
-
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
64456
|
-
const record2 = value;
|
|
64457
|
-
return `{${Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${canonical(record2[key2])}`).join(",")}}`;
|
|
64458
|
-
}
|
|
64459
|
-
function releasePayloadHash(payload) {
|
|
64460
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha2562(canonical(payload));
|
|
64461
|
-
const record2 = payload;
|
|
64462
|
-
const manifest = record2.manifest;
|
|
64463
|
-
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha2562(canonical(payload));
|
|
64464
|
-
const manifestRecord = manifest;
|
|
64465
|
-
const widgets = manifestRecord.widgets;
|
|
64466
|
-
if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha2562(canonical(payload));
|
|
64467
|
-
return sha2562(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
|
|
64468
|
-
}
|
|
64469
|
-
function withoutSelfHash(attestation) {
|
|
64470
|
-
const { selfHash: _selfHash, ...body } = attestation;
|
|
64471
|
-
return body;
|
|
64472
|
-
}
|
|
64473
|
-
function selfHashFor(attestation) {
|
|
64474
|
-
return sha2562(canonical(withoutSelfHash(attestation)));
|
|
64475
|
-
}
|
|
64476
|
-
function repoRoot(contentDir2) {
|
|
64477
|
-
try {
|
|
64478
|
-
const root = execFileSync2("git", ["-C", contentDir2, "rev-parse", "--show-toplevel"], {
|
|
64479
|
-
encoding: "utf8",
|
|
64480
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
64481
|
-
}).trim();
|
|
64482
|
-
if (!root) return null;
|
|
64483
|
-
execFileSync2("git", ["-C", root, "rev-parse", "--verify", "--quiet", "HEAD^{commit}"], { stdio: "ignore" });
|
|
64484
|
-
return root;
|
|
64485
|
-
} catch {
|
|
64486
|
-
return null;
|
|
64487
|
-
}
|
|
64488
|
-
}
|
|
64489
|
-
function storedContentDir(contentDir2) {
|
|
64490
|
-
const absolute = resolve6(contentDir2);
|
|
64491
|
-
const root = repoRoot(absolute);
|
|
64492
|
-
return root ? relative(root, absolute) || "." : ".";
|
|
64493
|
-
}
|
|
64494
|
-
function currentGitDiffHash(contentDir2) {
|
|
64495
|
-
const root = repoRoot(resolve6(contentDir2));
|
|
64496
|
-
if (!root) return "unavailable";
|
|
64497
|
-
return validationDiffHash(root);
|
|
64498
|
-
}
|
|
64499
|
-
function currentGitUntrackedHash(contentDir2) {
|
|
64500
|
-
const unresolved = resolve6(contentDir2);
|
|
64501
|
-
const absolute = realpathSync.native(unresolved);
|
|
64502
|
-
const root = repoRoot(absolute);
|
|
64503
|
-
if (!root) return sourceTreeHash(absolute);
|
|
64504
|
-
const scope = relative(root, absolute) || ".";
|
|
64505
|
-
const listed = execFileSync2("git", ["-C", root, "ls-files", "--others", "--exclude-standard", "-z", "--", scope], {
|
|
64506
|
-
encoding: "utf8",
|
|
64507
|
-
maxBuffer: GIT_HASH_MAX_BUFFER
|
|
64508
|
-
});
|
|
64509
|
-
const hash = createHash3("sha256");
|
|
64510
|
-
for (const path2 of listed.split("\0").filter(Boolean).sort()) {
|
|
64511
|
-
const local = relative(absolute, join6(root, path2));
|
|
64512
|
-
const segs = local.split(/[\\/]/);
|
|
64513
|
-
if (NOT_ATTESTED_DIRS.has(segs[0])) continue;
|
|
64514
|
-
if (segs.length === 1 && AGENT_TOOLING_ROOT_FILES.includes(segs[0])) continue;
|
|
64515
|
-
const full = join6(root, path2);
|
|
64516
|
-
const stats = lstatSync3(full, { throwIfNoEntry: false });
|
|
64517
|
-
if (!stats) continue;
|
|
64518
|
-
const kind = stats.isSymbolicLink() ? "link" : stats.isDirectory() ? "dir" : stats.isFile() ? "file" : null;
|
|
64519
|
-
if (!kind) continue;
|
|
64520
|
-
const payload = kind === "link" ? Buffer.from(readlinkSync2(full)) : kind === "file" ? readFileSync5(full) : Buffer.alloc(0);
|
|
64521
|
-
const pathBytes = Buffer.from(path2);
|
|
64522
|
-
hash.update(`${kind} ${pathBytes.length} ${payload.length}
|
|
64523
|
-
`).update(pathBytes).update(payload);
|
|
64524
|
-
}
|
|
64525
|
-
return hash.digest("hex");
|
|
64526
|
-
}
|
|
64527
|
-
var NOT_ATTESTED_DIRS = /* @__PURE__ */ new Set([".himi", ...AGENT_TOOLING_DIRS]);
|
|
64528
|
-
function sourceTreeHash(root) {
|
|
64529
|
-
const hash = createHash3("sha256");
|
|
64530
|
-
const visit = (dir) => {
|
|
64531
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
64532
|
-
if (entry.isDirectory() && (entry.name === ".git" || entry.name === "node_modules" || NOT_ATTESTED_DIRS.has(entry.name))) continue;
|
|
64533
|
-
const path2 = join6(dir, entry.name);
|
|
64534
|
-
const local = relative(root, path2);
|
|
64535
|
-
if (dir === root && entry.isFile() && AGENT_TOOLING_ROOT_FILES.includes(entry.name)) continue;
|
|
64536
|
-
if (entry.isDirectory()) visit(path2);
|
|
64537
|
-
else if (entry.isFile()) hash.update(local).update("\0").update(readFileSync5(path2)).update("\0");
|
|
64538
|
-
else if (entry.isSymbolicLink()) hash.update(local).update("\0link\0").update(readlinkSync2(path2)).update("\0");
|
|
64539
|
-
}
|
|
64540
|
-
};
|
|
64541
|
-
visit(root);
|
|
64542
|
-
return hash.digest("hex");
|
|
64543
|
-
}
|
|
64544
|
-
var stringRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
|
|
64545
|
-
function isAttestationV2(value) {
|
|
64546
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
64547
|
-
const a = value;
|
|
64548
|
-
const coverage = a.coverage;
|
|
64549
|
-
const report2 = a.report;
|
|
64550
|
-
return a.attestation === 2 && typeof a.runId === "string" && ["himi-test", "himi-render", "himi-shots"].includes(a.tool) && [a.app, a.contentDir, a.gitDiffHash, a.gitUntrackedHash, a.releaseId, a.releaseHash, a.selfHash].every((item) => typeof item === "string" && item.length > 0) && stringRecord(a.descriptorHashes) && stringRecord(a.screenshots) && !!coverage && [coverage.screens, coverage.actionsDispatched, coverage.uncoveredFns, coverage.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && typeof report2?.ok === "boolean" && !!report2.counts && [report2.counts.errors, report2.counts.warnings, report2.counts.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && (a.prev === null || typeof a.prev === "string");
|
|
64551
|
-
}
|
|
64552
|
-
function readLatestAttestation(contentDir2) {
|
|
64553
|
-
try {
|
|
64554
|
-
const parsed = JSON.parse(readFileSync5(join6(resolve6(contentDir2), ".himi", "attestations", "latest.json"), "utf8"));
|
|
64555
|
-
return isAttestationV2(parsed) ? parsed : null;
|
|
64556
|
-
} catch {
|
|
64557
|
-
return null;
|
|
64558
|
-
}
|
|
64559
|
-
}
|
|
64560
|
-
function readRetainedAttestations(contentDir2) {
|
|
64561
|
-
const dir = join6(resolve6(contentDir2), ".himi", "attestations");
|
|
64562
|
-
if (!existsSync3(dir)) return { runs: [], errors: [] };
|
|
64563
|
-
try {
|
|
64564
|
-
const names = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
|
|
64565
|
-
const parsedRuns = [];
|
|
64566
|
-
const errors = [];
|
|
64567
|
-
for (const name of names) {
|
|
64568
|
-
const classified = classifyRetainedRun(dir, name);
|
|
64569
|
-
if (classified.kind === "current") parsedRuns.push(classified.run);
|
|
64570
|
-
else if (classified.kind === "legacy") {
|
|
64571
|
-
errors.push(`attestation history contains retained run ${name} from an older attestation version \u2014 re-run himi test/render to start a v${ATTESTATION_VERSION} chain`);
|
|
64572
|
-
} else if (classified.kind === "future") {
|
|
64573
|
-
errors.push(`attestation history contains retained run ${name} from a newer attestation version \u2014 upgrade himi rather than deleting it`);
|
|
64574
|
-
} else if (classified.kind === "unreadable") {
|
|
64575
|
-
errors.push(`attestation history contains unreadable retained run ${name}`);
|
|
64576
|
-
} else errors.push(`attestation history contains malformed retained run ${name}`);
|
|
64577
|
-
}
|
|
64578
|
-
const latest = readLatestAttestation(contentDir2);
|
|
64579
|
-
if (existsSync3(join6(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
|
|
64580
|
-
else if (names.length > 0 && !latest) errors.push("attestation history has no latest.json");
|
|
64581
|
-
const byHash = new Map(parsedRuns.map((run2) => [run2.selfHash, run2]));
|
|
64582
|
-
if (byHash.size !== parsedRuns.length) errors.push("attestation history contains duplicate self hashes");
|
|
64583
|
-
for (const run2 of parsedRuns) {
|
|
64584
|
-
if (selfHashFor(run2) !== run2.selfHash) errors.push(`selfHash does not match retained run ${run2.runId}`);
|
|
64585
|
-
for (const expected of Object.values(run2.screenshots)) {
|
|
64586
|
-
const path2 = screenshotArtifactPath(contentDir2, expected);
|
|
64587
|
-
if (!existsSync3(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename2(path2)}`);
|
|
64588
|
-
else if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
|
|
64589
|
-
}
|
|
64590
|
-
}
|
|
64591
|
-
const runs = [];
|
|
64592
|
-
const seen = /* @__PURE__ */ new Set();
|
|
64593
|
-
let cursor = latest ?? void 0;
|
|
64594
|
-
while (cursor && !seen.has(cursor.selfHash)) {
|
|
64595
|
-
runs.push(cursor);
|
|
64596
|
-
seen.add(cursor.selfHash);
|
|
64597
|
-
cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
|
|
64598
|
-
}
|
|
64599
|
-
if (latest && !byHash.has(latest.selfHash)) errors.push("latest.json has no matching retained run");
|
|
64600
|
-
if (seen.size !== parsedRuns.length) errors.push("attestation hash chain does not reach every retained run");
|
|
64601
|
-
runs.push(...parsedRuns.filter((run2) => !seen.has(run2.selfHash)).sort((a, b) => a.runId < b.runId ? 1 : -1));
|
|
64602
|
-
return {
|
|
64603
|
-
runs,
|
|
64604
|
-
errors
|
|
64605
|
-
};
|
|
64606
|
-
} catch {
|
|
64607
|
-
return { runs: [], errors: ["attestation history could not be read"] };
|
|
64608
|
-
}
|
|
64609
|
-
}
|
|
64610
|
-
function selectAttestationForPush(runs) {
|
|
64611
|
-
const newestBehavioral = runs.find((run2) => run2.tool === "himi-test");
|
|
64612
|
-
if (!newestBehavioral) return runs[0];
|
|
64613
|
-
const behavioralIndex = runs.indexOf(newestBehavioral);
|
|
64614
|
-
const newerFailure = runs.slice(0, behavioralIndex).find((run2) => run2.report.ok === false);
|
|
64615
|
-
return newerFailure ?? newestBehavioral;
|
|
64616
|
-
}
|
|
64617
|
-
function pushEvidence(contentDir2, releaseId, verify) {
|
|
64618
|
-
const retained = readRetainedAttestations(contentDir2);
|
|
64619
|
-
const forRelease = retained.runs.filter((candidate) => candidate.releaseId === releaseId);
|
|
64620
|
-
const verified = forRelease.filter(verify);
|
|
64621
|
-
const selected = selectAttestationForPush(verified);
|
|
64622
|
-
return {
|
|
64623
|
-
...selected ? { selected } : {},
|
|
64624
|
-
historyErrors: retained.errors,
|
|
64625
|
-
staleRuns: verified.length === 0 ? forRelease.length : 0
|
|
64626
|
-
};
|
|
64627
|
-
}
|
|
64628
|
-
function summaryOf(attestation) {
|
|
64629
|
-
return {
|
|
64630
|
-
runId: attestation.runId,
|
|
64631
|
-
selfHash: attestation.selfHash,
|
|
64632
|
-
tool: attestation.tool,
|
|
64633
|
-
screenIds: Object.keys(attestation.descriptorHashes).sort(),
|
|
64634
|
-
coverage: attestation.coverage,
|
|
64635
|
-
report: { ok: attestation.report.ok, counts: attestation.report.counts },
|
|
64636
|
-
gitDiffHash: attestation.gitDiffHash,
|
|
64637
|
-
releaseHash: attestation.releaseHash
|
|
64638
|
-
};
|
|
64639
|
-
}
|
|
64640
|
-
function writeAttestation(input, opts = {}) {
|
|
64641
|
-
const contentDir2 = resolve6(input.contentDir);
|
|
64642
|
-
const dir = join6(contentDir2, ".himi", "attestations");
|
|
64643
|
-
mkdirSync5(dir, { recursive: true });
|
|
64644
|
-
const previous = readLatestAttestation(contentDir2);
|
|
64645
|
-
const runId = `${(opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()}-${(opts.randomSuffix ?? (() => randomBytes(3).toString("hex")))()}`;
|
|
64646
|
-
const descriptorHashes = Object.fromEntries(
|
|
64647
|
-
Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha2562(bytes2)])
|
|
64648
|
-
);
|
|
64649
|
-
const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a], [b]) => a.localeCompare(b)).filter(([, path2]) => existsSync3(path2)).map(([screen, path2]) => {
|
|
64650
|
-
const bytes2 = readFileSync5(path2);
|
|
64651
|
-
return { screen, bytes: bytes2, hash: sha2562(bytes2) };
|
|
64652
|
-
});
|
|
64653
|
-
const screenshots = Object.fromEntries(screenshotEntries.map(({ screen, hash }) => [screen, hash]));
|
|
64654
|
-
for (const { bytes: bytes2, hash } of screenshotEntries) {
|
|
64655
|
-
const artifact = screenshotArtifactPath(contentDir2, hash);
|
|
64656
|
-
mkdirSync5(join6(dir, "artifacts"), { recursive: true });
|
|
64657
|
-
if (!existsSync3(artifact)) writeFileSync5(artifact, bytes2);
|
|
64658
|
-
}
|
|
64659
|
-
const unsigned = {
|
|
64660
|
-
attestation: 2,
|
|
64661
|
-
runId,
|
|
64662
|
-
tool: input.tool,
|
|
64663
|
-
app: input.app,
|
|
64664
|
-
contentDir: storedContentDir(contentDir2),
|
|
64665
|
-
gitDiffHash: input.gitDiffHash,
|
|
64666
|
-
releaseId: input.releaseId,
|
|
64667
|
-
releaseHash: releasePayloadHash(input.releasePayload),
|
|
64668
|
-
gitUntrackedHash: currentGitUntrackedHash(contentDir2),
|
|
64669
|
-
descriptorHashes,
|
|
64670
|
-
screenshots,
|
|
64671
|
-
coverage: input.coverage,
|
|
64672
|
-
report: input.report,
|
|
64673
|
-
prev: previous?.selfHash ?? null
|
|
64674
|
-
};
|
|
64675
|
-
const attestation = { ...unsigned, selfHash: "" };
|
|
64676
|
-
attestation.selfHash = selfHashFor(attestation);
|
|
64677
|
-
const json5 = JSON.stringify(attestation, null, 2) + "\n";
|
|
64678
|
-
const runFile = `${runId.replace(/:/g, "-")}.json`;
|
|
64679
|
-
writeFileSync5(join6(dir, runFile), json5);
|
|
64680
|
-
writeFileSync5(join6(dir, "latest.json"), json5);
|
|
64681
|
-
const keep = Math.max(1, opts.maxRuns ?? DEFAULT_MAX_RUNS);
|
|
64682
|
-
const allPriorRuns = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").filter((name) => name !== runFile).sort().reverse();
|
|
64683
|
-
const priorRuns = allPriorRuns.filter((name) => {
|
|
64684
|
-
const kind = classifyRetainedRun(dir, name).kind;
|
|
64685
|
-
if (kind === "legacy") {
|
|
64686
|
-
unlinkSync(join6(dir, name));
|
|
64687
|
-
return false;
|
|
64688
|
-
}
|
|
64689
|
-
return kind !== "future";
|
|
64690
|
-
});
|
|
64691
|
-
for (const name of priorRuns.slice(Math.max(0, keep - 1))) unlinkSync(join6(dir, name));
|
|
64692
|
-
const retainedFiles = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
|
|
64693
|
-
const retainedRuns = retainedFiles.map((name) => {
|
|
64694
|
-
const classified = classifyRetainedRun(dir, name);
|
|
64695
|
-
return classified.kind === "current" ? classified.run : null;
|
|
64696
|
-
});
|
|
64697
|
-
if (retainedRuns.every((run2) => run2 !== null)) {
|
|
64698
|
-
const retainedScreenshotHashes = new Set(
|
|
64699
|
-
retainedRuns.flatMap((run2) => Object.values(run2.screenshots))
|
|
64700
|
-
);
|
|
64701
|
-
const artifactDir = join6(dir, "artifacts");
|
|
64702
|
-
if (existsSync3(artifactDir)) {
|
|
64703
|
-
for (const name of readdirSync3(artifactDir).filter((candidate) => candidate.endsWith(".png"))) {
|
|
64704
|
-
if (!retainedScreenshotHashes.has(name.slice(0, -4))) unlinkSync(join6(artifactDir, name));
|
|
64705
|
-
}
|
|
64706
|
-
}
|
|
64707
|
-
}
|
|
64708
|
-
return attestation;
|
|
64709
|
-
}
|
|
64710
|
-
function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, releasePayload, opts = {}) {
|
|
64711
|
-
const errors = [];
|
|
64712
|
-
if (selfHashFor(attestation) !== attestation.selfHash) errors.push("selfHash does not match the attestation bytes");
|
|
64713
|
-
try {
|
|
64714
|
-
const dir = join6(resolve6(contentDir2), ".himi", "attestations");
|
|
64715
|
-
const latest = readLatestAttestation(contentDir2);
|
|
64716
|
-
if (!latest || opts.requireLatest !== false && latest.selfHash !== attestation.selfHash) {
|
|
64717
|
-
errors.push("attestation does not match latest.json");
|
|
64718
|
-
} else {
|
|
64719
|
-
const classified = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").map((name) => classifyRetainedRun(dir, name)).filter((entry) => entry.kind !== "future");
|
|
64720
|
-
const retained = classified.map((entry) => entry.kind === "current" ? entry.run : null);
|
|
64721
|
-
if (retained.some((run2) => run2 === null)) {
|
|
64722
|
-
errors.push("attestation history contains a malformed retained run");
|
|
64723
|
-
} else {
|
|
64724
|
-
const runs = retained;
|
|
64725
|
-
const byHash = new Map(runs.map((run2) => [run2.selfHash, run2]));
|
|
64726
|
-
if (!byHash.has(attestation.selfHash)) errors.push("attestation has no matching retained run");
|
|
64727
|
-
if (!byHash.has(latest.selfHash)) {
|
|
64728
|
-
errors.push("latest.json has no matching retained run");
|
|
64729
|
-
} else {
|
|
64730
|
-
const seen = /* @__PURE__ */ new Set();
|
|
64731
|
-
let cursor = latest;
|
|
64732
|
-
while (cursor && !seen.has(cursor.selfHash)) {
|
|
64733
|
-
seen.add(cursor.selfHash);
|
|
64734
|
-
if (selfHashFor(cursor) !== cursor.selfHash) {
|
|
64735
|
-
errors.push(`selfHash does not match retained run ${cursor.runId}`);
|
|
64736
|
-
break;
|
|
64737
|
-
}
|
|
64738
|
-
cursor = cursor.prev ? byHash.get(cursor.prev) : void 0;
|
|
64739
|
-
}
|
|
64740
|
-
if (seen.size !== runs.length) errors.push("attestation hash chain does not reach every retained run");
|
|
64741
|
-
}
|
|
64742
|
-
}
|
|
64743
|
-
}
|
|
64744
|
-
} catch {
|
|
64745
|
-
errors.push("attestation history could not be verified");
|
|
64746
|
-
}
|
|
64747
|
-
if (attestation.app !== expectedApp) errors.push(`attestation app does not match ${expectedApp}`);
|
|
64748
|
-
if (attestation.contentDir !== storedContentDir(contentDir2)) errors.push("attestation contentDir does not match the requested content directory");
|
|
64749
|
-
if (releasePayloadHash(releasePayload) !== attestation.releaseHash) {
|
|
64750
|
-
errors.push("releaseHash does not match the current release payload");
|
|
64751
|
-
}
|
|
64752
|
-
try {
|
|
64753
|
-
const liveDiffHash = currentGitDiffHash(contentDir2);
|
|
64754
|
-
if (liveDiffHash !== "unavailable" && liveDiffHash !== attestation.gitDiffHash) {
|
|
64755
|
-
errors.push("gitDiffHash does not match the live git diff");
|
|
64756
|
-
}
|
|
64757
|
-
} catch {
|
|
64758
|
-
errors.push("gitDiffHash could not be recomputed from the live git diff");
|
|
64759
|
-
}
|
|
64760
|
-
try {
|
|
64761
|
-
const liveUntrackedHash = currentGitUntrackedHash(contentDir2);
|
|
64762
|
-
if (liveUntrackedHash !== "unavailable" && liveUntrackedHash !== attestation.gitUntrackedHash) {
|
|
64763
|
-
errors.push("gitUntrackedHash does not match the live untracked source bytes");
|
|
64764
|
-
}
|
|
64765
|
-
} catch {
|
|
64766
|
-
errors.push("gitUntrackedHash could not be recomputed from the live untracked source bytes");
|
|
64767
|
-
}
|
|
64768
|
-
for (const [screen, expected] of Object.entries(attestation.descriptorHashes)) {
|
|
64769
|
-
const bytes2 = descriptors[screen];
|
|
64770
|
-
if (bytes2 === void 0) {
|
|
64771
|
-
errors.push(`missing baked descriptor for ${screen}`);
|
|
64772
|
-
continue;
|
|
64773
|
-
}
|
|
64774
|
-
if (sha2562(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
|
|
64775
|
-
}
|
|
64776
|
-
for (const [screen, expected] of Object.entries(attestation.screenshots)) {
|
|
64777
|
-
const path2 = screenshotArtifactPath(contentDir2, expected);
|
|
64778
|
-
if (!existsSync3(path2)) {
|
|
64779
|
-
errors.push(`missing screenshot for ${screen}: ${basename2(path2)}`);
|
|
64780
|
-
continue;
|
|
64781
|
-
}
|
|
64782
|
-
if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
|
|
64783
|
-
}
|
|
64784
|
-
return { ok: errors.length === 0, errors };
|
|
64785
|
-
}
|
|
64887
|
+
init_attestation();
|
|
64786
64888
|
|
|
64787
64889
|
// ../../core/admin-web/src/utils/qr-terminal.ts
|
|
64788
64890
|
init_qr();
|
|
@@ -68001,7 +68103,7 @@ function nativeConfigTokenNotice(configSource, optedIn) {
|
|
|
68001
68103
|
}
|
|
68002
68104
|
async function nativeConfigAdvisories(app, configSource) {
|
|
68003
68105
|
try {
|
|
68004
|
-
const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (
|
|
68106
|
+
const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
|
|
68005
68107
|
const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
|
|
68006
68108
|
const iosYmlPath = iosProjectYml2(app);
|
|
68007
68109
|
const iosYml = existsSync63(iosYmlPath) ? readFileSync75(iosYmlPath, "utf8") : "";
|
|
@@ -68522,7 +68624,7 @@ function uxHandoverHint(uxEvidence, ctx) {
|
|
|
68522
68624
|
if (uxEvidence.state === "failed") {
|
|
68523
68625
|
return `a mobile-UX drive for ${releaseId} IS recorded, and it FAILED on ${uxEvidence.failedPlatforms.join(", ")} (see ${uxEvidence.path}). A published draft is the owner-private preview a customer is handed, so fix what the drive found and re-record it \u2014 re-running \`himi ux record\` over a failure without changing the app just writes the same failure down again. Only if you intend to hand a customer a preview whose UX you already know is broken, \`${door} --force\`.`;
|
|
68524
68626
|
}
|
|
68525
|
-
const why = uxEvidence.state === "absent" ? `nothing has recorded a mobile-UX drive for this package \u2014 ${uxEvidence.path} does not exist` : uxEvidence.state === "unrecorded" ? uxEvidence.malformed.length ? `${uxEvidence.path} has no iOS or Android drive this gate can read \u2014 ${uxEvidence.malformed.join("; ")}` : `${uxEvidence.path} exists but carries no iOS or Android drive (\`himi render\` writes its rendered widths there too, so the file alone proves nothing)` : uxEvidence.state === "unreadable" ? `${ctx.quote(`${ctx.contentDir}/${uxEvidence.path}`)} ${uxEvidence.error} \u2014 delete it and record again, because re-recording over a malformed store does not repair it` : uxEvidence.state === "outdated" && ctx.nondeterministicWorkers?.length ? `a mobile-UX drive for
|
|
68627
|
+
const why = uxEvidence.state === "absent" ? `nothing has recorded a mobile-UX drive for this package \u2014 ${uxEvidence.path} does not exist` : uxEvidence.state === "unrecorded" ? uxEvidence.malformed.length ? `${uxEvidence.path} has no iOS or Android drive this gate can read \u2014 ${uxEvidence.malformed.join("; ")}` : `${uxEvidence.path} exists but carries no iOS or Android drive (\`himi render\` writes its rendered widths there too, so the file alone proves nothing)` : uxEvidence.state === "unreadable" ? `${ctx.quote(`${ctx.contentDir}/${uxEvidence.path}`)} ${uxEvidence.error} \u2014 delete it and record again, because re-recording over a malformed store does not repair it` : uxEvidence.state === "outdated" && ctx.nondeterministicWorkers?.length ? `a mobile-UX drive for an earlier build of this commit is recorded, but it describes different content from the release this push just built (${releaseId}) \u2014 an uncommitted edit since the record would do that, and so does the worker named below` : uxEvidence.state === "outdated" ? `a mobile-UX drive for an earlier build of this commit is recorded, but the package has CHANGED since \u2014 editing a screen, a token or a worker without committing makes a new release, ${releaseId} (a record written before this check also lands here, carrying no content hash)` : `the recorded mobile-UX drive is about ${uxEvidence.otherReleases.length ? `release ${uxEvidence.otherReleases.join(", ")}` : "no release at all"}, not ${releaseId}${uxEvidence.unstamped ? ` (${uxEvidence.unstamped} drive(s) name no release)` : ""}`;
|
|
68526
68628
|
const drifting = ctx.nondeterministicWorkers ?? [];
|
|
68527
68629
|
if (drifting.length > 0) {
|
|
68528
68630
|
return `${why}. ${releaseDriftCause(drifting, "Re-recording", "a drive")}; then record the drive: ${uxRemedy}, and re-run \`${door}\`. Only if you intend to hand over a preview WITHOUT that evidence, \`${door} --force\`.`;
|
|
@@ -68757,7 +68859,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
68757
68859
|
};
|
|
68758
68860
|
const runReleasePatch = async (opts) => {
|
|
68759
68861
|
const { app, plan, draft } = opts;
|
|
68760
|
-
const { regressionDiff: regressionDiff2, patchHonesty: patchHonesty2 } = await Promise.resolve().then(() => (
|
|
68862
|
+
const { regressionDiff: regressionDiff2, patchHonesty: patchHonesty2 } = await Promise.resolve().then(() => (init_release_patch2(), release_patch_exports));
|
|
68761
68863
|
const target = targetName();
|
|
68762
68864
|
const baseFound = await fetchReleaseManifest(client2, app, opts.fromRelease);
|
|
68763
68865
|
if ("error" in baseFound) {
|
|
@@ -69166,12 +69268,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
69166
69268
|
try {
|
|
69167
69269
|
const bytes2 = readFileSync75(iconPath);
|
|
69168
69270
|
payload.icon = bytes2.toString("base64");
|
|
69169
|
-
iconHash =
|
|
69271
|
+
iconHash = createHash28("sha256").update(bytes2).digest("hex");
|
|
69170
69272
|
} catch {
|
|
69171
69273
|
}
|
|
69172
69274
|
}
|
|
69173
69275
|
payload.app = templateAppId;
|
|
69174
|
-
|
|
69276
|
+
const { rebindReleaseId: rebindReleaseId2 } = await Promise.resolve().then(() => (init_build3(), build_exports2));
|
|
69277
|
+
payload.manifest = rebindReleaseId2({ ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} });
|
|
69175
69278
|
const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
|
|
69176
69279
|
if (Object.keys(files).length === 0) {
|
|
69177
69280
|
io.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
|
|
@@ -70797,7 +70900,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70797
70900
|
const contentArg = shellQuote2(contentRel);
|
|
70798
70901
|
if (onlyRaw !== void 0) {
|
|
70799
70902
|
const onlyDraft = flags.draft === true;
|
|
70800
|
-
const { parseOnlySelectors: parseOnlySelectors2, planFromPayload: planFromPayload2, notCarriedDiff: notCarriedDiff2 } = await Promise.resolve().then(() => (
|
|
70903
|
+
const { parseOnlySelectors: parseOnlySelectors2, planFromPayload: planFromPayload2, notCarriedDiff: notCarriedDiff2 } = await Promise.resolve().then(() => (init_release_patch2(), release_patch_exports));
|
|
70801
70904
|
const parsed = parseOnlySelectors2(onlyRaw, {
|
|
70802
70905
|
screens: Object.keys(manifest.screens ?? {}),
|
|
70803
70906
|
bundles: Object.keys(manifest.bundles ?? {})
|
|
@@ -70855,7 +70958,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70855
70958
|
let localSourceHash;
|
|
70856
70959
|
try {
|
|
70857
70960
|
const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
|
|
70858
|
-
localSourceHash =
|
|
70961
|
+
localSourceHash = createHash28("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
|
|
70859
70962
|
} catch {
|
|
70860
70963
|
}
|
|
70861
70964
|
return runReleasePatch({
|
|
@@ -70930,7 +71033,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70930
71033
|
}
|
|
70931
71034
|
const evidence = pushEvidence(dir, manifest.releaseId, (candidate) => verifyAttestation(candidate, dir, payload.screens, declared, payload, { requireLatest: false }).ok);
|
|
70932
71035
|
const drifting = nondeterministicWorkers(outcome.workers);
|
|
70933
|
-
const staleEvidence = evidence.staleRuns > 0 ? `${evidence.staleRuns} retained attestation(s)
|
|
71036
|
+
const staleEvidence = evidence.staleRuns > 0 ? `${evidence.staleRuns} retained attestation(s) were recorded at this commit, but none describes the content being published` : null;
|
|
70934
71037
|
const attestationErrors = [...evidence.historyErrors, ...staleEvidence ? [staleEvidence] : []];
|
|
70935
71038
|
if (attestationErrors.length > 0 && !force) {
|
|
70936
71039
|
const remedy = `\`himi test --content ${contentArg}\` (or \`himi render <screenId> --content ${contentArg}\`)`;
|
|
@@ -70939,7 +71042,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
70939
71042
|
ok: false,
|
|
70940
71043
|
pushed: false,
|
|
70941
71044
|
errors: attestationErrors,
|
|
70942
|
-
hint: corrupted ? `attestation history in .himi/attestations/ is corrupt, so the retained runs cannot be trusted \u2014 a fresh run does not remove the bad entry. Delete .himi/attestations/, re-attest with ${remedy}, then re-run \`himi push\`. Only if you intend to publish WITHOUT that evidence, \`himi push --force\`.` : `no retained attestation for this release passed live verification${drifting.length ? `. ${releaseDriftCause(drifting, "Re-attesting", "an attestation")}, then re-attest` : " \u2014
|
|
71045
|
+
hint: corrupted ? `attestation history in .himi/attestations/ is corrupt, so the retained runs cannot be trusted \u2014 a fresh run does not remove the bad entry. Delete .himi/attestations/, re-attest with ${remedy}, then re-run \`himi push\`. Only if you intend to publish WITHOUT that evidence, \`himi push --force\`.` : `no retained attestation for this release passed live verification${drifting.length ? `. ${releaseDriftCause(drifting, "Re-attesting", "an attestation")}, then re-attest` : " \u2014 runs recorded at this commit before your latest edits describe a tree that no longer exists. Re-attest"} the CURRENT release with ${remedy}, then re-run \`himi push\`. Only if you intend to publish WITHOUT that evidence, \`himi push --force\`.`
|
|
70943
71046
|
});
|
|
70944
71047
|
return 1;
|
|
70945
71048
|
}
|
|
@@ -72404,7 +72507,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
72404
72507
|
});
|
|
72405
72508
|
}
|
|
72406
72509
|
const bytes2 = readFileSync75(file);
|
|
72407
|
-
const sha =
|
|
72510
|
+
const sha = createHash28("sha256").update(bytes2).digest("hex");
|
|
72408
72511
|
const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
72409
72512
|
if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
|
|
72410
72513
|
io.error(JSON.stringify({ ok: false, error: "no_rebind_takes_no_value", message: "--no-rebind is a switch and takes no value; pass it bare.", usage: PATCH_USAGE }));
|
|
@@ -72740,7 +72843,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
72740
72843
|
const { preflightApp: preflightApp2 } = await Promise.resolve().then(() => (init_preflight_app(), preflight_app_exports));
|
|
72741
72844
|
const { resolveStoreCredentials: resolveStoreCredentials2 } = await Promise.resolve().then(() => (init_credential_profile(), credential_profile_exports));
|
|
72742
72845
|
const { readIosVersion: readIosVersion2, bumpVersion: bumpVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
72743
|
-
const { iosProjectYml: iosYmlPath, hasRepoCheckout: hasRepoCheckout2 } = await Promise.resolve().then(() => (
|
|
72846
|
+
const { iosProjectYml: iosYmlPath, hasRepoCheckout: hasRepoCheckout2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
|
|
72744
72847
|
const { ascStatus: ascStatus2 } = await Promise.resolve().then(() => (init_asc_status(), asc_status_exports));
|
|
72745
72848
|
const { readFileSync: rf, readdirSync: rd, existsSync: ex, statSync: st } = await import("node:fs");
|
|
72746
72849
|
const { execFile: execFile2 } = await import("node:child_process");
|