@wix/himalaya-cli 0.1246.0 → 0.1248.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
CHANGED
|
@@ -13465,7 +13465,6 @@ function resolveIdiom(tokens2, ctx = {}) {
|
|
|
13465
13465
|
var IDIOM_CONTEXTS, UNSAFE_KEYS;
|
|
13466
13466
|
var init_idiom = __esm({
|
|
13467
13467
|
"../../core/ts/src/idiom.ts"() {
|
|
13468
|
-
"use strict";
|
|
13469
13468
|
IDIOM_CONTEXTS = {
|
|
13470
13469
|
ios: { native: ["ios"] },
|
|
13471
13470
|
android: { native: ["material"] },
|
|
@@ -25749,6 +25748,7 @@ function shouldRevealError(args) {
|
|
|
25749
25748
|
var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
|
|
25750
25749
|
var init_validation = __esm({
|
|
25751
25750
|
"../../core/ts/src/validation.ts"() {
|
|
25751
|
+
"use strict";
|
|
25752
25752
|
EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
|
|
25753
25753
|
URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
|
|
25754
25754
|
INTEGER_RE = /^[+-]?[0-9]+$/;
|
|
@@ -26408,6 +26408,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
|
|
|
26408
26408
|
var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
|
|
26409
26409
|
var init_worker_subtree = __esm({
|
|
26410
26410
|
"../../core/ts/src/worker-subtree.ts"() {
|
|
26411
|
+
"use strict";
|
|
26411
26412
|
init_worker_subtree_safe_types_generated();
|
|
26412
26413
|
WORKER_SUBTREE_CEILINGS = Object.freeze({
|
|
26413
26414
|
maxEncodedBytes: 65536,
|
|
@@ -132926,6 +132927,72 @@ var init_studio = __esm({
|
|
|
132926
132927
|
}
|
|
132927
132928
|
});
|
|
132928
132929
|
|
|
132930
|
+
// src/push-errors.ts
|
|
132931
|
+
var push_errors_exports = {};
|
|
132932
|
+
__export(push_errors_exports, {
|
|
132933
|
+
PUSH_FAILURE_CODES: () => PUSH_FAILURE_CODES,
|
|
132934
|
+
pushFailure: () => pushFailure,
|
|
132935
|
+
uxEvidenceCode: () => uxEvidenceCode
|
|
132936
|
+
});
|
|
132937
|
+
function pushFailure(code, fields) {
|
|
132938
|
+
return { ...fields, ok: false, code };
|
|
132939
|
+
}
|
|
132940
|
+
function uxEvidenceCode(state) {
|
|
132941
|
+
return UX_CODES[state];
|
|
132942
|
+
}
|
|
132943
|
+
var PUSH_FAILURE_CODES, UX_CODES;
|
|
132944
|
+
var init_push_errors = __esm({
|
|
132945
|
+
"src/push-errors.ts"() {
|
|
132946
|
+
PUSH_FAILURE_CODES = [
|
|
132947
|
+
"attestation_history_corrupt",
|
|
132948
|
+
"attestation_stale",
|
|
132949
|
+
"headless_run_failed",
|
|
132950
|
+
"ux_evidence_absent",
|
|
132951
|
+
"ux_evidence_unrecorded",
|
|
132952
|
+
"ux_evidence_stale",
|
|
132953
|
+
"ux_evidence_outdated",
|
|
132954
|
+
"ux_evidence_failed",
|
|
132955
|
+
"ux_evidence_inherited_screens",
|
|
132956
|
+
"ux_evidence_unreadable",
|
|
132957
|
+
"lint_errors",
|
|
132958
|
+
"control_token_expired",
|
|
132959
|
+
"app_source_contains_secrets",
|
|
132960
|
+
"app_source_too_large",
|
|
132961
|
+
"publish_body_too_large",
|
|
132962
|
+
"needs_vpn",
|
|
132963
|
+
"serve_unreachable",
|
|
132964
|
+
"no_serve_plane",
|
|
132965
|
+
"upload_http_error",
|
|
132966
|
+
"claim_failed",
|
|
132967
|
+
"invalid_flag_value",
|
|
132968
|
+
"invalid_selector",
|
|
132969
|
+
"duplicate_only",
|
|
132970
|
+
"incompatible_push_flags",
|
|
132971
|
+
"from_release_needs_a_value",
|
|
132972
|
+
"conflicting_blast_radius",
|
|
132973
|
+
"no_blast_radius",
|
|
132974
|
+
"invalid_visibility",
|
|
132975
|
+
"release_read_failed",
|
|
132976
|
+
"base_not_found",
|
|
132977
|
+
"no_live_release",
|
|
132978
|
+
"invalid_patch",
|
|
132979
|
+
"would_regress",
|
|
132980
|
+
"not_carried_by_patch",
|
|
132981
|
+
"authoring_unavailable",
|
|
132982
|
+
"push_failed"
|
|
132983
|
+
];
|
|
132984
|
+
UX_CODES = {
|
|
132985
|
+
absent: "ux_evidence_absent",
|
|
132986
|
+
unrecorded: "ux_evidence_unrecorded",
|
|
132987
|
+
stale: "ux_evidence_stale",
|
|
132988
|
+
outdated: "ux_evidence_outdated",
|
|
132989
|
+
failed: "ux_evidence_failed",
|
|
132990
|
+
"inherited-screens": "ux_evidence_inherited_screens",
|
|
132991
|
+
unreadable: "ux_evidence_unreadable"
|
|
132992
|
+
};
|
|
132993
|
+
}
|
|
132994
|
+
});
|
|
132995
|
+
|
|
132929
132996
|
// src/release-patch.ts
|
|
132930
132997
|
var release_patch_exports = {};
|
|
132931
132998
|
__export(release_patch_exports, {
|
|
@@ -143740,23 +143807,23 @@ var init_preview_assets_generated = __esm({
|
|
|
143740
143807
|
"../../packages/serve/src/preview-assets.generated.ts"() {
|
|
143741
143808
|
init_preview_assets_format();
|
|
143742
143809
|
PREVIEW_ASSET_MANIFEST = [
|
|
143743
|
-
{ key: "assets/index-
|
|
143744
|
-
{ key: "assets/pdf.worker.min-BmVo14Nb.mjs", contentType: "application/octet-stream", offset:
|
|
143745
|
-
{ key: "assets/pdfDocument-
|
|
143746
|
-
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset:
|
|
143747
|
-
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset:
|
|
143748
|
-
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset:
|
|
143749
|
-
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
143750
|
-
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset:
|
|
143751
|
-
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset:
|
|
143752
|
-
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
143753
|
-
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset:
|
|
143754
|
-
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
143755
|
-
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset:
|
|
143756
|
-
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset:
|
|
143757
|
-
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
143758
|
-
{ key: "index.html", contentType: "text/html; charset=utf-8", offset:
|
|
143759
|
-
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset:
|
|
143810
|
+
{ key: "assets/index-C_dUZJT0.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1194463, sha256: "6640d516f6b59bacd5f71dbcb264dbdc9aeef5502fb284d0680d91ae40ea141a" },
|
|
143811
|
+
{ key: "assets/pdf.worker.min-BmVo14Nb.mjs", contentType: "application/octet-stream", offset: 1194471, length: 1317034, sha256: "a33cfe728c584fdba4fcc1fd54bcdc2f9f2f13889ddbb5b2bd1d0f8cbe49b84e" },
|
|
143812
|
+
{ key: "assets/pdfDocument-sHOpLp0o.js", contentType: "application/javascript; charset=utf-8", offset: 2511505, length: 489166, sha256: "57445251df98761d808381a823b3f01c948f41faeae9e61d1bcd9ab8ef1eadc3" },
|
|
143813
|
+
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 3000671, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
|
|
143814
|
+
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 3005281, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
|
|
143815
|
+
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 3493865, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
|
|
143816
|
+
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 3584789, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
|
|
143817
|
+
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 3675773, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
|
|
143818
|
+
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 3766753, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
|
|
143819
|
+
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 3857625, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
|
|
143820
|
+
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 3948605, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
|
|
143821
|
+
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 4057241, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
|
|
143822
|
+
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 4166325, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
|
|
143823
|
+
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 4275373, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
|
|
143824
|
+
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 4383073, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
|
|
143825
|
+
{ key: "index.html", contentType: "text/html; charset=utf-8", offset: 4471181, length: 4947, sha256: "cecdf774237e111e8306c71e372f32f6fdad1f8ac7c0d7736723bf79fea24503" },
|
|
143826
|
+
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 4476128, length: 17344, sha256: "d057298d75e7373e5e2c4d11b7d1bcbea88e03af555ba63d97641c78509c58e4" }
|
|
143760
143827
|
];
|
|
143761
143828
|
}
|
|
143762
143829
|
});
|
|
@@ -163742,11 +163809,11 @@ async function contentVisibility(dir) {
|
|
|
163742
163809
|
}
|
|
163743
163810
|
return void 0;
|
|
163744
163811
|
}
|
|
163745
|
-
function visibilityFlag(flags, io2) {
|
|
163812
|
+
function visibilityFlag(flags, io2, failure = (fields) => fields) {
|
|
163746
163813
|
if (flags.visibility === void 0) return {};
|
|
163747
163814
|
const v2 = str9(flags.visibility);
|
|
163748
163815
|
if (v2 !== "public" && v2 !== "unlisted") {
|
|
163749
|
-
io2.error(JSON.stringify({ error: `--visibility must be public or unlisted (got "${v2 ?? ""}")` }));
|
|
163816
|
+
io2.error(JSON.stringify(failure({ error: `--visibility must be public or unlisted (got "${v2 ?? ""}")` })));
|
|
163750
163817
|
return null;
|
|
163751
163818
|
}
|
|
163752
163819
|
return { value: v2 };
|
|
@@ -164296,6 +164363,9 @@ async function runCommand(argv, io2, deps) {
|
|
|
164296
164363
|
const { _: pos, flags } = parseFlags(argv);
|
|
164297
164364
|
const flagErrors = switchFlagErrors(argv);
|
|
164298
164365
|
const command = pos[0];
|
|
164366
|
+
const pushErrors = command === "push" ? await Promise.resolve().then(() => (init_push_errors(), push_errors_exports)) : void 0;
|
|
164367
|
+
const uxEvidenceCode2 = (state) => pushErrors?.uxEvidenceCode(state) ?? "ux_evidence_absent";
|
|
164368
|
+
const pushFailure2 = command === "push" ? pushErrors.pushFailure : (_code, fields) => fields;
|
|
164299
164369
|
let updateNotice;
|
|
164300
164370
|
const print = (o2) => io2.log(JSON.stringify(
|
|
164301
164371
|
o2 && typeof o2 === "object" && !Array.isArray(o2) ? tailLast(updateNotice ? { ...o2, upgrade: upgradeEnvelope(updateNotice) } : o2) : o2,
|
|
@@ -164316,7 +164386,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
164316
164386
|
return command === "help" || flags.help ? 0 : 2;
|
|
164317
164387
|
}
|
|
164318
164388
|
if (flagErrors.length > 0 && flags.help !== true) {
|
|
164319
|
-
io2.error(JSON.stringify({ ok: false, error: "invalid_flag_value", problems: flagErrors, hint: "run `himi help` for the flag grammar." }));
|
|
164389
|
+
io2.error(JSON.stringify(pushFailure2("invalid_flag_value", { ok: false, error: "invalid_flag_value", problems: flagErrors, hint: "run `himi help` for the flag grammar." })));
|
|
164320
164390
|
return 2;
|
|
164321
164391
|
}
|
|
164322
164392
|
if (flags["skip-version-check"] !== true) {
|
|
@@ -164343,11 +164413,11 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
164343
164413
|
const base = serveBase();
|
|
164344
164414
|
if (!isLoopbackBase(base)) return null;
|
|
164345
164415
|
if (await servePlaneAnswers(() => client2().health("serve"))) return null;
|
|
164346
|
-
io2.error(JSON.stringify(foreignPlaneError(targetName(), base)));
|
|
164416
|
+
io2.error(JSON.stringify(pushFailure2("no_serve_plane", foreignPlaneError(targetName(), base))));
|
|
164347
164417
|
return 1;
|
|
164348
164418
|
};
|
|
164349
164419
|
const printServeFailure = (r2, extra) => {
|
|
164350
|
-
print({ ok: false, ...extra, status: r2.status, body: r2.json ?? r2.text });
|
|
164420
|
+
print(pushFailure2("upload_http_error", { ok: false, ...extra, status: r2.status, body: r2.json ?? r2.text }));
|
|
164351
164421
|
return 1;
|
|
164352
164422
|
};
|
|
164353
164423
|
const serveFailure = async (r2, extra) => {
|
|
@@ -164360,33 +164430,33 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
164360
164430
|
const target = targetName();
|
|
164361
164431
|
const baseFound = await fetchReleaseManifest(client2, app, opts.fromRelease);
|
|
164362
164432
|
if ("error" in baseFound) {
|
|
164363
|
-
io2.error(JSON.stringify(baseFound));
|
|
164433
|
+
io2.error(JSON.stringify(pushFailure2("release_read_failed", baseFound)));
|
|
164364
164434
|
return 1;
|
|
164365
164435
|
}
|
|
164366
164436
|
const base = baseFound.manifest;
|
|
164367
164437
|
if (!base) {
|
|
164368
|
-
io2.error(JSON.stringify({
|
|
164438
|
+
io2.error(JSON.stringify(pushFailure2(opts.explicitBase ? "base_not_found" : "no_live_release", {
|
|
164369
164439
|
ok: false,
|
|
164370
164440
|
error: opts.explicitBase ? "base_not_found" : "no_live_release",
|
|
164371
164441
|
app,
|
|
164372
164442
|
...opts.explicitBase ? { releaseId: opts.fromRelease } : {},
|
|
164373
164443
|
message: opts.explicitBase ? `no release ${opts.fromRelease} for ${app}. Retention keeps the current release plus roughly the newest 20 ledger entries.` : `${app} has no live release to derive from. A patch changes part of an existing release; there is nothing here to be part of.`,
|
|
164374
164444
|
hint: opts.explicitBase ? `list what exists: \`himi context --app ${app} --target ${target}\`` : "publish the whole app first: `himi push --draft` then `himi promote`."
|
|
164375
|
-
}));
|
|
164445
|
+
})));
|
|
164376
164446
|
return 1;
|
|
164377
164447
|
}
|
|
164378
164448
|
let regressions = [];
|
|
164379
164449
|
{
|
|
164380
164450
|
const liveFound = await fetchReleaseManifest(client2, app);
|
|
164381
164451
|
if ("error" in liveFound) {
|
|
164382
|
-
io2.error(JSON.stringify(liveFound));
|
|
164452
|
+
io2.error(JSON.stringify(pushFailure2("release_read_failed", liveFound)));
|
|
164383
164453
|
return 1;
|
|
164384
164454
|
}
|
|
164385
164455
|
const live3 = liveFound.manifest;
|
|
164386
164456
|
if (live3 && live3.releaseId !== base.releaseId) {
|
|
164387
164457
|
regressions = regressionDiff2(base, live3, plan.touched);
|
|
164388
164458
|
if (regressions.length > 0 && !opts.acceptRegressions) {
|
|
164389
|
-
io2.error(JSON.stringify({
|
|
164459
|
+
io2.error(JSON.stringify(pushFailure2("would_regress", {
|
|
164390
164460
|
ok: false,
|
|
164391
164461
|
patched: false,
|
|
164392
164462
|
error: "would_regress",
|
|
@@ -164398,14 +164468,14 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
164398
164468
|
moves: plan.touched,
|
|
164399
164469
|
hint: opts.explicitBase ? `patch the live release instead (drop --from-release), widen the patch to cover them, or re-run with --accept-regressions if reverting those ${regressions.length} item(s) is what you mean.` : `${app} was published again while this patch was being prepared, so the base it was planned against is no longer live. Re-run to plan against the new release, or pass --accept-regressions to derive from the older one anyway.`,
|
|
164400
164470
|
proof: "core/serve-control/tests/routes-release-branch-patch.test.ts:126"
|
|
164401
|
-
}, null, 2));
|
|
164471
|
+
}), null, 2));
|
|
164402
164472
|
return 1;
|
|
164403
164473
|
}
|
|
164404
164474
|
}
|
|
164405
164475
|
}
|
|
164406
164476
|
const notCarried = opts.notCarried ?? [];
|
|
164407
164477
|
if (notCarried.length > 0 && !opts.acceptPartial) {
|
|
164408
|
-
io2.error(JSON.stringify({
|
|
164478
|
+
io2.error(JSON.stringify(pushFailure2("not_carried_by_patch", {
|
|
164409
164479
|
ok: false,
|
|
164410
164480
|
patched: false,
|
|
164411
164481
|
error: "not_carried_by_patch",
|
|
@@ -164415,25 +164485,25 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
164415
164485
|
notCarried,
|
|
164416
164486
|
carriedByPatch: ["screens", "bundles", "tokens", "appConfig"],
|
|
164417
164487
|
hint: "run a full `himi push` \u2014 it publishes assets, the icon, the surface envelope and minBinaryVersion. Re-run with --accept-partial only if you know those devices are already covered."
|
|
164418
|
-
}, null, 2));
|
|
164488
|
+
}), null, 2));
|
|
164419
164489
|
return 1;
|
|
164420
164490
|
}
|
|
164421
164491
|
if (opts.ux?.evidence && opts.ux.evidence.state !== "recorded" && !opts.force) {
|
|
164422
|
-
print({
|
|
164492
|
+
print(pushFailure2(uxEvidenceCode2(opts.ux.evidence.state), {
|
|
164423
164493
|
ok: false,
|
|
164424
164494
|
patched: false,
|
|
164425
164495
|
pushed: false,
|
|
164426
164496
|
uxEvidence: opts.ux.evidence,
|
|
164427
164497
|
releaseId: opts.ux.ctx.releaseId,
|
|
164428
164498
|
hint: uxHandoverHint(opts.ux.evidence, opts.ux.ctx)
|
|
164429
|
-
});
|
|
164499
|
+
}));
|
|
164430
164500
|
return 1;
|
|
164431
164501
|
}
|
|
164432
164502
|
let r2;
|
|
164433
164503
|
try {
|
|
164434
164504
|
r2 = await client2().releasePatch(app, base.releaseId, plan.patch, draft, opts.meta, opts.force);
|
|
164435
164505
|
} catch (e2) {
|
|
164436
|
-
io2.error(JSON.stringify({ ok: false, patched: false, error: "serve_unreachable", app, message: e2.message }));
|
|
164506
|
+
io2.error(JSON.stringify(pushFailure2("serve_unreachable", { ok: false, patched: false, error: "serve_unreachable", app, message: e2.message })));
|
|
164437
164507
|
return 1;
|
|
164438
164508
|
}
|
|
164439
164509
|
if (!r2.ok) {
|
|
@@ -164499,17 +164569,17 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
164499
164569
|
const wantDraft = flags.draft === true;
|
|
164500
164570
|
const wantLive = flags.live === true;
|
|
164501
164571
|
if (wantDraft && wantLive) {
|
|
164502
|
-
io2.error(JSON.stringify({ ok: false, error: "conflicting_blast_radius", message: "--draft and --live are mutually exclusive; pass exactly one." }));
|
|
164572
|
+
io2.error(JSON.stringify(pushFailure2("conflicting_blast_radius", { ok: false, error: "conflicting_blast_radius", message: "--draft and --live are mutually exclusive; pass exactly one." })));
|
|
164503
164573
|
return null;
|
|
164504
164574
|
}
|
|
164505
164575
|
if (!wantDraft && !wantLive) {
|
|
164506
|
-
io2.error(JSON.stringify({
|
|
164576
|
+
io2.error(JSON.stringify(pushFailure2("no_blast_radius", {
|
|
164507
164577
|
ok: false,
|
|
164508
164578
|
error: "no_blast_radius",
|
|
164509
164579
|
message: "a patch must say where it lands: --draft (nothing changes on any device) or --live (every device, immediately).",
|
|
164510
164580
|
hint: "`himi push` defaults to --draft because it publishes a whole release you then promote. A patch has no default, because the two outcomes are not comparable.",
|
|
164511
164581
|
usage
|
|
164512
|
-
}));
|
|
164582
|
+
})));
|
|
164513
164583
|
return null;
|
|
164514
164584
|
}
|
|
164515
164585
|
return wantDraft;
|
|
@@ -166688,15 +166758,15 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166688
166758
|
if (flags.only !== void 0) {
|
|
166689
166759
|
onlyRaw = str9(flags.only);
|
|
166690
166760
|
if (!onlyRaw) {
|
|
166691
|
-
io2.error(JSON.stringify({ error: "--only needs a selector", usage: ONLY_USAGE }));
|
|
166761
|
+
io2.error(JSON.stringify(pushFailure2("invalid_selector", { error: "--only needs a selector", usage: ONLY_USAGE })));
|
|
166692
166762
|
return 2;
|
|
166693
166763
|
}
|
|
166694
166764
|
if (argv.filter((a2) => a2 === "--only" || a2.startsWith("--only=")).length > 1) {
|
|
166695
|
-
io2.error(JSON.stringify({
|
|
166765
|
+
io2.error(JSON.stringify(pushFailure2("duplicate_only", {
|
|
166696
166766
|
error: "duplicate_only",
|
|
166697
166767
|
usage: ONLY_USAGE,
|
|
166698
166768
|
message: "--only was passed more than once, and only the last would have taken effect. Comma-separate the selectors instead: --only screen:a,screen:b"
|
|
166699
|
-
}));
|
|
166769
|
+
})));
|
|
166700
166770
|
return 2;
|
|
166701
166771
|
}
|
|
166702
166772
|
for (const [flag, why] of [
|
|
@@ -166704,12 +166774,12 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166704
166774
|
["visibility", "the patch route does not stamp visibility; set it with `himi visibility` instead"]
|
|
166705
166775
|
]) {
|
|
166706
166776
|
if (flags[flag] !== void 0) {
|
|
166707
|
-
io2.error(JSON.stringify({ error: `--${flag} is not meaningful with --only`, message: why, usage: ONLY_USAGE }));
|
|
166777
|
+
io2.error(JSON.stringify(pushFailure2("incompatible_push_flags", { error: `--${flag} is not meaningful with --only`, message: why, usage: ONLY_USAGE })));
|
|
166708
166778
|
return 2;
|
|
166709
166779
|
}
|
|
166710
166780
|
}
|
|
166711
166781
|
if (flags["from-release"] !== void 0 && !str9(flags["from-release"])) {
|
|
166712
|
-
io2.error(JSON.stringify({ ok: false, error: "from_release_needs_a_value", message: "--from-release takes a release id to branch from. Omit the flag entirely to patch the live release.", usage: ONLY_USAGE }));
|
|
166782
|
+
io2.error(JSON.stringify(pushFailure2("from_release_needs_a_value", { ok: false, error: "from_release_needs_a_value", message: "--from-release takes a release id to branch from. Omit the flag entirely to patch the live release.", usage: ONLY_USAGE })));
|
|
166713
166783
|
return 2;
|
|
166714
166784
|
}
|
|
166715
166785
|
if (patchBlastRadius(ONLY_USAGE) === null) return 2;
|
|
@@ -166728,14 +166798,14 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166728
166798
|
const { loadTargetsFile: loadTargetsFile2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
166729
166799
|
const configured = loadTargetsFile2();
|
|
166730
166800
|
const usable = Object.keys(configured.targets ?? {}).filter((n2) => n2 !== pushTarget).filter((n2) => inspectControlToken(configured.targets?.[n2]?.controlToken).valid).sort().map((n2) => ({ target: n2, control: configured.targets?.[n2]?.control }));
|
|
166731
|
-
io2.error(JSON.stringify({
|
|
166801
|
+
io2.error(JSON.stringify(pushFailure2("control_token_expired", {
|
|
166732
166802
|
ok: false,
|
|
166733
166803
|
pushed: false,
|
|
166734
166804
|
error: "control_token_expired",
|
|
166735
166805
|
target: pushTarget,
|
|
166736
166806
|
...usable.length ? { usableTargets: usable } : {},
|
|
166737
166807
|
hint: `${pushAuthHint(401, pushToken, pushTarget, !!ownerIssue)}` + (usable.length ? ` \u2014 you are also logged in for ${usable.map((u2) => `${u2.target}${u2.control ? ` (${u2.control})` : ""}`).join(", ")}, but check the plane before switching: a different target publishes somewhere else and claims the slug there.` : "")
|
|
166738
|
-
}));
|
|
166808
|
+
})));
|
|
166739
166809
|
return 1;
|
|
166740
166810
|
}
|
|
166741
166811
|
let app = resolveAppSlug(declared, (pushToken ? decodeControlTokenClaims(pushToken) : null) ?? {});
|
|
@@ -166763,7 +166833,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166763
166833
|
...await navIssues2(dir, strict)
|
|
166764
166834
|
]);
|
|
166765
166835
|
if (hasErrors && !force) {
|
|
166766
|
-
print({ ...report3, pushed: false, hint: "lint found errors \u2014 fix them, or re-run `himi push --force` to publish anyway" });
|
|
166836
|
+
print(pushFailure2("lint_errors", { ...report3, pushed: false, hint: "lint found errors \u2014 fix them, or re-run `himi push --force` to publish anyway" }));
|
|
166767
166837
|
return 1;
|
|
166768
166838
|
}
|
|
166769
166839
|
const declaredData = await loadDataResourcesQuietly(dir);
|
|
@@ -166782,28 +166852,28 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166782
166852
|
bundles: Object.keys(manifest.bundles ?? {})
|
|
166783
166853
|
});
|
|
166784
166854
|
if (!parsed.ok) {
|
|
166785
|
-
io2.error(JSON.stringify(parsed.error, null, 2));
|
|
166855
|
+
io2.error(JSON.stringify(pushFailure2("invalid_selector", parsed.error), null, 2));
|
|
166786
166856
|
return 2;
|
|
166787
166857
|
}
|
|
166788
166858
|
const fromRelease = str9(flags["from-release"]);
|
|
166789
166859
|
const baseForPlan = await fetchReleaseManifest(client2, app, fromRelease);
|
|
166790
166860
|
if ("error" in baseForPlan) {
|
|
166791
|
-
io2.error(JSON.stringify(baseForPlan));
|
|
166861
|
+
io2.error(JSON.stringify(pushFailure2("release_read_failed", baseForPlan)));
|
|
166792
166862
|
return 1;
|
|
166793
166863
|
}
|
|
166794
166864
|
if (!baseForPlan.manifest) {
|
|
166795
|
-
io2.error(JSON.stringify({
|
|
166865
|
+
io2.error(JSON.stringify(pushFailure2(fromRelease ? "base_not_found" : "no_live_release", {
|
|
166796
166866
|
ok: false,
|
|
166797
166867
|
error: fromRelease ? "base_not_found" : "no_live_release",
|
|
166798
166868
|
app,
|
|
166799
166869
|
message: fromRelease ? `no release ${fromRelease} for ${app}.` : `no published release for "${app}" to patch \u2014 run \`himi push\` first.`,
|
|
166800
166870
|
hint: "if this app is registered under a different slug, pass --app <slug>."
|
|
166801
|
-
}));
|
|
166871
|
+
})));
|
|
166802
166872
|
return 1;
|
|
166803
166873
|
}
|
|
166804
166874
|
const planned = planFromPayload2(payload, baseForPlan.manifest, parsed.selectors);
|
|
166805
166875
|
if (!planned.ok) {
|
|
166806
|
-
io2.error(JSON.stringify(planned.error, null, 2));
|
|
166876
|
+
io2.error(JSON.stringify(pushFailure2("invalid_patch", planned.error), null, 2));
|
|
166807
166877
|
return 1;
|
|
166808
166878
|
}
|
|
166809
166879
|
const patchUxEvidence = handoverUxEvidence(dir, {
|
|
@@ -166873,21 +166943,21 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166873
166943
|
const collected = collectAppSource2(dir);
|
|
166874
166944
|
const findings = secretFindings2(collected.entries);
|
|
166875
166945
|
if (findings.length > 0) {
|
|
166876
|
-
io2.error(JSON.stringify({
|
|
166946
|
+
io2.error(JSON.stringify(pushFailure2("app_source_contains_secrets", {
|
|
166877
166947
|
ok: false,
|
|
166878
166948
|
pushed: false,
|
|
166879
166949
|
error: `refusing to archive the source: ${findings[0].path} looks like it contains ${findings[0].reason}`,
|
|
166880
166950
|
file: findings[0].path,
|
|
166881
166951
|
found: findings.map((f2) => f2.path),
|
|
166882
166952
|
hint: "move it out of the content package (or into .himi/, .env, or a *.pem/*.p8 file, which are never archived), or push with --no-source"
|
|
166883
|
-
}));
|
|
166953
|
+
})));
|
|
166884
166954
|
return 1;
|
|
166885
166955
|
}
|
|
166886
166956
|
const zip = zipAppSource2(collected.entries);
|
|
166887
166957
|
const encoded2 = zip.toString("base64");
|
|
166888
166958
|
if (encoded2.length > MAX_APP_SOURCE_BYTES2) {
|
|
166889
166959
|
const largest = largestEntries2(collected.entries);
|
|
166890
|
-
io2.error(JSON.stringify({
|
|
166960
|
+
io2.error(JSON.stringify(pushFailure2("app_source_too_large", {
|
|
166891
166961
|
ok: false,
|
|
166892
166962
|
pushed: false,
|
|
166893
166963
|
error: "app_source_too_large",
|
|
@@ -166897,7 +166967,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166897
166967
|
files: collected.entries.length,
|
|
166898
166968
|
largest,
|
|
166899
166969
|
hint: `trim the content package \u2014 the largest entries are ${largest.map((e2) => `${e2.path} (${Math.round(e2.compressed / 1024)} KB compressed)`).join(", ")}. Sizes are COMPRESSED, because the cap is measured on the deflated archive \u2014 a big but compressible source file is rarely what costs you it. Build output, dependencies, native shells and tooling output (.himi/, and shots/ + evidence/ at the package root) are already excluded. \`--no-source\` publishes without the archive, but then \`himi pull\` cannot restore this release.`
|
|
166900
|
-
}));
|
|
166970
|
+
})));
|
|
166901
166971
|
return 1;
|
|
166902
166972
|
}
|
|
166903
166973
|
appSourceB64 = encoded2;
|
|
@@ -166914,22 +166984,22 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166914
166984
|
if (attestationErrors.length > 0 && !force) {
|
|
166915
166985
|
const remedy = `\`himi test --content ${contentArg2}\` (or \`himi render <screenId> --content ${contentArg2}\`)`;
|
|
166916
166986
|
const corrupted = evidence.historyErrors.length > 0;
|
|
166917
|
-
print({
|
|
166987
|
+
print(pushFailure2(corrupted ? "attestation_history_corrupt" : "attestation_stale", {
|
|
166918
166988
|
ok: false,
|
|
166919
166989
|
pushed: false,
|
|
166920
166990
|
errors: attestationErrors,
|
|
166921
166991
|
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\`.`
|
|
166922
|
-
});
|
|
166992
|
+
}));
|
|
166923
166993
|
return 1;
|
|
166924
166994
|
}
|
|
166925
166995
|
const attestation = evidence.selected ? summaryOf(evidence.selected) : void 0;
|
|
166926
166996
|
if (attestation?.report.ok === false && !force) {
|
|
166927
|
-
print({
|
|
166997
|
+
print(pushFailure2("headless_run_failed", {
|
|
166928
166998
|
ok: false,
|
|
166929
166999
|
pushed: false,
|
|
166930
167000
|
attestation,
|
|
166931
167001
|
hint: "the latest verified headless run failed \u2014 fix it and rerun the tool, or re-run `himi push --force` to publish anyway"
|
|
166932
|
-
});
|
|
167002
|
+
}));
|
|
166933
167003
|
return 1;
|
|
166934
167004
|
}
|
|
166935
167005
|
const uxEvidence = handoverUxEvidence(dir, {
|
|
@@ -166941,7 +167011,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166941
167011
|
inheritedScreens: []
|
|
166942
167012
|
});
|
|
166943
167013
|
if (uxEvidence && uxEvidence.state !== "recorded" && !force) {
|
|
166944
|
-
print({
|
|
167014
|
+
print(pushFailure2(uxEvidenceCode2(uxEvidence.state), {
|
|
166945
167015
|
ok: false,
|
|
166946
167016
|
pushed: false,
|
|
166947
167017
|
uxEvidence,
|
|
@@ -166959,7 +167029,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166959
167029
|
// `inherited-screens` and has no base to restore.
|
|
166960
167030
|
nondeterministicWorkers: drifting
|
|
166961
167031
|
})
|
|
166962
|
-
});
|
|
167032
|
+
}));
|
|
166963
167033
|
return 1;
|
|
166964
167034
|
}
|
|
166965
167035
|
const forcedPastErrors = force && (hasErrors || attestation?.report.ok === false || attestationErrors.length > 0);
|
|
@@ -166970,7 +167040,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166970
167040
|
bakedBytes: assetRecords.filter(([, a2]) => a2.baked).reduce((n2, [, a2]) => n2 + a2.bytes, 0),
|
|
166971
167041
|
otaOnly: assetRecords.filter(([, a2]) => !a2.baked).map(([name]) => name)
|
|
166972
167042
|
} : null;
|
|
166973
|
-
const visArg = visibilityFlag(flags, io2);
|
|
167043
|
+
const visArg = visibilityFlag(flags, io2, (fields) => pushFailure2("invalid_visibility", fields));
|
|
166974
167044
|
if (!visArg) return 2;
|
|
166975
167045
|
const visibility = visArg.value ?? await contentVisibility(dir);
|
|
166976
167046
|
const meta = {
|
|
@@ -166989,7 +167059,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166989
167059
|
const publishBodyBytesFor = (slug) => publishReleaseBodyBytes(publishBodyFor(slug), meta, draft);
|
|
166990
167060
|
const refuseOversizedBody = (slug, bodyBytes) => {
|
|
166991
167061
|
const withoutSource = appSourceB64 ? bodyBytes - appSourceB64.length : bodyBytes;
|
|
166992
|
-
io2.error(JSON.stringify({
|
|
167062
|
+
io2.error(JSON.stringify(pushFailure2("publish_body_too_large", {
|
|
166993
167063
|
ok: false,
|
|
166994
167064
|
app: slug,
|
|
166995
167065
|
pushed: false,
|
|
@@ -166998,7 +167068,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
166998
167068
|
limit: MAX_PUBLISH_BODY_BYTES,
|
|
166999
167069
|
...appSourceB64 ? { appSourceBytes: appSourceB64.length } : {},
|
|
167000
167070
|
hint: appSourceB64 && withoutSource <= MAX_PUBLISH_BODY_BYTES ? "push with --no-source to publish without the source archive (this release then cannot be restored with `himi pull`), or trim the app's assets and bundles" : "trim the app's assets and bundles \u2014 this release is over the limit even without the source archive, so --no-source would not help"
|
|
167001
|
-
}));
|
|
167071
|
+
})));
|
|
167002
167072
|
return 1;
|
|
167003
167073
|
};
|
|
167004
167074
|
const composedBodyBytes = publishBodyBytesFor(app);
|
|
@@ -167009,14 +167079,14 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
167009
167079
|
if (ownerIssue && pushToken && !tokenCoversApp(pushToken, app)) {
|
|
167010
167080
|
const claim = await autoClaimSlug({ ownerIssue, controlToken: pushToken, app, accountId: str9(flags["account-id"]) });
|
|
167011
167081
|
if (claim.fatal) {
|
|
167012
|
-
io2.error(JSON.stringify({
|
|
167082
|
+
io2.error(JSON.stringify(pushFailure2("claim_failed", {
|
|
167013
167083
|
ok: false,
|
|
167014
167084
|
app,
|
|
167015
167085
|
pushed: false,
|
|
167016
167086
|
status: claim.fatal.status,
|
|
167017
167087
|
...claim.fatal.body,
|
|
167018
167088
|
...claimHint(claim.fatal.status, claim.fatal.error)
|
|
167019
|
-
}));
|
|
167089
|
+
})));
|
|
167020
167090
|
return 1;
|
|
167021
167091
|
}
|
|
167022
167092
|
if (claim.refusal) io2.error(JSON.stringify({ warning: claim.refusal.error, hint: claim.refusal.hint }));
|
|
@@ -167037,16 +167107,16 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
167037
167107
|
r2 = await client2().publishRelease(publishPayload, meta, draft);
|
|
167038
167108
|
} catch (e2) {
|
|
167039
167109
|
const target = targetName();
|
|
167040
|
-
io2.error(JSON.stringify({
|
|
167110
|
+
io2.error(JSON.stringify(pushFailure2("needs_vpn", {
|
|
167041
167111
|
...serveUnreachableError(target, serveBase(), e2.message),
|
|
167042
167112
|
version: cliVersion()
|
|
167043
|
-
}));
|
|
167113
|
+
})));
|
|
167044
167114
|
return 1;
|
|
167045
167115
|
}
|
|
167046
167116
|
if (!r2.ok) {
|
|
167047
167117
|
const authHint = pushAuthHint(r2.status, pushToken, pushTarget, !!ownerIssue);
|
|
167048
167118
|
const hint = authHint ?? releaseIdExistsHint(r2.status, r2.json);
|
|
167049
|
-
print({ ok: false, status: r2.status, body: r2.json ?? r2.text, ...hint ? { hint } : {} });
|
|
167119
|
+
print(pushFailure2("upload_http_error", { ok: false, status: r2.status, body: r2.json ?? r2.text, ...hint ? { hint } : {} }));
|
|
167050
167120
|
return 1;
|
|
167051
167121
|
}
|
|
167052
167122
|
const release = r2.json?.releaseId ?? manifest.releaseId;
|
|
@@ -170283,15 +170353,15 @@ Code: ${userCode}`);
|
|
|
170283
170353
|
}
|
|
170284
170354
|
} catch (err) {
|
|
170285
170355
|
if (err instanceof AuthoringUnavailable) {
|
|
170286
|
-
io2.error(JSON.stringify({
|
|
170356
|
+
io2.error(JSON.stringify(pushFailure2("authoring_unavailable", {
|
|
170287
170357
|
error: `the himi authoring runtime failed to load (himi v${cliVersion()}). Off-repo this almost always means an out-of-date CLI, NOT a platform limitation \u2014 upgrade: \`npm i -g @wix/himalaya-cli@latest\`. If you meant to run in-repo, run from a monorepo checkout.`,
|
|
170288
170358
|
version: cliVersion(),
|
|
170289
170359
|
upgrade: "npm i -g @wix/himalaya-cli@latest",
|
|
170290
170360
|
portal: DEVELOPER_PORTAL_URL
|
|
170291
|
-
}));
|
|
170361
|
+
})));
|
|
170292
170362
|
return 1;
|
|
170293
170363
|
}
|
|
170294
|
-
io2.error(JSON.stringify({ error: err.message }));
|
|
170364
|
+
io2.error(JSON.stringify(pushFailure2("push_failed", { error: err.message })));
|
|
170295
170365
|
const code = err.exitCode;
|
|
170296
170366
|
return code === 2 ? 2 : 1;
|
|
170297
170367
|
}
|
|
Binary file
|
|
@@ -278,6 +278,7 @@ Concrete patterns lifted from real usage:
|
|
|
278
278
|
- **Screen root** — `withModifiers(root, { padding: { leading: 16, trailing: 16, top: 16, bottom: 40 }, background: { token: "surfacePrimary" } })`. Consistent gutter, comfortable bottom inset.
|
|
279
279
|
- **Row with a leading label and trailing detail** — `hstack({ spacing: 8, children: [ icon, text, spacer(), trailingText ] })`. The `spacer()` does the push; don't hand-pad.
|
|
280
280
|
- **Fill-width controls share a row** — put fill-width controls in **one** `HStack` and let them share it (weight/flex), rather than stacking `fillMaxWidth` elements. This is a real iOS↔Android parity requirement, not a preference — a full-width control alone in an HStack renders differently across platforms.
|
|
281
|
+
- **A screen that owns its top bar (search beside back)** — set `topBar: { leading: "none" }` and put the row in `stickyHeader`: `hstack([ iconButton({ icon: "arrow_left", accessibilityLabel: "Back", action: { actionId: "nav.back", presentation: "back" } }), withModifiers(textField({ placeholder: "Search", … }), { weight: 1 }) ])`. With **both** set, every shell draws no bar and no back control of its own (iOS hides the system bar, Android floats no chevron and reserves no band, web hides its bar on every look), so the row is the only top bar. `leading: "none"` without a `stickyHeader` keeps the bar's title and trailing actions but drops the back control (and the hamburger, for that screen). System back — hardware, gesture, edge swipe, browser — still pops. Any other `leading` value keeps today's chrome (#3486; demo: `gallery_search_topbar`, linked from the gallery's "Lists & data").
|
|
281
282
|
- **Cell list** — `card([ vstack({ spacing: 0, alignment: "leading", children: [ …ListItem cells… ] }) ])`. `spacing: 0` because the cells own their own dividers/padding.
|
|
282
283
|
- **Overlay** — `zstack({ alignment: "center", children: [ backdropImage(cornerRadius 12), withModifiers(label, { padding, background: { token: "accentPrimary" }, cornerRadius: 10 }) ] })`.
|
|
283
284
|
- **Responsive** — `grid({ columns: 1, columnsMedium: 2, spacing: 12 })` for 1-up on phone / 2-up on tablet; `splitView({ primary, detail, primaryMaxWidth: 280 })` for master/detail that stacks on a phone; set `contentMaxWidth` in tokens so wide surfaces keep a readable column instead of running edge-to-edge.
|
package/guides/headless-loop.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
Author a Himi app from your own machine, push it to the production serve as a **draft**,
|
|
4
4
|
preview it, read the worker's logs, then promote it live. Nothing is live until you promote.
|
|
5
5
|
|
|
6
|
+
For machine consumers, `himi push` failures carry a stable top-level `code`.
|
|
7
|
+
See [JSON failure codes](himi-json-output.md) (`himi docs himi-json-output`);
|
|
8
|
+
branch on that code rather than human prose or envelope shape.
|
|
9
|
+
|
|
6
10
|
## 1. Get the CLI
|
|
7
11
|
|
|
8
12
|
```sh
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# himi JSON failure contract
|
|
2
|
+
|
|
3
|
+
Every `ok: false` JSON envelope must carry a top-level `code`: a stable, typed
|
|
4
|
+
snake_case identifier for consumers to branch on. Codes are never renamed or
|
|
5
|
+
repurposed when human wording changes. New codes may be added; consumers must
|
|
6
|
+
handle an unknown code as a failure, without parsing prose.
|
|
7
|
+
|
|
8
|
+
**Rollout:** this contract is enforced for **`himi push`**, including `--only`,
|
|
9
|
+
first. Other subcommands still have legacy failures without codes and are a
|
|
10
|
+
follow-up migration. This is an additive change: existing `error`, `errors`,
|
|
11
|
+
`message`, `hint`, status, body, and gate-specific fields stay as they were.
|
|
12
|
+
Some legacy envelopes have no singular `error` or `hint`; consumers must not
|
|
13
|
+
require those fields. Existing error-only push refusals now also carry `ok: false`.
|
|
14
|
+
Output channels and process exit codes are unchanged. Read JSON failures on both
|
|
15
|
+
stdout and stderr; progress/warnings are not failure envelopes.
|
|
16
|
+
|
|
17
|
+
`code` is the machine contract. `error`/`errors`/`message` describe the failure for
|
|
18
|
+
people and `hint`, when present, suggests recovery. Neither their wording nor the
|
|
19
|
+
presence of one of those legacy shapes identifies the failure class. The complete
|
|
20
|
+
push code vocabulary is listed below.
|
|
21
|
+
|
|
22
|
+
For example, an attestation that no longer describes the content being published
|
|
23
|
+
keeps its existing fields and adds the code:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"ok": false,
|
|
28
|
+
"pushed": false,
|
|
29
|
+
"errors": ["2 retained attestation(s) were recorded at this commit, but none describes the content being published"],
|
|
30
|
+
"hint": "…",
|
|
31
|
+
"code": "attestation_stale"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Push codes
|
|
36
|
+
|
|
37
|
+
| Code | Meaning |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `attestation_history_corrupt` | Retained attestation history failed integrity checks. Takes precedence if stale runs are also present. |
|
|
40
|
+
| `attestation_stale` | Retained runs at this commit do not describe the current release. |
|
|
41
|
+
| `headless_run_failed` | A verified headless run failed. |
|
|
42
|
+
| `ux_evidence_absent` | No UX evidence artifact exists. |
|
|
43
|
+
| `ux_evidence_unrecorded` | No usable native UX drive was recorded. |
|
|
44
|
+
| `ux_evidence_stale` | UX evidence belongs to a different release/commit. |
|
|
45
|
+
| `ux_evidence_outdated` | UX evidence predates edits at this commit. |
|
|
46
|
+
| `ux_evidence_failed` | A matching UX drive failed. |
|
|
47
|
+
| `ux_evidence_inherited_screens` | A partial push inherits screens not covered by local UX evidence. |
|
|
48
|
+
| `ux_evidence_unreadable` | The UX evidence artifact cannot be read. |
|
|
49
|
+
| `lint_errors` | Build/lint validation found errors. |
|
|
50
|
+
| `control_token_expired` | The selected control token has expired. |
|
|
51
|
+
| `app_source_contains_secrets` | Source archiving detected a possible credential. |
|
|
52
|
+
| `app_source_too_large` | The encoded source archive exceeds its limit. |
|
|
53
|
+
| `publish_body_too_large` | The complete publish request exceeds its limit. |
|
|
54
|
+
| `needs_vpn` | Full-push request to the publishing service could not complete (existing reachability code). This is not proof that VPN is the cause; read the target-aware error. |
|
|
55
|
+
| `serve_unreachable` | A partial-push upload could not reach the publishing service. |
|
|
56
|
+
| `no_serve_plane` | A loopback service answering a partial push is not a himi publishing service. |
|
|
57
|
+
| `upload_http_error` | The upload returned a non-success HTTP status. `status` and the original `body` remain available; server errors never replace this CLI code. |
|
|
58
|
+
| `claim_failed` | Auto-claim returned a fatal refusal. Existing server fields and `status` remain available. |
|
|
59
|
+
| `invalid_flag_value` | A flag violates the CLI flag grammar. |
|
|
60
|
+
| `invalid_selector` | `--only` is missing a selector or its selector syntax is invalid. |
|
|
61
|
+
| `duplicate_only` | `--only` was repeated. |
|
|
62
|
+
| `incompatible_push_flags` | A flag cannot be combined with `--only`. |
|
|
63
|
+
| `from_release_needs_a_value` | `--from-release` has no value. |
|
|
64
|
+
| `conflicting_blast_radius` | A partial push supplied both `--draft` and `--live`. |
|
|
65
|
+
| `no_blast_radius` | A partial push supplied neither `--draft` nor `--live`. |
|
|
66
|
+
| `invalid_visibility` | The visibility value is invalid. |
|
|
67
|
+
| `release_read_failed` | A partial push could not read the base/live release. |
|
|
68
|
+
| `base_not_found` | The explicitly selected base has no manifest. |
|
|
69
|
+
| `no_live_release` | There is no live release to patch. |
|
|
70
|
+
| `invalid_patch` | The selection cannot produce a valid patch. The existing `error` and plan details are preserved. |
|
|
71
|
+
| `would_regress` | A partial push would revert unselected live changes. |
|
|
72
|
+
| `not_carried_by_patch` | The package changed dependencies that a partial push cannot carry. |
|
|
73
|
+
| `authoring_unavailable` | The bundled authoring runtime failed to load. |
|
|
74
|
+
| `push_failed` | An otherwise unclassified exception stopped push (for example unreadable content). The original error is preserved. |
|
|
75
|
+
|
|
76
|
+
The `ux_evidence_*` codes map to the existing typed `uxEvidence.state`; a successful
|
|
77
|
+
`recorded` state has no refusal code. Successful pushes and warnings do not gain
|
|
78
|
+
failure codes. A forced push still follows the existing override rules.
|
|
79
|
+
|
|
80
|
+
## Follow-ups
|
|
81
|
+
|
|
82
|
+
- Migrate failure envelopes in subcommands other than `push`, keeping this contract.
|
|
83
|
+
- Review/reword human messages separately; no message rewrite is part of this change.
|
|
84
|
+
- Review exit-code conventions separately; no exit code changes are part of this change.
|
package/guides/index.json
CHANGED
|
@@ -53,6 +53,12 @@
|
|
|
53
53
|
"category": "CLI & Loop",
|
|
54
54
|
"file": "headless-loop.md"
|
|
55
55
|
},
|
|
56
|
+
{
|
|
57
|
+
"id": "himi-json-output",
|
|
58
|
+
"title": "JSON failures — stable error codes",
|
|
59
|
+
"category": "CLI & Loop",
|
|
60
|
+
"file": "himi-json-output.md"
|
|
61
|
+
},
|
|
56
62
|
{
|
|
57
63
|
"id": "himi-mcp",
|
|
58
64
|
"title": "himi in your coding agent (MCP)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1248.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": [
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"artifactId": "himalaya-cli"
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
|
-
"falconPackageHash": "
|
|
70
|
+
"falconPackageHash": "c3978ffa25b74036a72561b7d81106fb8230ea398edab38ac57b4270"
|
|
71
71
|
}
|