@wix/himalaya-cli 0.879.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
@@ -5241,6 +5241,7 @@ function resolveTranslation(value, catalog, sourceCatalog) {
5241
5241
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
5242
5242
  var init_bindings = __esm({
5243
5243
  "../../core/ts/src/bindings.ts"() {
5244
+ "use strict";
5244
5245
  BINDING_SIGILS = ["service", "flag", "item", "t"];
5245
5246
  BINDING_SIGIL_RE = new RegExp(
5246
5247
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -22772,7 +22773,7 @@ var init_app_module = __esm({
22772
22773
 
22773
22774
  // ../../core/dev-server/src/app-assets.ts
22774
22775
  import { createHash as createHash8 } from "node:crypto";
22775
- 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";
22776
22777
  import { join as join31, resolve as resolve19, sep as sep5 } from "node:path";
22777
22778
  function contentTypeFor(name) {
22778
22779
  const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
@@ -22850,7 +22851,7 @@ function collectBrandFonts(appDir2) {
22850
22851
  if (rel.startsWith("/") || rel.split("/").includes("..")) continue;
22851
22852
  const path = join31(appDir2, rel);
22852
22853
  try {
22853
- if (!lstatSync3(path).isFile()) continue;
22854
+ if (!lstatSync4(path).isFile()) continue;
22854
22855
  const root = realpathSync3(appDir2);
22855
22856
  if (!realpathSync3(path).startsWith(root + sep5)) continue;
22856
22857
  } catch {
@@ -46909,13 +46910,13 @@ __export(app_source_exports, {
46909
46910
  writeConflicts: () => writeConflicts,
46910
46911
  zipAppSource: () => zipAppSource
46911
46912
  });
46912
- 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";
46913
46914
  import { join as join37, resolve as resolve34, sep as sep7 } from "node:path";
46914
46915
  function walkSourceTree(dir, file, skip) {
46915
46916
  const walk2 = (relDir) => {
46916
46917
  for (const name of readdirSync21(join37(dir, relDir))) {
46917
46918
  const rel = relDir ? join37(relDir, name) : name;
46918
- const st = lstatSync4(join37(dir, rel));
46919
+ const st = lstatSync5(join37(dir, rel));
46919
46920
  if (st.isSymbolicLink()) {
46920
46921
  skip(rel);
46921
46922
  continue;
@@ -46963,7 +46964,7 @@ function readAppSource(zip) {
46963
46964
  }
46964
46965
  function pathKind(p) {
46965
46966
  try {
46966
- return lstatSync4(p);
46967
+ return lstatSync5(p);
46967
46968
  } catch {
46968
46969
  return null;
46969
46970
  }
@@ -47060,7 +47061,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
47060
47061
  for (const name of readdirSync21(rel ? join37(root, rel) : root)) {
47061
47062
  const childRel = rel ? join37(rel, name) : name;
47062
47063
  const posix3 = childRel.split(sep7).join("/");
47063
- const kind = lstatSync4(join37(root, childRel));
47064
+ const kind = lstatSync5(join37(root, childRel));
47064
47065
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name)) {
47065
47066
  empty = false;
47066
47067
  continue;
@@ -51238,8 +51239,9 @@ ${releaseId}`;
51238
51239
  if (packageShare) {
51239
51240
  const shareId = packageShare[1];
51240
51241
  const wantsMetadata = url.searchParams.get("metadata") === "1";
51242
+ const wantsDownload = url.searchParams.get("download") === "1";
51241
51243
  const wantsHtml = (request.headers.get("accept") ?? "").includes("text/html");
51242
- if (wantsMetadata || wantsHtml) {
51244
+ if (wantsMetadata || wantsHtml && !wantsDownload) {
51243
51245
  const share = await resolvePackageShareMetadata(await getStore(), shareId);
51244
51246
  if (!share) return json3(404, { error: "package_share_not_found" });
51245
51247
  if (wantsMetadata) return json3(200, share, { "cache-control": "no-store" });
@@ -59410,7 +59412,7 @@ var init_site_oauth_client = __esm({
59410
59412
  });
59411
59413
 
59412
59414
  // src/cli.ts
59413
- 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";
59414
59416
  import { execFileSync as execFileSync7 } from "node:child_process";
59415
59417
  import { createHash as createHash24 } from "node:crypto";
59416
59418
  import { tmpdir as tmpdir5 } from "node:os";
@@ -59871,6 +59873,7 @@ import { execFileSync as execFileSync2 } from "node:child_process";
59871
59873
  import { createHash as createHash3, randomBytes } from "node:crypto";
59872
59874
  import {
59873
59875
  existsSync as existsSync3,
59876
+ lstatSync as lstatSync3,
59874
59877
  mkdirSync as mkdirSync5,
59875
59878
  realpathSync,
59876
59879
  readlinkSync as readlinkSync2,
@@ -60229,6 +60232,24 @@ if (process.argv[1] && basename(process.argv[1]) === "diff-hash.mjs" && import.m
60229
60232
 
60230
60233
  // src/attestation.ts
60231
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
+ }
60232
60253
  function screenshotArtifactPath(contentDir2, hash) {
60233
60254
  return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
60234
60255
  }
@@ -60296,7 +60317,15 @@ function currentGitUntrackedHash(contentDir2) {
60296
60317
  for (const path of listed.split("\0").filter(Boolean).sort()) {
60297
60318
  const local = relative(absolute, join6(root, path));
60298
60319
  if (local.split(/[\\/]/)[0] === ".himi") continue;
60299
- 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);
60300
60329
  }
60301
60330
  return hash.digest("hex");
60302
60331
  }
@@ -60316,17 +60345,17 @@ function sourceTreeHash(root) {
60316
60345
  return hash.digest("hex");
60317
60346
  }
60318
60347
  var stringRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
60319
- function isAttestationV1(value) {
60348
+ function isAttestationV2(value) {
60320
60349
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
60321
60350
  const a = value;
60322
60351
  const coverage = a.coverage;
60323
60352
  const report = a.report;
60324
- 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");
60325
60354
  }
60326
60355
  function readLatestAttestation(contentDir2) {
60327
60356
  try {
60328
60357
  const parsed = JSON.parse(readFileSync5(join6(resolve6(contentDir2), ".himi", "attestations", "latest.json"), "utf8"));
60329
- return isAttestationV1(parsed) ? parsed : null;
60358
+ return isAttestationV2(parsed) ? parsed : null;
60330
60359
  } catch {
60331
60360
  return null;
60332
60361
  }
@@ -60339,13 +60368,15 @@ function readRetainedAttestations(contentDir2) {
60339
60368
  const parsedRuns = [];
60340
60369
  const errors = [];
60341
60370
  for (const name of names) {
60342
- try {
60343
- const value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
60344
- if (isAttestationV1(value)) parsedRuns.push(value);
60345
- else errors.push(`attestation history contains malformed retained run ${name}`);
60346
- } 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") {
60347
60378
  errors.push(`attestation history contains unreadable retained run ${name}`);
60348
- }
60379
+ } else errors.push(`attestation history contains malformed retained run ${name}`);
60349
60380
  }
60350
60381
  const latest = readLatestAttestation(contentDir2);
60351
60382
  if (existsSync3(join6(dir, "latest.json")) && !latest) errors.push("attestation history has no valid latest.json");
@@ -60429,7 +60460,7 @@ function writeAttestation(input, opts = {}) {
60429
60460
  if (!existsSync3(artifact)) writeFileSync5(artifact, bytes2);
60430
60461
  }
60431
60462
  const unsigned = {
60432
- attestation: 1,
60463
+ attestation: 2,
60433
60464
  runId,
60434
60465
  tool: input.tool,
60435
60466
  app: input.app,
@@ -60451,16 +60482,20 @@ function writeAttestation(input, opts = {}) {
60451
60482
  writeFileSync5(join6(dir, runFile), json5);
60452
60483
  writeFileSync5(join6(dir, "latest.json"), json5);
60453
60484
  const keep = Math.max(1, opts.maxRuns ?? DEFAULT_MAX_RUNS);
60454
- 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
+ });
60455
60494
  for (const name of priorRuns.slice(Math.max(0, keep - 1))) unlinkSync(join6(dir, name));
60456
60495
  const retainedFiles = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json");
60457
60496
  const retainedRuns = retainedFiles.map((name) => {
60458
- try {
60459
- const value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
60460
- return isAttestationV1(value) ? value : null;
60461
- } catch {
60462
- return null;
60463
- }
60497
+ const classified = classifyRetainedRun(dir, name);
60498
+ return classified.kind === "current" ? classified.run : null;
60464
60499
  });
60465
60500
  if (retainedRuns.every((run2) => run2 !== null)) {
60466
60501
  const retainedScreenshotHashes = new Set(
@@ -60484,14 +60519,8 @@ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, r
60484
60519
  if (!latest || opts.requireLatest !== false && latest.selfHash !== attestation.selfHash) {
60485
60520
  errors.push("attestation does not match latest.json");
60486
60521
  } else {
60487
- const retained = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").map((name) => {
60488
- try {
60489
- const value = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
60490
- return isAttestationV1(value) ? value : null;
60491
- } catch {
60492
- return null;
60493
- }
60494
- });
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);
60495
60524
  if (retained.some((run2) => run2 === null)) {
60496
60525
  errors.push("attestation history contains a malformed retained run");
60497
60526
  } else {
@@ -63965,7 +63994,7 @@ function renameRestoredSource(restored, declaredApp, renamed) {
63965
63994
  function symlinkDestinationIssue(dest) {
63966
63995
  let kind;
63967
63996
  try {
63968
- kind = lstatSync5(dest);
63997
+ kind = lstatSync6(dest);
63969
63998
  } catch {
63970
63999
  return null;
63971
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.879.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": "cadb5d525c1bd00dfb5a1b90aeae24a17d6b94eb560176b0cb962516"
66
+ "falconPackageHash": "c4de152d9d84b5fc2a1b6adaa22daab7e0b5e8f4fff6ccc83c1e72a3"
67
67
  }