@wix/himalaya-cli 0.880.0 → 0.881.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.
@@ -39,8 +39,8 @@ export interface WixGetPostRequest {
39
39
  * request type has no `fieldsets` field, so that fallback path cannot carry the
40
40
  * body.
41
41
  *
42
- * When that fallback ALSO finds nothing (#2658) — a deleted post, or one this
43
- * visitor may not see — we reject rather than resolve. `WixGetPostResponse.post`
42
+ * When that fallback ALSO finds nothing — a deleted post, or one this visitor
43
+ * may not see — we reject rather than resolve. `WixGetPostResponse.post`
44
44
  * is non-optional, so returning `{ post: resp.posts?.[0] }` would hand callers an
45
45
  * `undefined` the type promises is present, and the `post.title` access that type
46
46
  * licenses throws a `TypeError` deep inside the caller — the exact hard error
package/dist/cli.mjs CHANGED
@@ -22773,7 +22773,7 @@ var init_app_module = __esm({
22773
22773
 
22774
22774
  // ../../core/dev-server/src/app-assets.ts
22775
22775
  import { createHash as createHash8 } from "node:crypto";
22776
- import { existsSync as existsSync27, lstatSync as lstatSync3, readFileSync as readFileSync29, readdirSync as readdirSync14, realpathSync as realpathSync3, statSync as statSync5 } from "node:fs";
22776
+ import { existsSync as existsSync27, lstatSync as lstatSync4, readFileSync as readFileSync29, readdirSync as readdirSync14, realpathSync as realpathSync3, statSync as statSync5 } from "node:fs";
22777
22777
  import { join as join31, resolve as resolve19, sep as sep5 } from "node:path";
22778
22778
  function contentTypeFor(name) {
22779
22779
  const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
@@ -22851,7 +22851,7 @@ function collectBrandFonts(appDir2) {
22851
22851
  if (rel.startsWith("/") || rel.split("/").includes("..")) continue;
22852
22852
  const path = join31(appDir2, rel);
22853
22853
  try {
22854
- if (!lstatSync3(path).isFile()) continue;
22854
+ if (!lstatSync4(path).isFile()) continue;
22855
22855
  const root = realpathSync3(appDir2);
22856
22856
  if (!realpathSync3(path).startsWith(root + sep5)) continue;
22857
22857
  } catch {
@@ -46910,13 +46910,13 @@ __export(app_source_exports, {
46910
46910
  writeConflicts: () => writeConflicts,
46911
46911
  zipAppSource: () => zipAppSource
46912
46912
  });
46913
- import { readdirSync as readdirSync21, lstatSync as lstatSync4, readFileSync as readFileSync44, mkdirSync as mkdirSync18, writeFileSync as writeFileSync14, existsSync as existsSync38, rmSync as rmSync6, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
46913
+ import { readdirSync as readdirSync21, lstatSync as lstatSync5, readFileSync as readFileSync44, mkdirSync as mkdirSync18, writeFileSync as writeFileSync14, existsSync as existsSync38, rmSync as rmSync6, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
46914
46914
  import { join as join37, resolve as resolve34, sep as sep7 } from "node:path";
46915
46915
  function walkSourceTree(dir, file, skip) {
46916
46916
  const walk2 = (relDir) => {
46917
46917
  for (const name of readdirSync21(join37(dir, relDir))) {
46918
46918
  const rel = relDir ? join37(relDir, name) : name;
46919
- const st = lstatSync4(join37(dir, rel));
46919
+ const st = lstatSync5(join37(dir, rel));
46920
46920
  if (st.isSymbolicLink()) {
46921
46921
  skip(rel);
46922
46922
  continue;
@@ -46964,7 +46964,7 @@ function readAppSource(zip) {
46964
46964
  }
46965
46965
  function pathKind(p) {
46966
46966
  try {
46967
- return lstatSync4(p);
46967
+ return lstatSync5(p);
46968
46968
  } catch {
46969
46969
  return null;
46970
46970
  }
@@ -47061,7 +47061,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
47061
47061
  for (const name of readdirSync21(rel ? join37(root, rel) : root)) {
47062
47062
  const childRel = rel ? join37(rel, name) : name;
47063
47063
  const posix3 = childRel.split(sep7).join("/");
47064
- const kind = lstatSync4(join37(root, childRel));
47064
+ const kind = lstatSync5(join37(root, childRel));
47065
47065
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name)) {
47066
47066
  empty = false;
47067
47067
  continue;
@@ -51239,8 +51239,9 @@ ${releaseId}`;
51239
51239
  if (packageShare) {
51240
51240
  const shareId = packageShare[1];
51241
51241
  const wantsMetadata = url.searchParams.get("metadata") === "1";
51242
+ const wantsDownload = url.searchParams.get("download") === "1";
51242
51243
  const wantsHtml = (request.headers.get("accept") ?? "").includes("text/html");
51243
- if (wantsMetadata || wantsHtml) {
51244
+ if (wantsMetadata || wantsHtml && !wantsDownload) {
51244
51245
  const share = await resolvePackageShareMetadata(await getStore(), shareId);
51245
51246
  if (!share) return json3(404, { error: "package_share_not_found" });
51246
51247
  if (wantsMetadata) return json3(200, share, { "cache-control": "no-store" });
@@ -59411,7 +59412,7 @@ var init_site_oauth_client = __esm({
59411
59412
  });
59412
59413
 
59413
59414
  // src/cli.ts
59414
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync64, mkdirSync as mkdirSync25, writeFileSync as writeFileSync23, existsSync as existsSync53, rmSync as rmSync9, readdirSync as readdirSync24, statSync as statSync12, lstatSync as lstatSync5 } from "node:fs";
59415
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync64, mkdirSync as mkdirSync25, writeFileSync as writeFileSync23, existsSync as existsSync53, rmSync as rmSync9, readdirSync as readdirSync24, statSync as statSync12, lstatSync as lstatSync6 } from "node:fs";
59415
59416
  import { execFileSync as execFileSync7 } from "node:child_process";
59416
59417
  import { createHash as createHash24 } from "node:crypto";
59417
59418
  import { tmpdir as tmpdir5 } from "node:os";
@@ -59872,6 +59873,7 @@ import { execFileSync as execFileSync2 } from "node:child_process";
59872
59873
  import { createHash as createHash3, randomBytes } from "node:crypto";
59873
59874
  import {
59874
59875
  existsSync as existsSync3,
59876
+ lstatSync as lstatSync3,
59875
59877
  mkdirSync as mkdirSync5,
59876
59878
  realpathSync,
59877
59879
  readlinkSync as readlinkSync2,
@@ -60230,6 +60232,24 @@ if (process.argv[1] && basename(process.argv[1]) === "diff-hash.mjs" && import.m
60230
60232
 
60231
60233
  // src/attestation.ts
60232
60234
  var DEFAULT_MAX_RUNS = 20;
60235
+ var ATTESTATION_VERSION = 2;
60236
+ function attestationVersionOf(value) {
60237
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
60238
+ const version = value.attestation;
60239
+ return typeof version === "number" && Number.isInteger(version) && version > 0 ? version : null;
60240
+ }
60241
+ function classifyRetainedRun(dir, name) {
60242
+ let value;
60243
+ try {
60244
+ value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
60245
+ } catch {
60246
+ return { kind: "unreadable" };
60247
+ }
60248
+ if (isAttestationV2(value)) return { kind: "current", run: value };
60249
+ const version = attestationVersionOf(value);
60250
+ if (version === null || version === ATTESTATION_VERSION) return { kind: "malformed" };
60251
+ return { kind: version < ATTESTATION_VERSION ? "legacy" : "future" };
60252
+ }
60233
60253
  function screenshotArtifactPath(contentDir2, hash) {
60234
60254
  return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
60235
60255
  }
@@ -60297,7 +60317,15 @@ function currentGitUntrackedHash(contentDir2) {
60297
60317
  for (const path of listed.split("\0").filter(Boolean).sort()) {
60298
60318
  const local = relative(absolute, join6(root, path));
60299
60319
  if (local.split(/[\\/]/)[0] === ".himi") continue;
60300
- hash.update(path).update("\0").update(readFileSync5(join6(root, path))).update("\0");
60320
+ const full = join6(root, path);
60321
+ const stats = lstatSync3(full, { throwIfNoEntry: false });
60322
+ if (!stats) continue;
60323
+ const kind = stats.isSymbolicLink() ? "link" : stats.isDirectory() ? "dir" : stats.isFile() ? "file" : null;
60324
+ if (!kind) continue;
60325
+ const payload = kind === "link" ? Buffer.from(readlinkSync2(full)) : kind === "file" ? readFileSync5(full) : Buffer.alloc(0);
60326
+ const pathBytes = Buffer.from(path);
60327
+ hash.update(`${kind} ${pathBytes.length} ${payload.length}
60328
+ `).update(pathBytes).update(payload);
60301
60329
  }
60302
60330
  return hash.digest("hex");
60303
60331
  }
@@ -60317,17 +60345,17 @@ function sourceTreeHash(root) {
60317
60345
  return hash.digest("hex");
60318
60346
  }
60319
60347
  var stringRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
60320
- function isAttestationV1(value) {
60348
+ function isAttestationV2(value) {
60321
60349
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
60322
60350
  const a = value;
60323
60351
  const coverage = a.coverage;
60324
60352
  const report = a.report;
60325
- return a.attestation === 1 && 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 report?.ok === "boolean" && !!report.counts && [report.counts.errors, report.counts.warnings, report.counts.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && (a.prev === null || typeof a.prev === "string");
60353
+ 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 report?.ok === "boolean" && !!report.counts && [report.counts.errors, report.counts.warnings, report.counts.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && (a.prev === null || typeof a.prev === "string");
60326
60354
  }
60327
60355
  function readLatestAttestation(contentDir2) {
60328
60356
  try {
60329
60357
  const parsed = JSON.parse(readFileSync5(join6(resolve6(contentDir2), ".himi", "attestations", "latest.json"), "utf8"));
60330
- return isAttestationV1(parsed) ? parsed : null;
60358
+ return isAttestationV2(parsed) ? parsed : null;
60331
60359
  } catch {
60332
60360
  return null;
60333
60361
  }
@@ -60340,13 +60368,15 @@ function readRetainedAttestations(contentDir2) {
60340
60368
  const parsedRuns = [];
60341
60369
  const errors = [];
60342
60370
  for (const name of names) {
60343
- try {
60344
- const value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
60345
- if (isAttestationV1(value)) parsedRuns.push(value);
60346
- else errors.push(`attestation history contains malformed retained run ${name}`);
60347
- } catch {
60371
+ const classified = classifyRetainedRun(dir, name);
60372
+ if (classified.kind === "current") parsedRuns.push(classified.run);
60373
+ else if (classified.kind === "legacy") {
60374
+ 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`);
60375
+ } else if (classified.kind === "future") {
60376
+ errors.push(`attestation history contains retained run ${name} from a newer attestation version \u2014 upgrade himi rather than deleting it`);
60377
+ } else if (classified.kind === "unreadable") {
60348
60378
  errors.push(`attestation history contains unreadable retained run ${name}`);
60349
- }
60379
+ } else errors.push(`attestation history contains malformed retained run ${name}`);
60350
60380
  }
60351
60381
  const latest = readLatestAttestation(contentDir2);
60352
60382
  if (existsSync3(join6(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
@@ -60430,7 +60460,7 @@ function writeAttestation(input, opts = {}) {
60430
60460
  if (!existsSync3(artifact)) writeFileSync5(artifact, bytes2);
60431
60461
  }
60432
60462
  const unsigned = {
60433
- attestation: 1,
60463
+ attestation: 2,
60434
60464
  runId,
60435
60465
  tool: input.tool,
60436
60466
  app: input.app,
@@ -60452,16 +60482,20 @@ function writeAttestation(input, opts = {}) {
60452
60482
  writeFileSync5(join6(dir, runFile), json5);
60453
60483
  writeFileSync5(join6(dir, "latest.json"), json5);
60454
60484
  const keep = Math.max(1, opts.maxRuns ?? DEFAULT_MAX_RUNS);
60455
- const priorRuns = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").filter((name) => name !== runFile).sort().reverse();
60485
+ const allPriorRuns = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").filter((name) => name !== runFile).sort().reverse();
60486
+ const priorRuns = allPriorRuns.filter((name) => {
60487
+ const kind = classifyRetainedRun(dir, name).kind;
60488
+ if (kind === "legacy") {
60489
+ unlinkSync(join6(dir, name));
60490
+ return false;
60491
+ }
60492
+ return kind !== "future";
60493
+ });
60456
60494
  for (const name of priorRuns.slice(Math.max(0, keep - 1))) unlinkSync(join6(dir, name));
60457
60495
  const retainedFiles = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
60458
60496
  const retainedRuns = retainedFiles.map((name) => {
60459
- try {
60460
- const value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
60461
- return isAttestationV1(value) ? value : null;
60462
- } catch {
60463
- return null;
60464
- }
60497
+ const classified = classifyRetainedRun(dir, name);
60498
+ return classified.kind === "current" ? classified.run : null;
60465
60499
  });
60466
60500
  if (retainedRuns.every((run2) => run2 !== null)) {
60467
60501
  const retainedScreenshotHashes = new Set(
@@ -60485,14 +60519,8 @@ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, r
60485
60519
  if (!latest || opts.requireLatest !== false && latest.selfHash !== attestation.selfHash) {
60486
60520
  errors.push("attestation does not match latest.json");
60487
60521
  } else {
60488
- const retained = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").map((name) => {
60489
- try {
60490
- const value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
60491
- return isAttestationV1(value) ? value : null;
60492
- } catch {
60493
- return null;
60494
- }
60495
- });
60522
+ const classified = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").map((name) => classifyRetainedRun(dir, name)).filter((entry) => entry.kind !== "future");
60523
+ const retained = classified.map((entry) => entry.kind === "current" ? entry.run : null);
60496
60524
  if (retained.some((run2) => run2 === null)) {
60497
60525
  errors.push("attestation history contains a malformed retained run");
60498
60526
  } else {
@@ -63966,7 +63994,7 @@ function renameRestoredSource(restored, declaredApp, renamed) {
63966
63994
  function symlinkDestinationIssue(dest) {
63967
63995
  let kind;
63968
63996
  try {
63969
- kind = lstatSync5(dest);
63997
+ kind = lstatSync6(dest);
63970
63998
  } catch {
63971
63999
  return null;
63972
64000
  }
@@ -6590,7 +6590,7 @@
6590
6590
  {
6591
6591
  "name": "getPost",
6592
6592
  "kind": "function",
6593
- "jsdoc": "/** Fetch a single PUBLISHED post by id — GET /blog/v3/posts/{id}. The list\n * (queryPosts) returns published posts, so the detail reads them here (draft\n * ids are a different space — getDraftPost is for the edit/create flow).\n *\n * Body fieldsets (#826): Blog v3 `GET /posts/{id}` returns 200 but only the\n * LIST projection (title / excerpt / meta) unless the caller asks for the body —\n * the bare GET has NO `richContent` / `contentText`. So we request\n * RICH_CONTENT + CONTENT_TEXT by default and the article body loads on the happy\n * path; callers can override via `req.fieldsets`. (Supersedes the old #507 note:\n * the 502 this GET once returned for anonymous visitors is gone — it 200s now —\n * but a body-less 200 was never the \"body preserved whenever the backend is\n * healthy\" the earlier comment claimed; the body was simply never requested.)\n *\n * On any rejection (network / backend error) we still fall back to a queryPosts\n * filter-by-id — a graceful degrade to the LIST projection (no body) that keeps\n * blog detail + saved screens rendering instead of erroring out. `queryPosts`'s\n * request type has no `fieldsets` field, so that fallback path cannot carry the\n * body.\n *\n * When that fallback ALSO finds nothing (#2658) — a deleted post, or one this\n * visitor may not see — we reject rather than resolve. `WixGetPostResponse.post`\n * is non-optional, so returning `{ post: resp.posts?.[0] }` would hand callers an\n * `undefined` the type promises is present, and the `post.title` access that type\n * licenses throws a `TypeError` deep inside the caller — the exact hard error\n * this fallback exists to prevent, and harder to diagnose than a clean rejection.\n * Every call site rides its own rejection path (cached-post state, draft-only\n * `catch`, or an `allSettled` filter), so rejecting is what routes them into the\n * graceful degrade they already implement. The originating failure travels as\n * `cause`, so the 404-vs-network distinction is not lost. */",
6593
+ "jsdoc": "/** Fetch a single PUBLISHED post by id — GET /blog/v3/posts/{id}. The list\n * (queryPosts) returns published posts, so the detail reads them here (draft\n * ids are a different space — getDraftPost is for the edit/create flow).\n *\n * Body fieldsets (#826): Blog v3 `GET /posts/{id}` returns 200 but only the\n * LIST projection (title / excerpt / meta) unless the caller asks for the body —\n * the bare GET has NO `richContent` / `contentText`. So we request\n * RICH_CONTENT + CONTENT_TEXT by default and the article body loads on the happy\n * path; callers can override via `req.fieldsets`. (Supersedes the old #507 note:\n * the 502 this GET once returned for anonymous visitors is gone — it 200s now —\n * but a body-less 200 was never the \"body preserved whenever the backend is\n * healthy\" the earlier comment claimed; the body was simply never requested.)\n *\n * On any rejection (network / backend error) we still fall back to a queryPosts\n * filter-by-id — a graceful degrade to the LIST projection (no body) that keeps\n * blog detail + saved screens rendering instead of erroring out. `queryPosts`'s\n * request type has no `fieldsets` field, so that fallback path cannot carry the\n * body.\n *\n * When that fallback ALSO finds nothing — a deleted post, or one this visitor\n * may not see — we reject rather than resolve. `WixGetPostResponse.post`\n * is non-optional, so returning `{ post: resp.posts?.[0] }` would hand callers an\n * `undefined` the type promises is present, and the `post.title` access that type\n * licenses throws a `TypeError` deep inside the caller — the exact hard error\n * this fallback exists to prevent, and harder to diagnose than a clean rejection.\n * Every call site rides its own rejection path (cached-post state, draft-only\n * `catch`, or an `allSettled` filter), so rejecting is what routes them into the\n * graceful degrade they already implement. The originating failure travels as\n * `cause`, so the 404-vs-network distinction is not lost. */",
6594
6594
  "decl": "export function getPost(\n postId: string,\n req: WixGetPostRequest = {},\n): Promise<WixGetPostResponse>;",
6595
6595
  "http": "GET /blog/v3/posts/{…}"
6596
6596
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.880.0",
3
+ "version": "0.881.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -63,5 +63,5 @@
63
63
  "artifactId": "himalaya-cli"
64
64
  }
65
65
  },
66
- "falconPackageHash": "ef087e28bd7651f75f109ba701d8602c5b59ee6fe462bb2616c6802a"
66
+ "falconPackageHash": "c4de152d9d84b5fc2a1b6adaa22daab7e0b5e8f4fff6ccc83c1e72a3"
67
67
  }