@wix/himalaya-cli 0.1109.0 → 0.1111.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 +1178 -704
- package/guides/getting-started.md +18 -3
- package/guides/headless-loop.md +13 -7
- package/guides/himi-skill.md +18 -3
- package/guides/push-cli-onboarding.md +58 -4
- package/guides/skill.md +11 -7
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -994,8 +994,8 @@ function mobileUxDocumentBody(mobile) {
|
|
|
994
994
|
}
|
|
995
995
|
return out2 + mobile.slice(last);
|
|
996
996
|
}
|
|
997
|
-
function mobileUxSection(
|
|
998
|
-
const match = new RegExp(`(?:^|\\n)##[ \\t]+${heading}\\b[^\\n]*([\\s\\S]*?)(?=\\n##[ \\t]|$)`, "i").exec(
|
|
997
|
+
function mobileUxSection(text8, heading) {
|
|
998
|
+
const match = new RegExp(`(?:^|\\n)##[ \\t]+${heading}\\b[^\\n]*([\\s\\S]*?)(?=\\n##[ \\t]|$)`, "i").exec(text8);
|
|
999
999
|
return match ? match[1] : null;
|
|
1000
1000
|
}
|
|
1001
1001
|
function sectionTables(markdown) {
|
|
@@ -1024,8 +1024,8 @@ function namesOneCta(cell) {
|
|
|
1024
1024
|
if (!cell) return false;
|
|
1025
1025
|
return !/[,;]|\s\/\s/.test(cell);
|
|
1026
1026
|
}
|
|
1027
|
-
function declaredStates(
|
|
1028
|
-
const contract = section(
|
|
1027
|
+
function declaredStates(text8) {
|
|
1028
|
+
const contract = section(text8, "Screen contract");
|
|
1029
1029
|
if (!contract) return [];
|
|
1030
1030
|
const states = [];
|
|
1031
1031
|
for (const bullet of contract.matchAll(/^[ \t]*[-*][ \t]*[^:\n]*\bstates?[ \t]*:[ \t]*(\S.*)$/gim)) {
|
|
@@ -1035,8 +1035,8 @@ function declaredStates(text6) {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
return [...new Set(states.map((state) => state.toLowerCase()))];
|
|
1037
1037
|
}
|
|
1038
|
-
function primaryCtaStructureGap(
|
|
1039
|
-
const acceptance = section(
|
|
1038
|
+
function primaryCtaStructureGap(text8) {
|
|
1039
|
+
const acceptance = section(text8, "Implementation acceptance");
|
|
1040
1040
|
if (acceptance === null) return "There is no `## Implementation acceptance` section to read it from.";
|
|
1041
1041
|
const tables = sectionTables(acceptance).filter((table) => table.data.length > 0);
|
|
1042
1042
|
if (!tables.length) return "Its acceptance table has no rows.";
|
|
@@ -1049,7 +1049,7 @@ function primaryCtaStructureGap(text6) {
|
|
|
1049
1049
|
const data = ctaTables.flatMap(({ table }) => table.data);
|
|
1050
1050
|
const flat = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
1051
1051
|
const rowText = data.map((row) => flat(row.join(" ")));
|
|
1052
|
-
const missing = declaredStates(
|
|
1052
|
+
const missing = declaredStates(text8).filter((state) => !rowText.some((row) => row.includes(flat(state))));
|
|
1053
1053
|
if (missing.length) return `The acceptance table has no row for the declared state(s): ${missing.join(", ")}.`;
|
|
1054
1054
|
return null;
|
|
1055
1055
|
}
|
|
@@ -1064,16 +1064,16 @@ function hasUnterminatedComment(mobile) {
|
|
|
1064
1064
|
}
|
|
1065
1065
|
}
|
|
1066
1066
|
function mobileUxMarkerFindings(mobile, renderedWidths = []) {
|
|
1067
|
-
const
|
|
1067
|
+
const text8 = mobileUxDocumentBody(mobile);
|
|
1068
1068
|
const findings = [];
|
|
1069
|
-
if (!/^## Implementation acceptance\b/im.test(
|
|
1069
|
+
if (!/^## Implementation acceptance\b/im.test(text8)) {
|
|
1070
1070
|
findings.push({ code: "missing-acceptance-matrix", message: "MOBILE-UX.md must contain the heading `## Implementation acceptance` at the start of a line. Case is not checked, but use this spelling: other tooling addresses the document by it." });
|
|
1071
1071
|
}
|
|
1072
|
-
if (!/^## Evidence\b/im.test(
|
|
1072
|
+
if (!/^## Evidence\b/im.test(text8)) {
|
|
1073
1073
|
findings.push({ code: "missing-evidence-section", message: "MOBILE-UX.md must contain the heading `## Evidence` at the start of a line. Case is not checked, but use this spelling: other tooling addresses the document by it." });
|
|
1074
1074
|
}
|
|
1075
|
-
if (!/one primary CTA per state/i.test(
|
|
1076
|
-
const gap = primaryCtaStructureGap(
|
|
1075
|
+
if (!/one primary CTA per state/i.test(text8)) {
|
|
1076
|
+
const gap = primaryCtaStructureGap(text8);
|
|
1077
1077
|
if (gap) {
|
|
1078
1078
|
findings.push({
|
|
1079
1079
|
code: "missing-primary-cta-rule",
|
|
@@ -1081,8 +1081,8 @@ function mobileUxMarkerFindings(mobile, renderedWidths = []) {
|
|
|
1081
1081
|
});
|
|
1082
1082
|
}
|
|
1083
1083
|
}
|
|
1084
|
-
if (!hasWidthEvidence(
|
|
1085
|
-
findings.push({ code: "missing-width-evidence", message: widthEvidenceMessage(
|
|
1084
|
+
if (!hasWidthEvidence(text8, renderedWidths)) {
|
|
1085
|
+
findings.push({ code: "missing-width-evidence", message: widthEvidenceMessage(text8, renderedWidths) });
|
|
1086
1086
|
}
|
|
1087
1087
|
if (findings.length && hasUnterminatedComment(mobile)) {
|
|
1088
1088
|
return [{
|
|
@@ -1092,15 +1092,15 @@ function mobileUxMarkerFindings(mobile, renderedWidths = []) {
|
|
|
1092
1092
|
}
|
|
1093
1093
|
return findings;
|
|
1094
1094
|
}
|
|
1095
|
-
function widthMentions(
|
|
1095
|
+
function widthMentions(text8) {
|
|
1096
1096
|
const mentions = [];
|
|
1097
|
-
for (const match of
|
|
1097
|
+
for (const match of text8.matchAll(/\d+/g)) {
|
|
1098
1098
|
const value = Number(match[0]);
|
|
1099
1099
|
const start = match.index ?? 0;
|
|
1100
1100
|
if (!inRange(value)) continue;
|
|
1101
1101
|
const end = start + match[0].length;
|
|
1102
|
-
const after =
|
|
1103
|
-
const before =
|
|
1102
|
+
const after = text8.slice(end, end + EDGE_WINDOW);
|
|
1103
|
+
const before = text8.slice(Math.max(0, start - EDGE_WINDOW), start);
|
|
1104
1104
|
if (looksLikeYear(value) && !(UNIT_ATTACHED.test(after) || UNIT_WORD_ATTACHED.test(after))) continue;
|
|
1105
1105
|
const spelledAsWidth = UNIT_ATTACHED.test(after) || UNIT_WORD_ATTACHED.test(after) || FILENAME_AFTER.test(after);
|
|
1106
1106
|
const vetoed = spelledAsWidth && (ANTI_CUE_BEFORE.test(before) || PAIR_HEIGHT_BEFORE.test(before) || ANTI_CUE_AFTER.test(after));
|
|
@@ -2093,7 +2093,7 @@ function foldLegacyReport(legacy, seed2) {
|
|
|
2093
2093
|
...arrayField(legacy.failures),
|
|
2094
2094
|
...arrayField(legacy.warnings)
|
|
2095
2095
|
];
|
|
2096
|
-
const
|
|
2096
|
+
const report3 = emptyReport({
|
|
2097
2097
|
...seed2,
|
|
2098
2098
|
ok: typeof legacy.ok === "boolean" ? legacy.ok : seed2.ok,
|
|
2099
2099
|
app: seed2.app ?? stringField(legacy.app),
|
|
@@ -2104,12 +2104,12 @@ function foldLegacyReport(legacy, seed2) {
|
|
|
2104
2104
|
const oldCounts = recordField(legacy.counts);
|
|
2105
2105
|
return {
|
|
2106
2106
|
...legacy,
|
|
2107
|
-
...
|
|
2107
|
+
...report3,
|
|
2108
2108
|
counts: {
|
|
2109
2109
|
...oldCounts,
|
|
2110
|
-
errors: numberField(oldCounts.errors) ??
|
|
2111
|
-
warnings: numberField(oldCounts.warnings) ??
|
|
2112
|
-
gaps:
|
|
2110
|
+
errors: numberField(oldCounts.errors) ?? report3.counts.errors,
|
|
2111
|
+
warnings: numberField(oldCounts.warnings) ?? report3.counts.warnings,
|
|
2112
|
+
gaps: report3.counts.gaps
|
|
2113
2113
|
}
|
|
2114
2114
|
};
|
|
2115
2115
|
}
|
|
@@ -2773,8 +2773,8 @@ function isAttestationV2(value) {
|
|
|
2773
2773
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2774
2774
|
const a = value;
|
|
2775
2775
|
const coverage = a.coverage;
|
|
2776
|
-
const
|
|
2777
|
-
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((count2) => typeof count2 === "number" && Number.isFinite(count2) && count2 >= 0) && typeof
|
|
2776
|
+
const report3 = a.report;
|
|
2777
|
+
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((count2) => typeof count2 === "number" && Number.isFinite(count2) && count2 >= 0) && typeof report3?.ok === "boolean" && !!report3.counts && [report3.counts.errors, report3.counts.warnings, report3.counts.gaps].every((count2) => typeof count2 === "number" && Number.isFinite(count2) && count2 >= 0) && (a.prev === null || typeof a.prev === "string");
|
|
2778
2778
|
}
|
|
2779
2779
|
function readLatestAttestation(contentDir2) {
|
|
2780
2780
|
try {
|
|
@@ -3170,8 +3170,8 @@ function penalty(m, size) {
|
|
|
3170
3170
|
score += Math.floor(Math.abs(dark * 20 - total * 10) / total) * 10;
|
|
3171
3171
|
return score;
|
|
3172
3172
|
}
|
|
3173
|
-
function encodeQr(
|
|
3174
|
-
const bytes2 = [...new TextEncoder().encode(
|
|
3173
|
+
function encodeQr(text8) {
|
|
3174
|
+
const bytes2 = [...new TextEncoder().encode(text8)];
|
|
3175
3175
|
const version2 = fitVersion(bytes2.length);
|
|
3176
3176
|
if (!version2) {
|
|
3177
3177
|
throw new RangeError(`QR payload too long: ${bytes2.length} bytes exceeds version ${QR_MAX_VERSION} at EC level L`);
|
|
@@ -3742,7 +3742,7 @@ async function mintToken(args, label2, cacheKey, spawnImpl) {
|
|
|
3742
3742
|
tokenCache.set(cacheKey, fromEnv);
|
|
3743
3743
|
return fromEnv;
|
|
3744
3744
|
}
|
|
3745
|
-
const token = await new Promise((
|
|
3745
|
+
const token = await new Promise((resolve53, reject) => {
|
|
3746
3746
|
let child;
|
|
3747
3747
|
try {
|
|
3748
3748
|
child = spawnImpl("wix", [...args]);
|
|
@@ -3771,12 +3771,12 @@ async function mintToken(args, label2, cacheKey, spawnImpl) {
|
|
|
3771
3771
|
child.stdout?.on("data", (d) => {
|
|
3772
3772
|
out2 += String(d);
|
|
3773
3773
|
const m = out2.match(TOKEN_RE);
|
|
3774
|
-
if (m) done(() =>
|
|
3774
|
+
if (m) done(() => resolve53(m[0]));
|
|
3775
3775
|
});
|
|
3776
3776
|
child.stderr?.on("data", (d) => {
|
|
3777
3777
|
out2 += String(d);
|
|
3778
3778
|
const m = out2.match(TOKEN_RE);
|
|
3779
|
-
if (m) done(() =>
|
|
3779
|
+
if (m) done(() => resolve53(m[0]));
|
|
3780
3780
|
});
|
|
3781
3781
|
child.on("error", (e) => done(() => reject(new Error(mintHelp(label2, e.message)))));
|
|
3782
3782
|
child.on("exit", (code) => done(() => reject(new Error(
|
|
@@ -3821,10 +3821,10 @@ function ownerTransport(msid, fetchImpl = fetch, timeoutMs = DEFAULT_TIMEOUT_MS2
|
|
|
3821
3821
|
}
|
|
3822
3822
|
throw new Error(`${method} ${path2} failed: ${err?.message ?? String(e)}`);
|
|
3823
3823
|
}
|
|
3824
|
-
const
|
|
3825
|
-
let parsed =
|
|
3824
|
+
const text8 = await res.text();
|
|
3825
|
+
let parsed = text8;
|
|
3826
3826
|
try {
|
|
3827
|
-
parsed =
|
|
3827
|
+
parsed = text8 ? JSON.parse(text8) : null;
|
|
3828
3828
|
} catch {
|
|
3829
3829
|
}
|
|
3830
3830
|
if (!res.ok) throw new WixApiError(res.status, method, path2, parsed);
|
|
@@ -4486,18 +4486,18 @@ function confinementPlugin(contentRoot) {
|
|
|
4486
4486
|
}
|
|
4487
4487
|
function assertConfinedTsconfig(entry, contentRoot, allows) {
|
|
4488
4488
|
const seen = /* @__PURE__ */ new Set();
|
|
4489
|
-
const
|
|
4489
|
+
const refuse5 = (p, why) => {
|
|
4490
4490
|
throw new Error(
|
|
4491
4491
|
`refused to read ${p}: ${why}. A tsconfig may extend a file from its own package or an installed dependency \u2014 nothing else.`
|
|
4492
4492
|
);
|
|
4493
4493
|
};
|
|
4494
|
-
const stripJsonc = (
|
|
4495
|
-
if (
|
|
4494
|
+
const stripJsonc = (text8) => {
|
|
4495
|
+
if (text8.charCodeAt(0) === 65279) text8 = text8.slice(1);
|
|
4496
4496
|
let out2 = "";
|
|
4497
4497
|
let inStr = false;
|
|
4498
4498
|
let esc = false;
|
|
4499
|
-
for (let i = 0; i <
|
|
4500
|
-
const c =
|
|
4499
|
+
for (let i = 0; i < text8.length; i++) {
|
|
4500
|
+
const c = text8[i];
|
|
4501
4501
|
if (inStr) {
|
|
4502
4502
|
out2 += c;
|
|
4503
4503
|
if (esc) esc = false;
|
|
@@ -4510,13 +4510,13 @@ function assertConfinedTsconfig(entry, contentRoot, allows) {
|
|
|
4510
4510
|
out2 += c;
|
|
4511
4511
|
continue;
|
|
4512
4512
|
}
|
|
4513
|
-
if (c === "/" &&
|
|
4514
|
-
while (i <
|
|
4513
|
+
if (c === "/" && text8[i + 1] === "/") {
|
|
4514
|
+
while (i < text8.length && text8[i] !== "\n") i++;
|
|
4515
4515
|
continue;
|
|
4516
4516
|
}
|
|
4517
|
-
if (c === "/" &&
|
|
4517
|
+
if (c === "/" && text8[i + 1] === "*") {
|
|
4518
4518
|
i += 2;
|
|
4519
|
-
while (i <
|
|
4519
|
+
while (i < text8.length && !(text8[i] === "*" && text8[i + 1] === "/")) i++;
|
|
4520
4520
|
i++;
|
|
4521
4521
|
continue;
|
|
4522
4522
|
}
|
|
@@ -4552,7 +4552,7 @@ function assertConfinedTsconfig(entry, contentRoot, allows) {
|
|
|
4552
4552
|
try {
|
|
4553
4553
|
manifest = JSON.parse(stripJsonc(readFileSync10(pj, "utf8")));
|
|
4554
4554
|
} catch {
|
|
4555
|
-
|
|
4555
|
+
refuse5(pj, "its package.json could not be parsed, so its `tsconfig` field cannot be checked");
|
|
4556
4556
|
}
|
|
4557
4557
|
const field = manifest?.tsconfig;
|
|
4558
4558
|
if (typeof field === "string" && field) {
|
|
@@ -4568,7 +4568,7 @@ function assertConfinedTsconfig(entry, contentRoot, allows) {
|
|
|
4568
4568
|
const real = realpathOrSelf(file);
|
|
4569
4569
|
if (seen.has(real)) return;
|
|
4570
4570
|
seen.add(real);
|
|
4571
|
-
if (!allows(real))
|
|
4571
|
+
if (!allows(real)) refuse5(file, "it is outside this content package");
|
|
4572
4572
|
let raw;
|
|
4573
4573
|
try {
|
|
4574
4574
|
raw = readFileSync10(real, "utf8");
|
|
@@ -4579,7 +4579,7 @@ function assertConfinedTsconfig(entry, contentRoot, allows) {
|
|
|
4579
4579
|
try {
|
|
4580
4580
|
parsed = JSON.parse(stripJsonc(raw));
|
|
4581
4581
|
} catch {
|
|
4582
|
-
|
|
4582
|
+
refuse5(file, "its tsconfig could not be parsed, so its `extends` cannot be checked");
|
|
4583
4583
|
}
|
|
4584
4584
|
const ext = parsed?.extends;
|
|
4585
4585
|
for (const one of Array.isArray(ext) ? ext : ext ? [ext] : []) {
|
|
@@ -6762,6 +6762,7 @@ function walk(node, registry, path2, out2) {
|
|
|
6762
6762
|
var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA;
|
|
6763
6763
|
var init_tokens = __esm({
|
|
6764
6764
|
"../../core/ts/src/tokens.ts"() {
|
|
6765
|
+
"use strict";
|
|
6765
6766
|
SYSTEM_FONT_FAMILY = "$system";
|
|
6766
6767
|
TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
|
|
6767
6768
|
}
|
|
@@ -8419,8 +8420,8 @@ function pushSpan(out2, s) {
|
|
|
8419
8420
|
else out2.push(s);
|
|
8420
8421
|
}
|
|
8421
8422
|
function spanOf(textNode) {
|
|
8422
|
-
const
|
|
8423
|
-
if (
|
|
8423
|
+
const text8 = str(at(textNode, "textData", "text"));
|
|
8424
|
+
if (text8 === "") return null;
|
|
8424
8425
|
let bold = false, italic = false, underline = false;
|
|
8425
8426
|
let link;
|
|
8426
8427
|
const decorations = at(textNode, "textData", "decorations");
|
|
@@ -8447,7 +8448,7 @@ function spanOf(textNode) {
|
|
|
8447
8448
|
}
|
|
8448
8449
|
}
|
|
8449
8450
|
}
|
|
8450
|
-
const span = { text:
|
|
8451
|
+
const span = { text: text8 };
|
|
8451
8452
|
if (bold) span.bold = true;
|
|
8452
8453
|
if (italic) span.italic = true;
|
|
8453
8454
|
if (underline) span.underline = true;
|
|
@@ -8669,8 +8670,8 @@ function blockOf(ids, n, path2) {
|
|
|
8669
8670
|
const { title, url } = cardFields(n, type);
|
|
8670
8671
|
return [card(ids.claim(n, path2), type, title, url)];
|
|
8671
8672
|
}
|
|
8672
|
-
const
|
|
8673
|
-
return
|
|
8673
|
+
const text8 = plainText(n);
|
|
8674
|
+
return text8.trim() !== "" ? [{ kind: "paragraph", id: ids.claim(n, path2), spans: [{ text: text8 }] }] : [];
|
|
8674
8675
|
}
|
|
8675
8676
|
}
|
|
8676
8677
|
}
|
|
@@ -9941,26 +9942,26 @@ function serviceOnlyBindingValues(props, key2) {
|
|
|
9941
9942
|
});
|
|
9942
9943
|
return out2;
|
|
9943
9944
|
}
|
|
9944
|
-
function matchBraceSafe(
|
|
9945
|
+
function matchBraceSafe(text8, open) {
|
|
9945
9946
|
let depth = 0;
|
|
9946
|
-
for (let i = open; i <
|
|
9947
|
-
const c =
|
|
9947
|
+
for (let i = open; i < text8.length; i++) {
|
|
9948
|
+
const c = text8[i];
|
|
9948
9949
|
if (c === '"' || c === "'" || c === "`") {
|
|
9949
9950
|
const q = c;
|
|
9950
9951
|
i++;
|
|
9951
|
-
while (i <
|
|
9952
|
-
if (
|
|
9952
|
+
while (i < text8.length && text8[i] !== q) {
|
|
9953
|
+
if (text8[i] === "\\") i++;
|
|
9953
9954
|
i++;
|
|
9954
9955
|
}
|
|
9955
9956
|
continue;
|
|
9956
9957
|
}
|
|
9957
|
-
if (c === "/" &&
|
|
9958
|
-
while (i <
|
|
9958
|
+
if (c === "/" && text8[i + 1] === "/") {
|
|
9959
|
+
while (i < text8.length && text8[i] !== "\n") i++;
|
|
9959
9960
|
continue;
|
|
9960
9961
|
}
|
|
9961
|
-
if (c === "/" &&
|
|
9962
|
+
if (c === "/" && text8[i + 1] === "*") {
|
|
9962
9963
|
i += 2;
|
|
9963
|
-
while (i <
|
|
9964
|
+
while (i < text8.length && !(text8[i] === "*" && text8[i + 1] === "/")) i++;
|
|
9964
9965
|
i++;
|
|
9965
9966
|
continue;
|
|
9966
9967
|
}
|
|
@@ -12908,8 +12909,8 @@ var init_owner_claim = __esm({
|
|
|
12908
12909
|
|
|
12909
12910
|
// ../native-config/car.ts
|
|
12910
12911
|
function parseCarConfig(configTs) {
|
|
12911
|
-
const { text:
|
|
12912
|
-
for (const m of
|
|
12912
|
+
const { text: text8, inString } = scanConfig(configTs);
|
|
12913
|
+
for (const m of text8.matchAll(/\bcar\s*[:=]\s*\{([^}]*)\}/g)) {
|
|
12913
12914
|
if (inString[m.index]) continue;
|
|
12914
12915
|
return { category: /category\s*:\s*["']([^"']+)["']/.exec(m[1])?.[1] };
|
|
12915
12916
|
}
|
|
@@ -17995,12 +17996,12 @@ function hostAppliesDeferredWidgetActions(app) {
|
|
|
17995
17996
|
}
|
|
17996
17997
|
return false;
|
|
17997
17998
|
}
|
|
17998
|
-
function hasMarkers(
|
|
17999
|
-
const lines =
|
|
17999
|
+
function hasMarkers(text8, kind, label2) {
|
|
18000
|
+
const lines = text8.split("\n");
|
|
18000
18001
|
return lines.some((l) => isBegin(l, kind, label2)) && lines.some((l) => isEnd(l, kind, label2));
|
|
18001
18002
|
}
|
|
18002
|
-
function injectBlock(
|
|
18003
|
-
const lines =
|
|
18003
|
+
function injectBlock(text8, kind, label2, render) {
|
|
18004
|
+
const lines = text8.split("\n");
|
|
18004
18005
|
const beginIdx = lines.findIndex((l) => isBegin(l, kind, label2));
|
|
18005
18006
|
const endIdx = lines.findIndex((l) => isEnd(l, kind, label2));
|
|
18006
18007
|
if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) {
|
|
@@ -18115,8 +18116,8 @@ function genNativeConfig(app, opts = {}) {
|
|
|
18115
18116
|
];
|
|
18116
18117
|
const rce = declaresRichContentEditor(configTs);
|
|
18117
18118
|
for (const yml of [iosYml, macosProjectYml(app)]) {
|
|
18118
|
-
const
|
|
18119
|
-
const r = applyToFile(yml, "yaml", LABEL_LOCALWEB, renderLocalWebYaml(resourcesDirOf(
|
|
18119
|
+
const text8 = existsSync26(yml) ? readFileSync26(yml, "utf8") : "";
|
|
18120
|
+
const r = applyToFile(yml, "yaml", LABEL_LOCALWEB, renderLocalWebYaml(resourcesDirOf(text8), rce), check);
|
|
18120
18121
|
if (existsSync26(yml)) assertLocalWebBlockLanded(app, rce, yml, r.outcome);
|
|
18121
18122
|
files.push(r);
|
|
18122
18123
|
}
|
|
@@ -18526,21 +18527,21 @@ function carryPlanningJournal(parentDir, appDir3) {
|
|
|
18526
18527
|
return carried;
|
|
18527
18528
|
}
|
|
18528
18529
|
function formatBrief(input) {
|
|
18529
|
-
const { summary, open } = input;
|
|
18530
|
+
const { summary: summary2, open } = input;
|
|
18530
18531
|
const who = `himi \u2014 ${input.appName ?? "this folder"}`;
|
|
18531
18532
|
const lines = [];
|
|
18532
|
-
if (!
|
|
18533
|
+
if (!summary2.count) {
|
|
18533
18534
|
lines.push(`${who}: no journey journal yet (${PROGRESS_FILE.split(sep6).join("/")}).`);
|
|
18534
18535
|
lines.push(
|
|
18535
18536
|
'A new app starts with discovery (step 1). An app built before journaling existed continues from its current state instead: confirm with the customer that its SPEC.md was approved rather than re-running discovery. Either way, journal each milestone with `himi progress "<what changed>" --step <n>` so the next session can pick up from it.'
|
|
18536
18537
|
);
|
|
18537
18538
|
} else {
|
|
18538
|
-
const at2 =
|
|
18539
|
-
lines.push(`${who}: ${at2} \xB7 ${
|
|
18540
|
-
if (
|
|
18541
|
-
const pastPlan = (
|
|
18539
|
+
const at2 = summary2.current ? `journey step ${summary2.current.stepNumber}/${summary2.current.of} (${summary2.current.step})` : "no journey step recorded yet";
|
|
18540
|
+
lines.push(`${who}: ${at2} \xB7 ${summary2.count} postcard${summary2.count === 1 ? "" : "s"} since ${minute(summary2.startedAt)}.`);
|
|
18541
|
+
if (summary2.last) lines.push(`Last postcard (${minute(summary2.last.ts)}): ${summary2.last.message}`);
|
|
18542
|
+
const pastPlan = (summary2.current?.stepNumber ?? 0) > JOURNEY_STEPS.indexOf("plan") + 1;
|
|
18542
18543
|
lines.push(
|
|
18543
|
-
!
|
|
18544
|
+
!summary2.current ? "No journey step recorded yet: a new app starts with discovery (step 1); one whose SPEC.md the customer approved continues from its current state. `himi progress` prints every postcard." : pastPlan ? `Resume at step ${summary2.current.stepNumber}. Do not restart discovery: the approved plan is SPEC.md + MOBILE-UX.md.` : `Resume at step ${summary2.current.stepNumber}. Implementation has not started: treat SPEC.md + MOBILE-UX.md as a draft unless the journal records the customer approving them.`
|
|
18544
18545
|
);
|
|
18545
18546
|
}
|
|
18546
18547
|
if (open.length) {
|
|
@@ -18620,8 +18621,8 @@ var init_progress = __esm({
|
|
|
18620
18621
|
"src/progress.ts"() {
|
|
18621
18622
|
JOURNEY_STEPS = ["discover", "classify", "plan", "implement", "brand", "validate", "review", "download"];
|
|
18622
18623
|
CLI_AUTHOR = "himi";
|
|
18623
|
-
AUTO_MILESTONES = ["first-look", "test", "draft"];
|
|
18624
|
-
MILESTONE_STEP = { "first-look": "implement", test: "validate", draft: "review" };
|
|
18624
|
+
AUTO_MILESTONES = ["first-look", "test", "draft", "still-building"];
|
|
18625
|
+
MILESTONE_STEP = { "first-look": "implement", test: "validate", draft: "review", "still-building": "implement" };
|
|
18625
18626
|
AGENT_WINS_WITHIN_MS = 5 * 60 * 1e3;
|
|
18626
18627
|
isCliEntry = (e) => e.by === CLI_AUTHOR;
|
|
18627
18628
|
shasOf = (images) => new Set(Array.isArray(images) ? images.flatMap((i) => i && typeof i === "object" && typeof i.sha256 === "string" ? [i.sha256] : []) : []);
|
|
@@ -19328,13 +19329,13 @@ async function recordState(dir, r, deps) {
|
|
|
19328
19329
|
if (who.root === "") return "legacy";
|
|
19329
19330
|
return who.root === runRootId(dir) ? "live" : "stale";
|
|
19330
19331
|
}
|
|
19331
|
-
function openPreviewLog(dir) {
|
|
19332
|
+
function openPreviewLog(dir, name = "preview.log") {
|
|
19332
19333
|
const himiDir2 = join31(dir, ".himi");
|
|
19333
19334
|
if (existsSync28(himiDir2) && !lstatSync6(himiDir2).isDirectory()) {
|
|
19334
19335
|
throw new PreviewStartError(`${himiDir2} is not a real directory (a symlink?) \u2014 refusing to write the preview log through it`, "");
|
|
19335
19336
|
}
|
|
19336
19337
|
mkdirSync14(himiDir2, { recursive: true });
|
|
19337
|
-
const path2 = join31(himiDir2,
|
|
19338
|
+
const path2 = join31(himiDir2, name);
|
|
19338
19339
|
let existing;
|
|
19339
19340
|
try {
|
|
19340
19341
|
existing = lstatSync6(path2);
|
|
@@ -19464,10 +19465,10 @@ var init_preview = __esm({
|
|
|
19464
19465
|
}
|
|
19465
19466
|
},
|
|
19466
19467
|
kill: (pid, signal) => process.kill(pid, signal),
|
|
19467
|
-
portFree: (port) => new Promise((
|
|
19468
|
+
portFree: (port) => new Promise((resolve53) => {
|
|
19468
19469
|
const s = createServer2();
|
|
19469
|
-
s.once("error", () =>
|
|
19470
|
-
s.listen(port, "127.0.0.1", () => s.close(() =>
|
|
19470
|
+
s.once("error", () => resolve53(false));
|
|
19471
|
+
s.listen(port, "127.0.0.1", () => s.close(() => resolve53(true)));
|
|
19471
19472
|
}),
|
|
19472
19473
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
19473
19474
|
servingIdentity: async (origin) => {
|
|
@@ -19651,17 +19652,17 @@ ${BLOCK_END}`;
|
|
|
19651
19652
|
${block}
|
|
19652
19653
|
`;
|
|
19653
19654
|
}
|
|
19654
|
-
function markersIntact(
|
|
19655
|
-
const begins =
|
|
19656
|
-
const ends =
|
|
19655
|
+
function markersIntact(text8) {
|
|
19656
|
+
const begins = text8.split(BLOCK_BEGIN).length - 1;
|
|
19657
|
+
const ends = text8.split(BLOCK_END).length - 1;
|
|
19657
19658
|
if (begins === 0 && ends === 0) return true;
|
|
19658
|
-
return begins === 1 && ends === 1 &&
|
|
19659
|
+
return begins === 1 && ends === 1 && text8.indexOf(BLOCK_BEGIN) < text8.indexOf(BLOCK_END) && BLOCK_RE.test(text8);
|
|
19659
19660
|
}
|
|
19660
|
-
function withoutBlock(
|
|
19661
|
-
const m = BLOCK_RE.exec(
|
|
19662
|
-
if (!m) return
|
|
19663
|
-
const before =
|
|
19664
|
-
const after =
|
|
19661
|
+
function withoutBlock(text8) {
|
|
19662
|
+
const m = BLOCK_RE.exec(text8);
|
|
19663
|
+
if (!m) return text8;
|
|
19664
|
+
const before = text8.slice(0, m.index).replace(/\n+$/, "");
|
|
19665
|
+
const after = text8.slice(m.index + m[0].length).replace(/^\n+/, "");
|
|
19665
19666
|
if (!before) return after;
|
|
19666
19667
|
if (!after) return `${before}
|
|
19667
19668
|
`;
|
|
@@ -19669,8 +19670,8 @@ function withoutBlock(text6) {
|
|
|
19669
19670
|
|
|
19670
19671
|
${after}`;
|
|
19671
19672
|
}
|
|
19672
|
-
function blockVersion(
|
|
19673
|
-
return STAMP_RE.exec(
|
|
19673
|
+
function blockVersion(text8) {
|
|
19674
|
+
return STAMP_RE.exec(text8)?.[1] ?? null;
|
|
19674
19675
|
}
|
|
19675
19676
|
function agentsMdBody(appName2) {
|
|
19676
19677
|
const gates = APPROVAL_GATES.map((g) => `- \`${g.id}\` \u2014 ${g.label}`).join("\n");
|
|
@@ -20611,7 +20612,7 @@ async function injectRequest(listener, request) {
|
|
|
20611
20612
|
req.headers.host ??= url.host;
|
|
20612
20613
|
if (body && body.length > 0) req.push(body);
|
|
20613
20614
|
req.push(null);
|
|
20614
|
-
return await new Promise((
|
|
20615
|
+
return await new Promise((resolve53, reject) => {
|
|
20615
20616
|
const streamed = () => {
|
|
20616
20617
|
queueMicrotask(() => {
|
|
20617
20618
|
req.emit("close");
|
|
@@ -20627,7 +20628,7 @@ async function injectRequest(listener, request) {
|
|
|
20627
20628
|
else headers2.set(name, value);
|
|
20628
20629
|
}
|
|
20629
20630
|
const bodyless = out2.status === 204 || out2.status === 304 || request.method === "HEAD";
|
|
20630
|
-
|
|
20631
|
+
resolve53(new Response(bodyless ? null : out2.body, { status: out2.status, headers: headers2 }));
|
|
20631
20632
|
}, streamed);
|
|
20632
20633
|
res.on("inject-error", reject);
|
|
20633
20634
|
try {
|
|
@@ -21165,8 +21166,8 @@ async function postJson(path2, body, auth) {
|
|
|
21165
21166
|
headers: { "Content-Type": "application/json", ...auth ? { Authorization: auth } : {} },
|
|
21166
21167
|
body: JSON.stringify(body)
|
|
21167
21168
|
});
|
|
21168
|
-
const
|
|
21169
|
-
return { status: res.status, json:
|
|
21169
|
+
const text8 = await res.text();
|
|
21170
|
+
return { status: res.status, json: text8 ? JSON.parse(text8) : {} };
|
|
21170
21171
|
}
|
|
21171
21172
|
async function mintVisitor(clientId) {
|
|
21172
21173
|
const r = await postJson("/oauth2/token", { clientId, grantType: "anonymous" });
|
|
@@ -21412,13 +21413,13 @@ function recordWixTraffic(gw, app, adminUrl, ev) {
|
|
|
21412
21413
|
full.respBody = truncate(redactBodyString(full.respBody));
|
|
21413
21414
|
gw.traffic.push(full);
|
|
21414
21415
|
if (gw.traffic.length > TRAFFIC_CAP) gw.traffic.splice(0, gw.traffic.length - TRAFFIC_CAP);
|
|
21415
|
-
const
|
|
21416
|
+
const text8 = `event: wix.traffic
|
|
21416
21417
|
data: ${JSON.stringify(full)}
|
|
21417
21418
|
|
|
21418
21419
|
`;
|
|
21419
21420
|
for (const res of [...gw.clients]) {
|
|
21420
21421
|
try {
|
|
21421
|
-
res.write(
|
|
21422
|
+
res.write(text8);
|
|
21422
21423
|
} catch {
|
|
21423
21424
|
gw.clients.delete(res);
|
|
21424
21425
|
}
|
|
@@ -21780,10 +21781,10 @@ async function mintAnonymousVisitor(clientId, fetchImpl = fetch) {
|
|
|
21780
21781
|
headers: { "Content-Type": "application/json" },
|
|
21781
21782
|
body: JSON.stringify({ clientId, grantType: "anonymous" })
|
|
21782
21783
|
});
|
|
21783
|
-
const
|
|
21784
|
+
const text8 = await res.text();
|
|
21784
21785
|
let j = {};
|
|
21785
21786
|
try {
|
|
21786
|
-
j =
|
|
21787
|
+
j = text8 ? JSON.parse(text8) : {};
|
|
21787
21788
|
} catch {
|
|
21788
21789
|
}
|
|
21789
21790
|
if (res.status !== 200 || !j.access_token) throw new Error(`visitor mint failed (${res.status})`);
|
|
@@ -21947,10 +21948,10 @@ ${session}`);
|
|
|
21947
21948
|
}),
|
|
21948
21949
|
timedOut
|
|
21949
21950
|
]);
|
|
21950
|
-
const
|
|
21951
|
+
const text8 = await Promise.race([res.text(), timedOut]);
|
|
21951
21952
|
let j = {};
|
|
21952
21953
|
try {
|
|
21953
|
-
j =
|
|
21954
|
+
j = text8 ? JSON.parse(text8) : {};
|
|
21954
21955
|
} catch {
|
|
21955
21956
|
}
|
|
21956
21957
|
if (res.status < 200 || res.status >= 300 || !j.signedInstance)
|
|
@@ -22301,9 +22302,9 @@ var init_images = __esm({
|
|
|
22301
22302
|
});
|
|
22302
22303
|
|
|
22303
22304
|
// ../../core/dev-server/src/l10n-catalog.ts
|
|
22304
|
-
function parseCatalog(
|
|
22305
|
+
function parseCatalog(text8) {
|
|
22305
22306
|
try {
|
|
22306
|
-
const c = JSON.parse(
|
|
22307
|
+
const c = JSON.parse(text8);
|
|
22307
22308
|
return c && typeof c === "object" && c.messages && typeof c.messages === "object" ? c : void 0;
|
|
22308
22309
|
} catch {
|
|
22309
22310
|
return void 0;
|
|
@@ -22784,9 +22785,9 @@ async function getAndroidPixelDims(serial) {
|
|
|
22784
22785
|
try {
|
|
22785
22786
|
const args = serial ? ["-s", serial, "shell", "wm", "size"] : ["shell", "wm", "size"];
|
|
22786
22787
|
const out2 = await runner.capture(ADB_BIN, args);
|
|
22787
|
-
const
|
|
22788
|
-
const override = /Override size:\s*(\d+)x(\d+)/.exec(
|
|
22789
|
-
const physical = /Physical size:\s*(\d+)x(\d+)/.exec(
|
|
22788
|
+
const text8 = out2.toString("utf8");
|
|
22789
|
+
const override = /Override size:\s*(\d+)x(\d+)/.exec(text8);
|
|
22790
|
+
const physical = /Physical size:\s*(\d+)x(\d+)/.exec(text8);
|
|
22790
22791
|
const m = override ?? physical;
|
|
22791
22792
|
if (!m) throw new Error("could not parse wm size");
|
|
22792
22793
|
const dims = { width: parseInt(m[1], 10), height: parseInt(m[2], 10) };
|
|
@@ -23161,12 +23162,12 @@ function finalizeRecording(key2, rec) {
|
|
|
23161
23162
|
if (rec.finalize) return rec.finalize;
|
|
23162
23163
|
rec.finalize = (async () => {
|
|
23163
23164
|
clearTimeout(rec.autoStop);
|
|
23164
|
-
await new Promise((
|
|
23165
|
+
await new Promise((resolve53) => {
|
|
23165
23166
|
let done = false;
|
|
23166
23167
|
const finish2 = () => {
|
|
23167
23168
|
if (!done) {
|
|
23168
23169
|
done = true;
|
|
23169
|
-
|
|
23170
|
+
resolve53();
|
|
23170
23171
|
}
|
|
23171
23172
|
};
|
|
23172
23173
|
rec.child.once("close", finish2);
|
|
@@ -23277,7 +23278,7 @@ var init_preview2 = __esm({
|
|
|
23277
23278
|
DEVICE_LIST_TIMEOUT_MS = 5e3;
|
|
23278
23279
|
defaultRunner2 = {
|
|
23279
23280
|
capture(cmd, args, timeoutMs = DEFAULT_CAPTURE_TIMEOUT_MS) {
|
|
23280
|
-
return new Promise((
|
|
23281
|
+
return new Promise((resolve53, reject) => {
|
|
23281
23282
|
const child = spawn4(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
23282
23283
|
const out2 = [];
|
|
23283
23284
|
const err = [];
|
|
@@ -23299,7 +23300,7 @@ var init_preview2 = __esm({
|
|
|
23299
23300
|
child.stderr.on("data", (b) => err.push(b));
|
|
23300
23301
|
child.on("error", (e) => settle(reject, e));
|
|
23301
23302
|
child.on("close", (code) => {
|
|
23302
|
-
if (code === 0) settle(
|
|
23303
|
+
if (code === 0) settle(resolve53, Buffer.concat(out2));
|
|
23303
23304
|
else settle(reject, new Error(`${cmd} exited ${code}: ${Buffer.concat(err).toString("utf8")}`));
|
|
23304
23305
|
});
|
|
23305
23306
|
});
|
|
@@ -23719,7 +23720,7 @@ function buildOpenApi(descriptors) {
|
|
|
23719
23720
|
// Widgets
|
|
23720
23721
|
"/__dev/widget/preview": ["get", "Render a frozen widget snapshot."]
|
|
23721
23722
|
};
|
|
23722
|
-
for (const [p, [method,
|
|
23723
|
+
for (const [p, [method, summary2]] of Object.entries(control)) {
|
|
23723
23724
|
const responses = p === "/__dev/render" ? {
|
|
23724
23725
|
"200": { description: "completed render (legacy mode, or clean envelope verdict)" },
|
|
23725
23726
|
"400": { description: "invalid_json" },
|
|
@@ -23727,7 +23728,7 @@ function buildOpenApi(descriptors) {
|
|
|
23727
23728
|
"422": { description: "completed render with a failing envelope:1 verdict" },
|
|
23728
23729
|
"503": { description: "dev plane unavailable" }
|
|
23729
23730
|
} : { "200": { description: "ok" } };
|
|
23730
|
-
paths[p] = { [method]: { summary, responses } };
|
|
23731
|
+
paths[p] = { [method]: { summary: summary2, responses } };
|
|
23731
23732
|
}
|
|
23732
23733
|
for (const scope of ["net", "wix"]) {
|
|
23733
23734
|
paths[`/__dev/${scope}/rules`] = {
|
|
@@ -24155,9 +24156,9 @@ function mdScan(src, linkStyle = "") {
|
|
|
24155
24156
|
}
|
|
24156
24157
|
return out2 + mdEsc(src.slice(last));
|
|
24157
24158
|
}
|
|
24158
|
-
function mdInline(
|
|
24159
|
+
function mdInline(text8, linkColor) {
|
|
24159
24160
|
const safe = typeof linkColor === "string" && /^[#a-zA-Z0-9(),.%\s-]+$/.test(linkColor) ? linkColor : "";
|
|
24160
|
-
return { __html: mdScan(
|
|
24161
|
+
return { __html: mdScan(text8, safe ? ` style="color:${safe}"` : "") };
|
|
24161
24162
|
}
|
|
24162
24163
|
function sevColor(ctx, sev) {
|
|
24163
24164
|
switch (sev) {
|
|
@@ -27526,9 +27527,9 @@ function createRicosHostSession(o) {
|
|
|
27526
27527
|
if (!state.ready || state.unavailable) return;
|
|
27527
27528
|
o.send({ v: 1, type: "command", name, ...arg?.level ? { level: arg.level } : {}, ...arg?.value ? { value: arg.value } : {} });
|
|
27528
27529
|
},
|
|
27529
|
-
insertLink(url,
|
|
27530
|
+
insertLink(url, text8) {
|
|
27530
27531
|
if (!state.ready || state.unavailable || !isSafeEditorLink(url)) return false;
|
|
27531
|
-
o.send({ v: 1, type: "insertLink", url: url.trim(), ...
|
|
27532
|
+
o.send({ v: 1, type: "insertLink", url: url.trim(), ...text8 ? { text: text8 } : {} });
|
|
27532
27533
|
return true;
|
|
27533
27534
|
},
|
|
27534
27535
|
removeLink() {
|
|
@@ -27551,12 +27552,12 @@ function createRicosHostSession(o) {
|
|
|
27551
27552
|
flush() {
|
|
27552
27553
|
if (!state.ready || state.unavailable || disposed) return Promise.resolve(null);
|
|
27553
27554
|
const requestId2 = newId();
|
|
27554
|
-
return new Promise((
|
|
27555
|
+
return new Promise((resolve53) => {
|
|
27555
27556
|
const timer = t.setTimeout(() => {
|
|
27556
27557
|
flushes.delete(requestId2);
|
|
27557
|
-
|
|
27558
|
+
resolve53(null);
|
|
27558
27559
|
}, flushMs);
|
|
27559
|
-
flushes.set(requestId2, { resolve:
|
|
27560
|
+
flushes.set(requestId2, { resolve: resolve53, timer });
|
|
27560
27561
|
o.send({ v: 1, type: "requestContent", requestId: requestId2 });
|
|
27561
27562
|
});
|
|
27562
27563
|
},
|
|
@@ -27609,7 +27610,7 @@ async function runRicosImageUpload(o) {
|
|
|
27609
27610
|
};
|
|
27610
27611
|
session.insertMediaPlaceholder(requestId2);
|
|
27611
27612
|
try {
|
|
27612
|
-
const entry = await new Promise((
|
|
27613
|
+
const entry = await new Promise((resolve53) => {
|
|
27613
27614
|
let done = false;
|
|
27614
27615
|
let unsub = () => {
|
|
27615
27616
|
};
|
|
@@ -27618,7 +27619,7 @@ async function runRicosImageUpload(o) {
|
|
|
27618
27619
|
done = true;
|
|
27619
27620
|
t.clearTimeout(timer);
|
|
27620
27621
|
queueMicrotask(() => unsub());
|
|
27621
|
-
|
|
27622
|
+
resolve53(v);
|
|
27622
27623
|
};
|
|
27623
27624
|
const timer = t.setTimeout(() => finish2(null), o.timeoutMs ?? RICOS_UPLOAD_TIMEOUT_MS);
|
|
27624
27625
|
unsub = deps.subscribe(`${svc}.uploads.${requestId2}`, (v) => {
|
|
@@ -29225,13 +29226,13 @@ function NamedImage({ node, alt, env }) {
|
|
|
29225
29226
|
}
|
|
29226
29227
|
function Block({ b, env, depth }) {
|
|
29227
29228
|
const { ctx } = env;
|
|
29228
|
-
const
|
|
29229
|
+
const text8 = ctx.color("textPrimary", "#13171e");
|
|
29229
29230
|
const secondary = ctx.color("textSecondary", "#5c6370");
|
|
29230
29231
|
const border = ctx.color("border", "#e5e5e5");
|
|
29231
29232
|
switch (b.kind) {
|
|
29232
29233
|
case "paragraph":
|
|
29233
29234
|
return /* @__PURE__ */ jsx7("p", { ...tid(env, b.id, b.kind), style: {
|
|
29234
|
-
...textStyle(ctx, "body",
|
|
29235
|
+
...textStyle(ctx, "body", text8),
|
|
29235
29236
|
textAlign: b.align,
|
|
29236
29237
|
paddingInlineStart: b.indent ? Math.min(b.indent, 8) * 16 : void 0,
|
|
29237
29238
|
minHeight: b.spans.length ? void 0 : "1.5em"
|
|
@@ -29239,7 +29240,7 @@ function Block({ b, env, depth }) {
|
|
|
29239
29240
|
case "heading":
|
|
29240
29241
|
return React6.createElement(
|
|
29241
29242
|
`h${b.level}`,
|
|
29242
|
-
{ ...tid(env, b.id, b.kind), style: textStyle(ctx, RICH_CONTENT_HEADING_TOKENS[b.level - 1],
|
|
29243
|
+
{ ...tid(env, b.id, b.kind), style: textStyle(ctx, RICH_CONTENT_HEADING_TOKENS[b.level - 1], text8) },
|
|
29243
29244
|
/* @__PURE__ */ jsx7(Spans, { spans: b.spans, env })
|
|
29244
29245
|
);
|
|
29245
29246
|
case "list": {
|
|
@@ -29249,7 +29250,7 @@ function Block({ b, env, depth }) {
|
|
|
29249
29250
|
{
|
|
29250
29251
|
...tid(env, b.id, b.kind),
|
|
29251
29252
|
start: b.ordered ? b.start : void 0,
|
|
29252
|
-
style: { ...textStyle(ctx, "body",
|
|
29253
|
+
style: { ...textStyle(ctx, "body", text8), paddingInlineStart: 24, listStyleType: b.ordered ? "decimal" : depth % 2 ? "circle" : "disc" },
|
|
29253
29254
|
children: b.items.map((item, i) => /* @__PURE__ */ jsx7("li", { "data-component-id": `${env.componentId}/${item.id}`, id: `${env.componentId}/${item.id}`, style: { marginBottom: 6 }, children: /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: item.blocks.map((child, j) => /* @__PURE__ */ jsx7(Block, { b: child, env, depth: depth + 1 }, `${j}:${child.id}`)) }) }, `${i}:${item.id}`))
|
|
29254
29255
|
}
|
|
29255
29256
|
);
|
|
@@ -29263,7 +29264,7 @@ function Block({ b, env, depth }) {
|
|
|
29263
29264
|
overflowWrap: "anywhere",
|
|
29264
29265
|
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
29265
29266
|
fontSize: "0.9em",
|
|
29266
|
-
color:
|
|
29267
|
+
color: text8,
|
|
29267
29268
|
background: ctx.color("surfaceSecondary", "#f5f5f7"),
|
|
29268
29269
|
padding: 10,
|
|
29269
29270
|
borderRadius: 6
|
|
@@ -29282,11 +29283,11 @@ function Block({ b, env, depth }) {
|
|
|
29282
29283
|
case "button":
|
|
29283
29284
|
return /* @__PURE__ */ jsx7(LinkButton, { label: b.label, url: b.url, env, testId: `${env.componentId}/${b.id}` });
|
|
29284
29285
|
case "table":
|
|
29285
|
-
return /* @__PURE__ */ jsx7("table", { ...tid(env, b.id, b.kind), style: { width: "100%", borderCollapse: "collapse", ...textStyle(ctx, "body",
|
|
29286
|
+
return /* @__PURE__ */ jsx7("table", { ...tid(env, b.id, b.kind), style: { width: "100%", borderCollapse: "collapse", ...textStyle(ctx, "body", text8) }, children: /* @__PURE__ */ jsx7("tbody", { children: b.rows.map((row, r) => /* @__PURE__ */ jsx7("tr", { children: row.map((cell, c) => /* @__PURE__ */ jsx7("td", { style: { border: `1px solid ${border}`, padding: 6, whiteSpace: "pre-wrap", verticalAlign: "top" }, children: cell }, c)) }, r)) }) });
|
|
29286
29287
|
case "card": {
|
|
29287
29288
|
const title = richContentCardTitle(b);
|
|
29288
29289
|
return /* @__PURE__ */ jsxs4("div", { ...tid(env, b.id, b.kind), role: "group", "aria-label": title, style: { border: `1px solid ${border}`, borderRadius: 8, padding: 12, display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
29289
|
-
/* @__PURE__ */ jsx7("div", { style: textStyle(ctx, "bodyEmphasis",
|
|
29290
|
+
/* @__PURE__ */ jsx7("div", { style: textStyle(ctx, "bodyEmphasis", text8), children: title }),
|
|
29290
29291
|
b.url ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
29291
29292
|
/* @__PURE__ */ jsx7("div", { style: textStyle(ctx, "caption", secondary), children: richContentHost(b.url) }),
|
|
29292
29293
|
/* @__PURE__ */ jsx7(LinkButton, { label: RICH_CONTENT_OPEN_LABEL, url: b.url, env, testId: `${env.componentId}/${b.id}/open` })
|
|
@@ -30882,8 +30883,8 @@ function applyA11ySemantics(node, ctx, extra) {
|
|
|
30882
30883
|
extra["aria-live"] = str9(announce.politeness) === "assertive" ? "assertive" : "polite";
|
|
30883
30884
|
extra["aria-atomic"] = "true";
|
|
30884
30885
|
extra["aria-relevant"] = trigger === "on_appear" ? "additions" : trigger === "both" ? "additions text" : "text";
|
|
30885
|
-
const
|
|
30886
|
-
if (
|
|
30886
|
+
const text8 = str9(announce.text);
|
|
30887
|
+
if (text8 && extra["aria-label"] == null) extra["aria-label"] = text8;
|
|
30887
30888
|
}
|
|
30888
30889
|
}
|
|
30889
30890
|
return hint;
|
|
@@ -32473,13 +32474,13 @@ var init_renderNode = __esm({
|
|
|
32473
32474
|
const C = ctx.color;
|
|
32474
32475
|
const isBoundText = typeof props.textPath === "string" && isBinding(props.textPath);
|
|
32475
32476
|
const bound = useResolvedScalar(isBoundText ? props.textPath : void 0, ctx);
|
|
32476
|
-
const
|
|
32477
|
+
const text8 = isBoundText ? typeof bound === "string" || typeof bound === "number" ? String(bound) : "" : literalErrorText;
|
|
32477
32478
|
if (props.visible === false) return null;
|
|
32478
|
-
if (isBoundText && !
|
|
32479
|
+
if (isBoundText && !text8) return null;
|
|
32479
32480
|
const c = (props.severity ?? "error") === "warning" ? C("warning", "#E89F00") : C("danger", "#E62214");
|
|
32480
32481
|
return /* @__PURE__ */ jsxs5("div", { role: "alert", style: { display: "flex", gap: 6, alignItems: "center", color: c, fontSize: 13, fontFamily: bodyFont(ctx), ...wrap }, children: [
|
|
32481
32482
|
/* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "\u26A0" }),
|
|
32482
|
-
|
|
32483
|
+
text8
|
|
32483
32484
|
] });
|
|
32484
32485
|
});
|
|
32485
32486
|
FormView = withExtras("FormView", ({ props, ctx, wrap }) => {
|
|
@@ -33966,8 +33967,8 @@ var init_renderNode = __esm({
|
|
|
33966
33967
|
const base = invalidIcon ? ICON_FALLBACK : (typeof props.icon === "string" && props.icon ? props.icon : "star").replace(/_fill$/, "");
|
|
33967
33968
|
const filledCount = Math.round(val);
|
|
33968
33969
|
const onClickStar = (i) => ctx.onAction && props.interactive && props.onChange ? () => ctx.onAction({ ...props.onChange, params: { ...props.onChange?.params ?? {}, value: String(i + 1) } }) : void 0;
|
|
33969
|
-
const cv = Number.isFinite(val) ? Math.max(0, Math.min(max, val)) : 0,
|
|
33970
|
-
return /* @__PURE__ */ jsx8("span", { style: { display: "inline-flex", alignItems: "center", gap: m.gap, ...wrap }, role: props.interactive ? "group" : "img", "aria-label": `Rating, ${
|
|
33970
|
+
const cv = Number.isFinite(val) ? Math.max(0, Math.min(max, val)) : 0, summary2 = `${Number.isInteger(cv) ? cv : cv.toFixed(1)} of ${max}`;
|
|
33971
|
+
return /* @__PURE__ */ jsx8("span", { style: { display: "inline-flex", alignItems: "center", gap: m.gap, ...wrap }, role: props.interactive ? "group" : "img", "aria-label": `Rating, ${summary2}`, "data-a11y-value": summary2, children: Array.from({ length: max }).map((_, i) => {
|
|
33971
33972
|
const filled = i < filledCount;
|
|
33972
33973
|
return /* @__PURE__ */ jsx8("span", { onClick: onClickStar(i), style: { display: "inline-grid", placeItems: "center", width: m.cell, height: m.cell, cursor: props.interactive ? "pointer" : "default", opacity: filled ? 1 : 0.28 }, children: iconSvg(filled ? `${base}_fill` : base, { size: m.glyph, color: col }) }, i);
|
|
33973
33974
|
}) });
|
|
@@ -35719,8 +35720,8 @@ function buildRenderTree(node, path2 = "$", provider = null, itemScope = void 0,
|
|
|
35719
35720
|
const asText3 = VALUE_PATH_TEXT.get(type);
|
|
35720
35721
|
if (typeof valuePath === "string" && asText3 && (valuePath.startsWith("$service:") || /^\$item\.[^.]/.test(valuePath))) {
|
|
35721
35722
|
const r = resolveTreeScalar(valuePath, provider, itemScope);
|
|
35722
|
-
const
|
|
35723
|
-
if (
|
|
35723
|
+
const text8 = asText3(r, props);
|
|
35724
|
+
if (text8 !== void 0) values.value = text8;
|
|
35724
35725
|
else if (r !== void 0 && r !== null) {
|
|
35725
35726
|
delete values.value;
|
|
35726
35727
|
}
|
|
@@ -35901,7 +35902,7 @@ async function stabilizePngPage(page, scheme) {
|
|
|
35901
35902
|
const browser = globalThis;
|
|
35902
35903
|
browser.document.documentElement.style.colorScheme = selected;
|
|
35903
35904
|
await browser.document.fonts?.ready;
|
|
35904
|
-
await new Promise((
|
|
35905
|
+
await new Promise((resolve53) => browser.requestAnimationFrame(() => browser.requestAnimationFrame(() => resolve53())));
|
|
35905
35906
|
}, scheme);
|
|
35906
35907
|
}
|
|
35907
35908
|
async function renderPngBatch(items) {
|
|
@@ -37915,14 +37916,14 @@ function discoverNativeActions(appDir3) {
|
|
|
37915
37916
|
const roots = NATIVE_SOURCE_ROOTS.filter(({ directory }) => existsSync38(resolve31(appDir3, directory)));
|
|
37916
37917
|
const sources = roots.flatMap(({ directory, extension, buildDirMaxDepth }) => nativeSourceFiles(resolve31(appDir3, directory), extension, buildDirMaxDepth));
|
|
37917
37918
|
const registered = sources.flatMap((source) => {
|
|
37918
|
-
let
|
|
37919
|
+
let text8;
|
|
37919
37920
|
try {
|
|
37920
|
-
|
|
37921
|
+
text8 = readFileSync40(source, "utf8");
|
|
37921
37922
|
} catch (err) {
|
|
37922
37923
|
if (!vanished(err)) throw err;
|
|
37923
37924
|
return [];
|
|
37924
37925
|
}
|
|
37925
|
-
return registeredActions(
|
|
37926
|
+
return registeredActions(text8);
|
|
37926
37927
|
});
|
|
37927
37928
|
return {
|
|
37928
37929
|
registered: new Set(registered),
|
|
@@ -38021,11 +38022,11 @@ function strip(line) {
|
|
|
38021
38022
|
function scalar(v) {
|
|
38022
38023
|
return v.trim().replace(/^["']|["']$/g, "");
|
|
38023
38024
|
}
|
|
38024
|
-
function parseRolloutYaml(
|
|
38025
|
+
function parseRolloutYaml(text8) {
|
|
38025
38026
|
const cfg = { app: "", rings: [] };
|
|
38026
38027
|
let cur = null;
|
|
38027
38028
|
let inRings = false;
|
|
38028
|
-
for (const raw of
|
|
38029
|
+
for (const raw of text8.split("\n")) {
|
|
38029
38030
|
const line = strip(raw);
|
|
38030
38031
|
if (!line.trim()) continue;
|
|
38031
38032
|
const top = /^([a-zA-Z_]+):\s*(.*)$/.exec(line);
|
|
@@ -38056,10 +38057,10 @@ function assignRing(r, key2, val) {
|
|
|
38056
38057
|
else if (key2 === "percent") r.percent = Number(v);
|
|
38057
38058
|
else if (key2 === "minBinaryVersion") r.minBinaryVersion = v;
|
|
38058
38059
|
}
|
|
38059
|
-
function parseRevocationsYaml(
|
|
38060
|
+
function parseRevocationsYaml(text8) {
|
|
38060
38061
|
const out2 = { app: "", revoked: [] };
|
|
38061
38062
|
let inList = false;
|
|
38062
|
-
for (const raw of
|
|
38063
|
+
for (const raw of text8.split("\n")) {
|
|
38063
38064
|
const line = strip(raw);
|
|
38064
38065
|
if (!line.trim()) continue;
|
|
38065
38066
|
const top = /^([a-zA-Z_]+):\s*(.*)$/.exec(line);
|
|
@@ -38416,7 +38417,7 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
|
|
|
38416
38417
|
state.app?.module.config,
|
|
38417
38418
|
state.tier5Dir ?? TIER5_ROOT2
|
|
38418
38419
|
);
|
|
38419
|
-
const
|
|
38420
|
+
const report3 = validateScreenDescriptor2(screenId, descriptor, {
|
|
38420
38421
|
tokens,
|
|
38421
38422
|
allowedComponents,
|
|
38422
38423
|
bundleJs,
|
|
@@ -38429,7 +38430,7 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
|
|
|
38429
38430
|
});
|
|
38430
38431
|
const telemetry = state.telemetryEvents.filter((e) => e.screenId === screenId && (/^worker_/.test(e.type) || e.type === "bundle_load_revoked")).slice(-20);
|
|
38431
38432
|
const lastError = [...telemetry].reverse().find((e) => /crash|revoked|error|fail/i.test(e.type)) ?? null;
|
|
38432
|
-
const legacy = { ...
|
|
38433
|
+
const legacy = { ...report3, bundle: bundleName ?? null, telemetry, lastError };
|
|
38433
38434
|
return foldLegacyReport(legacy, {
|
|
38434
38435
|
source: "diagnostics",
|
|
38435
38436
|
screenId,
|
|
@@ -38638,11 +38639,11 @@ async function handleOwnerChatRequest(req, res) {
|
|
|
38638
38639
|
return sendJson(res, 400, { error: "invalid_json" }, cors);
|
|
38639
38640
|
}
|
|
38640
38641
|
const chatroomId = (parsed.chatroomId ?? "").trim();
|
|
38641
|
-
const
|
|
38642
|
-
if (!chatroomId || !
|
|
38642
|
+
const text8 = (parsed.text ?? "").trim();
|
|
38643
|
+
if (!chatroomId || !text8) return sendJson(res, 400, { error: "chatroomId_and_text_required" }, cors);
|
|
38643
38644
|
const metaSiteId = (parsed.metaSiteId ?? "").trim() || await resolveMetaSiteId();
|
|
38644
38645
|
if (!metaSiteId) return sendJson(res, 400, { error: "no_meta_site" }, cors);
|
|
38645
|
-
const result = await sendChatMessageLive(metaSiteId, chatroomId,
|
|
38646
|
+
const result = await sendChatMessageLive(metaSiteId, chatroomId, text8);
|
|
38646
38647
|
if (result.ok) return sendJson(res, 200, { ok: true, message: result.message }, cors);
|
|
38647
38648
|
return sendJson(res, result.status >= 400 && result.status < 600 ? result.status : 502, { ok: false, status: result.status, error: result.error }, cors);
|
|
38648
38649
|
}
|
|
@@ -38903,13 +38904,13 @@ function invalidate(state, ...screenIds) {
|
|
|
38903
38904
|
publishSSE(state, "screen.invalidated", { screenIds });
|
|
38904
38905
|
}
|
|
38905
38906
|
function publishSSE(state, type, payload) {
|
|
38906
|
-
const
|
|
38907
|
+
const text8 = `event: ${type}
|
|
38907
38908
|
data: ${JSON.stringify(payload)}
|
|
38908
38909
|
|
|
38909
38910
|
`;
|
|
38910
38911
|
for (const res of [...state.sseClients]) {
|
|
38911
38912
|
try {
|
|
38912
|
-
res.write(
|
|
38913
|
+
res.write(text8);
|
|
38913
38914
|
} catch {
|
|
38914
38915
|
state.sseClients.delete(res);
|
|
38915
38916
|
}
|
|
@@ -38925,13 +38926,13 @@ function publishAdminSSE(state, type, payload) {
|
|
|
38925
38926
|
state.lastDeviceScreen.set(key2, screenId);
|
|
38926
38927
|
}
|
|
38927
38928
|
}
|
|
38928
|
-
const
|
|
38929
|
+
const text8 = `event: ${type}
|
|
38929
38930
|
data: ${JSON.stringify(payload)}
|
|
38930
38931
|
|
|
38931
38932
|
`;
|
|
38932
38933
|
for (const res of [...state.adminClients]) {
|
|
38933
38934
|
try {
|
|
38934
|
-
res.write(
|
|
38935
|
+
res.write(text8);
|
|
38935
38936
|
} catch {
|
|
38936
38937
|
state.adminClients.delete(res);
|
|
38937
38938
|
}
|
|
@@ -38952,12 +38953,12 @@ function publishDevReady(state, res, extra = {}) {
|
|
|
38952
38953
|
hotReloadEnabled: state.hotReloadEnabled,
|
|
38953
38954
|
...extra
|
|
38954
38955
|
};
|
|
38955
|
-
const
|
|
38956
|
+
const text8 = `event: dev.ready
|
|
38956
38957
|
data: ${JSON.stringify(payload)}
|
|
38957
38958
|
|
|
38958
38959
|
`;
|
|
38959
38960
|
if (res) {
|
|
38960
|
-
res.write(
|
|
38961
|
+
res.write(text8);
|
|
38961
38962
|
} else {
|
|
38962
38963
|
publishSSE(state, "dev.ready", payload);
|
|
38963
38964
|
}
|
|
@@ -39542,7 +39543,7 @@ async function handleWebWix(req, res, state) {
|
|
|
39542
39543
|
}
|
|
39543
39544
|
if (match) {
|
|
39544
39545
|
const mocked = match.rule.response;
|
|
39545
|
-
if (mocked.delayMs) await new Promise((
|
|
39546
|
+
if (mocked.delayMs) await new Promise((resolve53) => setTimeout(resolve53, mocked.delayMs));
|
|
39546
39547
|
const responseBody = mocked.template ? renderTemplate(mocked.body, match.captures) : mocked.body;
|
|
39547
39548
|
record2("mock", mocked.status, responseBody, match.rule.id, mocked.headers);
|
|
39548
39549
|
return reply(200, { status: mocked.status, body: responseBody ?? "" });
|
|
@@ -39708,7 +39709,7 @@ async function handleDev(req, res, state) {
|
|
|
39708
39709
|
idiomContext
|
|
39709
39710
|
);
|
|
39710
39711
|
const legacy = { screenId, scheme, ...render, diagnostics };
|
|
39711
|
-
const
|
|
39712
|
+
const report3 = foldLegacyReport(legacy, {
|
|
39712
39713
|
source: "render",
|
|
39713
39714
|
screenId,
|
|
39714
39715
|
ok: diagnostics.ok !== false && render.renderable !== false,
|
|
@@ -39720,8 +39721,8 @@ async function handleDev(req, res, state) {
|
|
|
39720
39721
|
],
|
|
39721
39722
|
evidence: { runId: `dev:${state.startedAt}:${screenId}`, ts: (/* @__PURE__ */ new Date()).toISOString() }
|
|
39722
39723
|
});
|
|
39723
|
-
const status = body.envelope === 1 && !
|
|
39724
|
-
return sendJson(res, status,
|
|
39724
|
+
const status = body.envelope === 1 && !report3.ok ? 422 : 200;
|
|
39725
|
+
return sendJson(res, status, report3);
|
|
39725
39726
|
}
|
|
39726
39727
|
{
|
|
39727
39728
|
const diagMatch = /^\/__dev\/screen\/(.+?)\/diagnostics(?:\?|$)/.exec(url);
|
|
@@ -40182,7 +40183,7 @@ async function handleAdminPlayground(req, res, state) {
|
|
|
40182
40183
|
state.app?.module.config,
|
|
40183
40184
|
state.tier5Dir ?? TIER5_ROOT2
|
|
40184
40185
|
);
|
|
40185
|
-
const
|
|
40186
|
+
const report3 = validateScreenDescriptor2(screenId, descriptor, {
|
|
40186
40187
|
tokens,
|
|
40187
40188
|
allowedComponents,
|
|
40188
40189
|
bundleJs,
|
|
@@ -40192,7 +40193,7 @@ async function handleAdminPlayground(req, res, state) {
|
|
|
40192
40193
|
workerSubtreeScreens: new Set(uiConfig(state.app?.module.config).workerSubtreeScreens ?? []),
|
|
40193
40194
|
a11y: uiConfig(state.app?.module.config).a11y
|
|
40194
40195
|
});
|
|
40195
|
-
return sendJson(res,
|
|
40196
|
+
return sendJson(res, report3.ok ? 200 : 422, report3);
|
|
40196
40197
|
}
|
|
40197
40198
|
const detailMatch = /^\/v1\/admin\/screens\/([^/]+)$/.exec(path2);
|
|
40198
40199
|
if (detailMatch && req.method === "GET") {
|
|
@@ -40647,9 +40648,9 @@ async function handleLiveSubscription(req, res, state) {
|
|
|
40647
40648
|
},
|
|
40648
40649
|
body: JSON.stringify({ ...body, app })
|
|
40649
40650
|
});
|
|
40650
|
-
const
|
|
40651
|
+
const text8 = await upstream.text();
|
|
40651
40652
|
res.writeHead(upstream.status, { "Content-Type": "application/json", ...cors });
|
|
40652
|
-
res.end(
|
|
40653
|
+
res.end(text8 || JSON.stringify({ ok: upstream.ok }));
|
|
40653
40654
|
} catch (error) {
|
|
40654
40655
|
sendJson(res, 502, { error: "admin_server_unreachable", detail: String(error) }, cors);
|
|
40655
40656
|
}
|
|
@@ -41641,12 +41642,12 @@ var init_query = __esm({
|
|
|
41641
41642
|
});
|
|
41642
41643
|
|
|
41643
41644
|
// ../../core/dev-server/src/crawl-input.ts
|
|
41644
|
-
function resolved(v,
|
|
41645
|
-
if (typeof v === "string") return v.startsWith("$service:") ?
|
|
41645
|
+
function resolved(v, resolve53) {
|
|
41646
|
+
if (typeof v === "string") return v.startsWith("$service:") ? resolve53(v) : v;
|
|
41646
41647
|
if (Array.isArray(v)) {
|
|
41647
41648
|
const out2 = v.flatMap((el) => {
|
|
41648
41649
|
if (typeof el !== "string" || !el.startsWith("$service:")) return [el];
|
|
41649
|
-
const r =
|
|
41650
|
+
const r = resolve53(el);
|
|
41650
41651
|
return Array.isArray(r) ? r : [r];
|
|
41651
41652
|
});
|
|
41652
41653
|
return out2;
|
|
@@ -41660,9 +41661,9 @@ function hasRowBinding(props) {
|
|
|
41660
41661
|
return itemToken(v) || Array.isArray(v) && v.some(itemToken);
|
|
41661
41662
|
});
|
|
41662
41663
|
}
|
|
41663
|
-
function declaredOptionIds(props,
|
|
41664
|
+
function declaredOptionIds(props, resolve53) {
|
|
41664
41665
|
const literal = props.options ?? props.tabs ?? props.chips;
|
|
41665
|
-
const bound = props.optionsPath !== void 0 ? resolved(props.optionsPath,
|
|
41666
|
+
const bound = props.optionsPath !== void 0 ? resolved(props.optionsPath, resolve53) : void 0;
|
|
41666
41667
|
const raw = Array.isArray(bound) ? bound : literal;
|
|
41667
41668
|
if (!Array.isArray(raw)) return [];
|
|
41668
41669
|
const out2 = [];
|
|
@@ -41705,24 +41706,24 @@ function seedTokens(state, limit = 48) {
|
|
|
41705
41706
|
walk2(state, 0);
|
|
41706
41707
|
return out2;
|
|
41707
41708
|
}
|
|
41708
|
-
function synthesizeInput(control,
|
|
41709
|
+
function synthesizeInput(control, resolve53, tokens) {
|
|
41709
41710
|
const id = `${control.type}.${control.callback}`;
|
|
41710
41711
|
const row = SYNTHESIZED_CONTROLS[id];
|
|
41711
41712
|
if (!row) return null;
|
|
41712
41713
|
const props = control.props;
|
|
41713
41714
|
if (hasRowBinding(props)) return null;
|
|
41714
41715
|
if (row.from === "declared-options") {
|
|
41715
|
-
const ids = declaredOptionIds(props,
|
|
41716
|
+
const ids = declaredOptionIds(props, resolve53);
|
|
41716
41717
|
if (!ids.length) return null;
|
|
41717
41718
|
if (row.encoding === "string-array") {
|
|
41718
|
-
const currentRaw2 = resolved(props.selectedIds,
|
|
41719
|
+
const currentRaw2 = resolved(props.selectedIds, resolve53);
|
|
41719
41720
|
const current3 = (Array.isArray(currentRaw2) ? currentRaw2 : []).filter((x) => typeof x === "string");
|
|
41720
41721
|
const pick4 = ids.find((o) => !current3.includes(o));
|
|
41721
41722
|
if (pick4 === void 0) return null;
|
|
41722
41723
|
const value = props.multiSelect === true ? [...current3, pick4] : [pick4];
|
|
41723
41724
|
return { key: row.key, value, from: row.from, current: current3, control: id };
|
|
41724
41725
|
}
|
|
41725
|
-
const currentRaw = resolved(props.selectedId,
|
|
41726
|
+
const currentRaw = resolved(props.selectedId, resolve53);
|
|
41726
41727
|
const current2 = typeof currentRaw === "string" ? currentRaw : void 0;
|
|
41727
41728
|
const pick3 = ids.find((o) => o !== current2);
|
|
41728
41729
|
if (pick3 === void 0) return null;
|
|
@@ -41730,7 +41731,7 @@ function synthesizeInput(control, resolve52, tokens) {
|
|
|
41730
41731
|
}
|
|
41731
41732
|
if (!acceptsWords(props) || !acceptsWords(control.wrapper ?? {})) return null;
|
|
41732
41733
|
const binding2 = typeof props.valuePath === "string" ? props.valuePath : typeof props.value === "string" && props.value.startsWith("$service:") ? props.value : void 0;
|
|
41733
|
-
const bound = binding2 === void 0 ? void 0 :
|
|
41734
|
+
const bound = binding2 === void 0 ? void 0 : resolve53(binding2);
|
|
41734
41735
|
const literal = typeof props.value === "string" && !props.value.startsWith("$") ? props.value : void 0;
|
|
41735
41736
|
const current = typeof bound === "string" ? bound : typeof bound === "number" ? String(bound) : literal ?? "";
|
|
41736
41737
|
const max = typeof props.maxLength === "number" && props.maxLength > 0 ? props.maxLength : void 0;
|
|
@@ -42094,10 +42095,10 @@ function netTransport(rules) {
|
|
|
42094
42095
|
if (res.needsUpstream) {
|
|
42095
42096
|
throw new Error(`net fixture rule ${res.ruleId} is a \`transform\` rule, which needs a real upstream \u2014 not usable in a hermetic crawl`);
|
|
42096
42097
|
}
|
|
42097
|
-
const { status, body:
|
|
42098
|
+
const { status, body: text8 } = res.resolved;
|
|
42098
42099
|
if (status < 200 || status >= 300) throw new Error(`Wix ${status} for ${method} ${bare}`);
|
|
42099
42100
|
try {
|
|
42100
|
-
return
|
|
42101
|
+
return text8 ? JSON.parse(text8) : {};
|
|
42101
42102
|
} catch {
|
|
42102
42103
|
return {};
|
|
42103
42104
|
}
|
|
@@ -42141,10 +42142,10 @@ function layeredTransport(layers2) {
|
|
|
42141
42142
|
});
|
|
42142
42143
|
if (res.matched) {
|
|
42143
42144
|
if (res.needsUpstream) throw new Error(`net fixture rule ${res.ruleId} is a \`transform\` rule, which needs a real upstream \u2014 not usable in a hermetic crawl`);
|
|
42144
|
-
const { status, body:
|
|
42145
|
+
const { status, body: text8 } = res.resolved;
|
|
42145
42146
|
if (status < 200 || status >= 300) throw new Error(`Wix ${status} for ${m} ${bare}`);
|
|
42146
42147
|
try {
|
|
42147
|
-
return
|
|
42148
|
+
return text8 ? JSON.parse(text8) : {};
|
|
42148
42149
|
} catch {
|
|
42149
42150
|
return {};
|
|
42150
42151
|
}
|
|
@@ -44166,13 +44167,13 @@ var init_build2 = __esm({
|
|
|
44166
44167
|
});
|
|
44167
44168
|
|
|
44168
44169
|
// ../mobile-dist/release-pin.ts
|
|
44169
|
-
function withPinnedRelease(
|
|
44170
|
-
if (!releaseId) return
|
|
44170
|
+
function withPinnedRelease(text8, releaseId) {
|
|
44171
|
+
if (!releaseId) return text8;
|
|
44171
44172
|
let cfg;
|
|
44172
44173
|
try {
|
|
44173
|
-
cfg = JSON.parse(
|
|
44174
|
+
cfg = JSON.parse(text8);
|
|
44174
44175
|
} catch {
|
|
44175
|
-
return
|
|
44176
|
+
return text8;
|
|
44176
44177
|
}
|
|
44177
44178
|
cfg.releaseId = releaseId;
|
|
44178
44179
|
cfg.releasePinned = true;
|
|
@@ -44290,10 +44291,10 @@ async function bakeCatalogs(app, locale, fetchCatalog, putDoc) {
|
|
|
44290
44291
|
if (!declared.length) return out2;
|
|
44291
44292
|
const tags = [.../* @__PURE__ */ new Set([...locale?.default ? [locale.default] : [], ...declared])];
|
|
44292
44293
|
for (const tag of tags) {
|
|
44293
|
-
const
|
|
44294
|
-
if (!
|
|
44295
|
-
const hash = sha2564(
|
|
44296
|
-
await putDoc(app, hash,
|
|
44294
|
+
const text8 = await fetchCatalog(tag);
|
|
44295
|
+
if (!text8) continue;
|
|
44296
|
+
const hash = sha2564(text8);
|
|
44297
|
+
await putDoc(app, hash, text8);
|
|
44297
44298
|
out2[tag] = hash;
|
|
44298
44299
|
}
|
|
44299
44300
|
return out2;
|
|
@@ -44385,9 +44386,9 @@ async function snapshotTokens(app, base, fetchTokens, put) {
|
|
|
44385
44386
|
}
|
|
44386
44387
|
};
|
|
44387
44388
|
}
|
|
44388
|
-
const
|
|
44389
|
-
await put(
|
|
44390
|
-
return { hash: sha2564(
|
|
44389
|
+
const text8 = await res.text();
|
|
44390
|
+
await put(text8);
|
|
44391
|
+
return { hash: sha2564(text8) };
|
|
44391
44392
|
}
|
|
44392
44393
|
function tokensSnapshotMessage(app, base, status) {
|
|
44393
44394
|
const why = status === 400 ? `the dev-server rejected the app id as malformed (400 bad_app). It must be lowercase letters, digits and hyphens, starting with a letter or digit, at most 63 characters, with at most one interior dot \u2014 so a leading underscore or an uppercase letter fails, on every build of that app. A single dot is FINE (an owner-namespaced slug), so do not remove one to get past this` : `${status} from the tokens route`;
|
|
@@ -44481,23 +44482,23 @@ function maximumBinaryVersion(current, candidate) {
|
|
|
44481
44482
|
}
|
|
44482
44483
|
return current;
|
|
44483
44484
|
}
|
|
44484
|
-
async function withStoreNavBundle(
|
|
44485
|
+
async function withStoreNavBundle(text8, address) {
|
|
44485
44486
|
let cfg;
|
|
44486
44487
|
try {
|
|
44487
|
-
cfg = JSON.parse(
|
|
44488
|
+
cfg = JSON.parse(text8);
|
|
44488
44489
|
} catch {
|
|
44489
|
-
return
|
|
44490
|
+
return text8;
|
|
44490
44491
|
}
|
|
44491
44492
|
const refs = [cfg?.navigation?.navLogicBundle, cfg?.navLogicBundle].filter(
|
|
44492
44493
|
(r) => !!r && typeof r === "object" && typeof r.url === "string"
|
|
44493
44494
|
);
|
|
44494
|
-
if (refs.length === 0) return
|
|
44495
|
+
if (refs.length === 0) return text8;
|
|
44495
44496
|
const resolved2 = [];
|
|
44496
44497
|
for (const ref of refs) {
|
|
44497
44498
|
const name = bundleNameFromUrl(ref.url);
|
|
44498
|
-
if (!name) return
|
|
44499
|
+
if (!name) return text8;
|
|
44499
44500
|
const addressed = await address(name);
|
|
44500
|
-
if (!addressed) return
|
|
44501
|
+
if (!addressed) return text8;
|
|
44501
44502
|
resolved2.push({ ref, url: addressed.url, sri: addressed.sri });
|
|
44502
44503
|
}
|
|
44503
44504
|
for (const { ref, url, sri: integrity2 } of resolved2) {
|
|
@@ -44506,10 +44507,10 @@ async function withStoreNavBundle(text6, address) {
|
|
|
44506
44507
|
}
|
|
44507
44508
|
return JSON.stringify(cfg);
|
|
44508
44509
|
}
|
|
44509
|
-
function linkableScreenIdsFrom(
|
|
44510
|
+
function linkableScreenIdsFrom(text8) {
|
|
44510
44511
|
let cfg;
|
|
44511
44512
|
try {
|
|
44512
|
-
cfg = JSON.parse(
|
|
44513
|
+
cfg = JSON.parse(text8);
|
|
44513
44514
|
} catch {
|
|
44514
44515
|
return [];
|
|
44515
44516
|
}
|
|
@@ -44521,24 +44522,24 @@ function linkableScreenIdsFrom(text6) {
|
|
|
44521
44522
|
}
|
|
44522
44523
|
return [...ids];
|
|
44523
44524
|
}
|
|
44524
|
-
function withoutDevOnlyAppConfig(
|
|
44525
|
+
function withoutDevOnlyAppConfig(text8) {
|
|
44525
44526
|
let cfg;
|
|
44526
44527
|
try {
|
|
44527
|
-
cfg = JSON.parse(
|
|
44528
|
+
cfg = JSON.parse(text8);
|
|
44528
44529
|
} catch {
|
|
44529
|
-
return
|
|
44530
|
+
return text8;
|
|
44530
44531
|
}
|
|
44531
|
-
if (!cfg || typeof cfg !== "object" || !("devGatewayConfig" in cfg)) return
|
|
44532
|
+
if (!cfg || typeof cfg !== "object" || !("devGatewayConfig" in cfg)) return text8;
|
|
44532
44533
|
delete cfg.devGatewayConfig;
|
|
44533
44534
|
return JSON.stringify(cfg);
|
|
44534
44535
|
}
|
|
44535
|
-
function withProdServeAppConfig(
|
|
44536
|
-
if (!prod?.serve) return
|
|
44536
|
+
function withProdServeAppConfig(text8, prod) {
|
|
44537
|
+
if (!prod?.serve) return text8;
|
|
44537
44538
|
let cfg;
|
|
44538
44539
|
try {
|
|
44539
|
-
cfg = JSON.parse(
|
|
44540
|
+
cfg = JSON.parse(text8);
|
|
44540
44541
|
} catch {
|
|
44541
|
-
return
|
|
44542
|
+
return text8;
|
|
44542
44543
|
}
|
|
44543
44544
|
cfg.useProdServe = true;
|
|
44544
44545
|
cfg.prodServeBaseUrl = prod.serve;
|
|
@@ -44759,10 +44760,10 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
44759
44760
|
if (bytes2.length === 0 || bytes2.length > MAX_AUTHORING_DOC_BYTES) {
|
|
44760
44761
|
throw new Error(`${file} must be non-empty and at most ${MAX_AUTHORING_DOC_BYTES} bytes to ship with a release.`);
|
|
44761
44762
|
}
|
|
44762
|
-
const
|
|
44763
|
-
if (!Buffer.from(
|
|
44763
|
+
const text8 = bytes2.toString("utf8");
|
|
44764
|
+
if (!Buffer.from(text8, "utf8").equals(bytes2)) throw new Error(`${file} must be canonical UTF-8 Markdown.`);
|
|
44764
44765
|
const hash = sha2564(bytes2);
|
|
44765
|
-
await store.putDoc(app, hash,
|
|
44766
|
+
await store.putDoc(app, hash, text8);
|
|
44766
44767
|
authoringDocs[name] = { sha256: hash, sri: sri2(bytes2), bytes: bytes2.length, contentType: "text/markdown; charset=utf-8" };
|
|
44767
44768
|
}
|
|
44768
44769
|
}
|
|
@@ -44961,7 +44962,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
44961
44962
|
}
|
|
44962
44963
|
if (collect2?.lint) {
|
|
44963
44964
|
const bundleJs = bundleName ? readBuiltBundle(tier5Dir, bundleName) : void 0;
|
|
44964
|
-
const
|
|
44965
|
+
const report3 = validateScreenDescriptor(screenId, descriptor, {
|
|
44965
44966
|
tokens: lintTokens,
|
|
44966
44967
|
allowedComponents,
|
|
44967
44968
|
allowedIcons: KNOWN_ICONS,
|
|
@@ -44975,7 +44976,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
44975
44976
|
// default, "error"; an in-repo app with ratcheted debt declares "warn".
|
|
44976
44977
|
a11y: config.a11y
|
|
44977
44978
|
});
|
|
44978
|
-
lint.push({ screenId, ok:
|
|
44979
|
+
lint.push({ screenId, ok: report3.ok, issues: report3.issues });
|
|
44979
44980
|
}
|
|
44980
44981
|
if (collect2?.render && (!collect2.render.screenId || collect2.render.screenId === screenId)) {
|
|
44981
44982
|
const { services, liveCalls, liveReturned, liveErrors } = await renderServicesFor(bundleName, descriptor);
|
|
@@ -44993,7 +44994,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
44993
44994
|
const bundleName = lb ? bundleNameFromUrl(lb.url) : null;
|
|
44994
44995
|
if (collect2?.lint) {
|
|
44995
44996
|
const bundleJs = bundleName ? readBuiltBundle(tier5Dir, bundleName) : void 0;
|
|
44996
|
-
const
|
|
44997
|
+
const report3 = validateScreenDescriptor(wantedRender, descriptor, {
|
|
44997
44998
|
tokens: lintTokens,
|
|
44998
44999
|
allowedComponents,
|
|
44999
45000
|
allowedIcons: KNOWN_ICONS,
|
|
@@ -45004,7 +45005,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
45004
45005
|
workerSubtreeScreens: new Set(config.workerSubtreeScreens ?? []),
|
|
45005
45006
|
a11y: config.a11y
|
|
45006
45007
|
});
|
|
45007
|
-
lint.push({ screenId: wantedRender, ok:
|
|
45008
|
+
lint.push({ screenId: wantedRender, ok: report3.ok, issues: report3.issues });
|
|
45008
45009
|
}
|
|
45009
45010
|
const { services, liveCalls, liveReturned, liveErrors } = await renderServicesFor(bundleName, descriptor);
|
|
45010
45011
|
const rr = await renderDescriptorWeb(descriptor, renderTokens, renderScheme, collect2.render.png === true, services, collect2.render.width, collect2.render.rtl === true);
|
|
@@ -45122,11 +45123,11 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
45122
45123
|
ok: res.ok,
|
|
45123
45124
|
status: res.status,
|
|
45124
45125
|
text: async () => {
|
|
45125
|
-
const
|
|
45126
|
+
const text8 = await res.text();
|
|
45126
45127
|
try {
|
|
45127
|
-
const doc = JSON.parse(
|
|
45128
|
+
const doc = JSON.parse(text8);
|
|
45128
45129
|
if (!publishedFontPlan) {
|
|
45129
|
-
if (!("fontFaces" in doc)) return
|
|
45130
|
+
if (!("fontFaces" in doc)) return text8;
|
|
45130
45131
|
const kept = publishableFontFaces(doc.fontFaces);
|
|
45131
45132
|
if (kept) doc.fontFaces = kept;
|
|
45132
45133
|
else delete doc.fontFaces;
|
|
@@ -45135,12 +45136,12 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
45135
45136
|
doc.fontFaces = servedProjection(publishedFontPlan);
|
|
45136
45137
|
return JSON.stringify(doc);
|
|
45137
45138
|
} catch {
|
|
45138
|
-
return
|
|
45139
|
+
return text8;
|
|
45139
45140
|
}
|
|
45140
45141
|
}
|
|
45141
45142
|
};
|
|
45142
45143
|
},
|
|
45143
|
-
(
|
|
45144
|
+
(text8) => store.putTokens(app, text8)
|
|
45144
45145
|
);
|
|
45145
45146
|
if (snapshot2.hash) tokensHash = snapshot2.hash;
|
|
45146
45147
|
if (snapshot2.failure) {
|
|
@@ -45170,14 +45171,14 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
45170
45171
|
if (streamPolicyErrors.length) {
|
|
45171
45172
|
throw new Error(`invalid streamPolicy for release ${app}: ${streamPolicyErrors.join("; ")}`);
|
|
45172
45173
|
}
|
|
45173
|
-
const
|
|
45174
|
+
const text8 = await withStoreNavBundle(prodText, async (name) => {
|
|
45174
45175
|
const hash = await addressBundle(name) ?? null;
|
|
45175
45176
|
const entry = hash ? bundles[name] : void 0;
|
|
45176
45177
|
return entry ? { url: store.bundleUrl(app, entry.sha256), sri: entry.sri } : null;
|
|
45177
45178
|
});
|
|
45178
|
-
await store.putAppConfig(app,
|
|
45179
|
-
appConfigHash = sha2564(
|
|
45180
|
-
if (collect2?.descriptors) for (const id of linkableScreenIdsFrom(
|
|
45179
|
+
await store.putAppConfig(app, text8);
|
|
45180
|
+
appConfigHash = sha2564(text8);
|
|
45181
|
+
if (collect2?.descriptors) for (const id of linkableScreenIdsFrom(text8)) linkable.add(id);
|
|
45181
45182
|
}
|
|
45182
45183
|
const ahRes = await devFetch(`${base}/v1/action-handlers/${encodeURIComponent(app)}`, { headers: { "X-Himalaya-App": app } });
|
|
45183
45184
|
if (ahRes.ok) {
|
|
@@ -46043,14 +46044,14 @@ function headline(changes) {
|
|
|
46043
46044
|
function boardLines(changes) {
|
|
46044
46045
|
const lines = [];
|
|
46045
46046
|
for (const c of changes.items) {
|
|
46046
|
-
const
|
|
46047
|
-
const same3 = lines.find((l) => l.text ===
|
|
46047
|
+
const text8 = lineOf(c);
|
|
46048
|
+
const same3 = lines.find((l) => l.text === text8);
|
|
46048
46049
|
if (same3) {
|
|
46049
46050
|
same3.n++;
|
|
46050
46051
|
continue;
|
|
46051
46052
|
}
|
|
46052
46053
|
lines.push({
|
|
46053
|
-
text:
|
|
46054
|
+
text: text8,
|
|
46054
46055
|
n: 1,
|
|
46055
46056
|
...c.kind === "token" && c.from && HEX.test(c.from) ? { from: c.from } : {},
|
|
46056
46057
|
...c.kind === "token" && c.to && HEX.test(c.to) ? { to: c.to } : {}
|
|
@@ -46370,12 +46371,12 @@ function testRuns(dir) {
|
|
|
46370
46371
|
if (!f.endsWith(".json") || f === "latest.json") continue;
|
|
46371
46372
|
const r = obj(readJson3(join41(att, f)));
|
|
46372
46373
|
if (!r || r.tool !== "himi-test" || typeof r.runId !== "string") continue;
|
|
46373
|
-
const
|
|
46374
|
+
const report3 = obj(r.report);
|
|
46374
46375
|
const coverage = obj(r.coverage);
|
|
46375
46376
|
runs.push({
|
|
46376
46377
|
runId: r.runId,
|
|
46377
|
-
ok:
|
|
46378
|
-
errors: num(obj(
|
|
46378
|
+
ok: report3?.ok === true,
|
|
46379
|
+
errors: num(obj(report3?.counts)?.errors),
|
|
46379
46380
|
screens: num(coverage?.screens),
|
|
46380
46381
|
actions: num(coverage?.actionsDispatched),
|
|
46381
46382
|
gaps: num(coverage?.gaps)
|
|
@@ -46494,7 +46495,7 @@ function focusScreen(memory) {
|
|
|
46494
46495
|
}
|
|
46495
46496
|
function buildView(dir, memory, now) {
|
|
46496
46497
|
const { entries } = readProgress(dir);
|
|
46497
|
-
const
|
|
46498
|
+
const summary2 = summarizeProgress(entries);
|
|
46498
46499
|
const { name, iconFile } = appIdentity(dir);
|
|
46499
46500
|
const iconArchived = iconFile ? archiveFile(dir, memory, iconFile) : null;
|
|
46500
46501
|
const icon = iconArchived ? historyUrl(iconArchived.file) : null;
|
|
@@ -46523,12 +46524,12 @@ function buildView(dir, memory, now) {
|
|
|
46523
46524
|
}
|
|
46524
46525
|
}
|
|
46525
46526
|
const firstShot = screens.map((s) => s.firstSeen).sort()[0];
|
|
46526
|
-
const startedAt = [
|
|
46527
|
+
const startedAt = [summary2.startedAt, firstShot, memory.createdAt].filter((x) => !!x).sort()[0] ?? null;
|
|
46527
46528
|
const view = {
|
|
46528
46529
|
generatedAt: now,
|
|
46529
46530
|
app: { name, icon, palette: palette(dir) },
|
|
46530
46531
|
steps: JOURNEY_STEPS,
|
|
46531
|
-
current:
|
|
46532
|
+
current: summary2.current ?? null,
|
|
46532
46533
|
startedAt,
|
|
46533
46534
|
journal: entries.slice(-MAX_JOURNAL).reverse().map((e) => ({
|
|
46534
46535
|
ts: e.ts,
|
|
@@ -47193,16 +47194,16 @@ function pushSetupBodyBytes(body) {
|
|
|
47193
47194
|
return Buffer.byteLength(JSON.stringify(body), "utf8");
|
|
47194
47195
|
}
|
|
47195
47196
|
async function pushSetupRequest(options) {
|
|
47196
|
-
const
|
|
47197
|
-
if (!options.target.controlToken) return
|
|
47198
|
-
if (!options.wixToken) return
|
|
47199
|
-
if (credentialHostRefusal(options.target.control)) return
|
|
47200
|
-
if (options.previewTag !== void 0 && !PREVIEW_TAG.test(options.previewTag)) return
|
|
47197
|
+
const refuse5 = (error) => ({ ok: false, status: 0, body: null, error });
|
|
47198
|
+
if (!options.target.controlToken) return refuse5("control_token_required");
|
|
47199
|
+
if (!options.wixToken) return refuse5("wix_account_token_required");
|
|
47200
|
+
if (credentialHostRefusal(options.target.control)) return refuse5("insecure_target");
|
|
47201
|
+
if (options.previewTag !== void 0 && !PREVIEW_TAG.test(options.previewTag)) return refuse5("invalid_preview_tag");
|
|
47201
47202
|
const setup = options.operation === "setup";
|
|
47202
|
-
if (setup && !options.body) return
|
|
47203
|
-
if (!setup && (!options.query?.app || !options.query?.site)) return
|
|
47203
|
+
if (setup && !options.body) return refuse5("push_setup_body_required");
|
|
47204
|
+
if (!setup && (!options.query?.app || !options.query?.site)) return refuse5("push_setup_query_required");
|
|
47204
47205
|
if (setup && pushSetupBodyBytes(options.body) > MAX_PUSH_SETUP_BODY_BYTES) {
|
|
47205
|
-
return
|
|
47206
|
+
return refuse5("push_setup_body_too_large");
|
|
47206
47207
|
}
|
|
47207
47208
|
const base = options.target.control.replace(/\/+$/, "");
|
|
47208
47209
|
let path2 = "/v1/admin/push/setup";
|
|
@@ -47227,10 +47228,10 @@ async function pushSetupRequest(options) {
|
|
|
47227
47228
|
signal: AbortSignal.timeout(options.timeoutMs ?? (setup ? 12e4 : 3e4)),
|
|
47228
47229
|
...setup ? { body: JSON.stringify(options.body) } : {}
|
|
47229
47230
|
});
|
|
47230
|
-
const
|
|
47231
|
+
const text8 = await response.text();
|
|
47231
47232
|
let parsed = null;
|
|
47232
47233
|
try {
|
|
47233
|
-
const value =
|
|
47234
|
+
const value = text8 ? JSON.parse(text8) : null;
|
|
47234
47235
|
parsed = value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
47235
47236
|
} catch {
|
|
47236
47237
|
parsed = null;
|
|
@@ -47270,6 +47271,41 @@ var init_push_setup_apple_credentials = __esm({
|
|
|
47270
47271
|
}
|
|
47271
47272
|
});
|
|
47272
47273
|
|
|
47274
|
+
// src/hosted-owner.ts
|
|
47275
|
+
async function hostedOwnerSession(flags, deps = {}) {
|
|
47276
|
+
let target;
|
|
47277
|
+
try {
|
|
47278
|
+
target = resolveTarget({ target: text3(flags.target), defaultTarget: "wix", controlUrl: text3(flags["control-url"]), controlToken: text3(flags["control-token"]), ownerIssueUrl: text3(flags["owner-issue-url"]) });
|
|
47279
|
+
} catch {
|
|
47280
|
+
return refused("unknown_target", "Select a configured serve target.");
|
|
47281
|
+
}
|
|
47282
|
+
if (credentialHostRefusal(target.control)) return refused("insecure_target");
|
|
47283
|
+
const previewTag = text3(flags["preview-tag"]) ?? process.env.HIMI_SERVE_PREVIEW_TAG;
|
|
47284
|
+
if (previewTag !== void 0 && !/^dp-[a-z0-9]+$/.test(previewTag)) return refused("invalid_preview_tag");
|
|
47285
|
+
const controlToken = target.controlToken;
|
|
47286
|
+
if (!controlToken || !decodeControlTokenClaims(controlToken)?.sub) return refused("owner_login_required", "Run himi login --browser --target wix.");
|
|
47287
|
+
let wixToken;
|
|
47288
|
+
try {
|
|
47289
|
+
wixToken = await (deps.wixToken ?? accountToken)();
|
|
47290
|
+
} catch {
|
|
47291
|
+
return refused("wix_account_token_required", "Browser login supplies a Himi control token only. Sign into the Wix CLI with wix login, using the same Wix account, then retry. You can continue building without push setup.", 1);
|
|
47292
|
+
}
|
|
47293
|
+
if (!wixToken) return refused("wix_account_token_required", "Run wix login using the same Wix account.", 1);
|
|
47294
|
+
if (!target.ownerIssue) return refused("owner_issuer_required");
|
|
47295
|
+
return { ok: true, session: { target, controlToken, wixToken, ownerIssue: target.ownerIssue, ...previewTag ? { previewTag } : {} } };
|
|
47296
|
+
}
|
|
47297
|
+
var text3, refused;
|
|
47298
|
+
var init_hosted_owner = __esm({
|
|
47299
|
+
"src/hosted-owner.ts"() {
|
|
47300
|
+
init_config();
|
|
47301
|
+
init_credential_hosts();
|
|
47302
|
+
init_owner_claim();
|
|
47303
|
+
init_wix_owner();
|
|
47304
|
+
text3 = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
47305
|
+
refused = (error, hint, exit = 2) => ({ ok: false, error, ...hint ? { hint } : {}, exit });
|
|
47306
|
+
}
|
|
47307
|
+
});
|
|
47308
|
+
|
|
47273
47309
|
// src/push-onboarding.ts
|
|
47274
47310
|
import { existsSync as existsSync44, mkdirSync as mkdirSync20, readFileSync as readFileSync48, statSync as statSync9, writeFileSync as writeFileSync15, chmodSync as chmodSync2 } from "node:fs";
|
|
47275
47311
|
import { basename as basename12, dirname as dirname25, join as join42, resolve as resolve37 } from "node:path";
|
|
@@ -47293,26 +47329,26 @@ function pushInputsFromContent(dir) {
|
|
|
47293
47329
|
// autoClaimSlug's docblock describes as the project-declared slug. Without the fallback a
|
|
47294
47330
|
// package with no `name` sent a body missing `app` and earned `400 invalid_app_id` with both
|
|
47295
47331
|
// private keys already on the wire.
|
|
47296
|
-
app:
|
|
47297
|
-
...
|
|
47298
|
-
...
|
|
47299
|
-
...
|
|
47332
|
+
app: text4(content.name) ?? basename12(root),
|
|
47333
|
+
...text4(provisioning.siteId) ? { site: text4(provisioning.siteId) } : {},
|
|
47334
|
+
...text4(native.iosBundleId) ? { bundleId: text4(native.iosBundleId) } : {},
|
|
47335
|
+
...text4(native.androidApplicationId) ? { androidApplicationId: text4(native.androidApplicationId) } : {}
|
|
47300
47336
|
};
|
|
47301
47337
|
}
|
|
47302
47338
|
function mergePushInputs(content, flags) {
|
|
47303
47339
|
const merged = { ...content };
|
|
47304
47340
|
for (const [flag, apply] of FLAG_SETTERS) {
|
|
47305
|
-
const value =
|
|
47341
|
+
const value = text4(flags[flag]);
|
|
47306
47342
|
if (value !== void 0) apply(merged, value);
|
|
47307
47343
|
}
|
|
47308
47344
|
return merged;
|
|
47309
47345
|
}
|
|
47310
47346
|
function missingPushInputs(i, scope) {
|
|
47311
47347
|
const needed = scope === "push_and_signing" ? [...REQUIRED_ORDER, ...ASC_ORDER] : REQUIRED_ORDER;
|
|
47312
|
-
return needed.filter((name) => !
|
|
47348
|
+
return needed.filter((name) => !text4(i[name]));
|
|
47313
47349
|
}
|
|
47314
47350
|
function ascGroupError(i) {
|
|
47315
|
-
const present = ASC_ORDER.filter((name) =>
|
|
47351
|
+
const present = ASC_ORDER.filter((name) => text4(i[name])).length;
|
|
47316
47352
|
return present === 0 || present === ASC_ORDER.length ? null : "incomplete_asc_group";
|
|
47317
47353
|
}
|
|
47318
47354
|
function localIdentityRefusal(i) {
|
|
@@ -47381,8 +47417,8 @@ function validateApnsAuthKey(body) {
|
|
|
47381
47417
|
return { ok: true };
|
|
47382
47418
|
}
|
|
47383
47419
|
function buildPushSetupBody(i, keys) {
|
|
47384
|
-
const missing = REQUIRED_ORDER.filter((name) => !
|
|
47385
|
-
const app =
|
|
47420
|
+
const missing = REQUIRED_ORDER.filter((name) => !text4(i[name]));
|
|
47421
|
+
const app = text4(i.app);
|
|
47386
47422
|
if (!app) return { ok: false, code: "app_not_initialized", missing };
|
|
47387
47423
|
if (missing.length > 0) return { ok: false, code: "needs_input", missing };
|
|
47388
47424
|
if (i.apnsEnvironment !== "development" && i.apnsEnvironment !== "production") {
|
|
@@ -47462,7 +47498,7 @@ function savePushSelection(dir, input) {
|
|
|
47462
47498
|
if (input.androidApplicationId) selection.androidApplicationId = input.androidApplicationId;
|
|
47463
47499
|
writeFile(dir, { ...readPushOnboarding(dir), selection });
|
|
47464
47500
|
}
|
|
47465
|
-
var PUSH_ONBOARDING_PATH, MAX_KEY_FILE_BYTES, SITE_RE, APP_RE, ANDROID_ID_RE, PLACEHOLDER_BUNDLE_RE,
|
|
47501
|
+
var PUSH_ONBOARDING_PATH, MAX_KEY_FILE_BYTES, SITE_RE, APP_RE, ANDROID_ID_RE, PLACEHOLDER_BUNDLE_RE, text4, FLAG_SETTERS, REQUIRED_ORDER, ASC_ORDER, NOTE;
|
|
47466
47502
|
var init_push_onboarding = __esm({
|
|
47467
47503
|
"src/push-onboarding.ts"() {
|
|
47468
47504
|
PUSH_ONBOARDING_PATH = ".himi/push-onboarding.json";
|
|
@@ -47471,7 +47507,7 @@ var init_push_onboarding = __esm({
|
|
|
47471
47507
|
APP_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
47472
47508
|
ANDROID_ID_RE = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/;
|
|
47473
47509
|
PLACEHOLDER_BUNDLE_RE = /^com\.example\.|^__HIMI_APP_/;
|
|
47474
|
-
|
|
47510
|
+
text4 = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
47475
47511
|
FLAG_SETTERS = [
|
|
47476
47512
|
["app", (m, v) => {
|
|
47477
47513
|
m.app = v;
|
|
@@ -47603,7 +47639,7 @@ async function runNotifySetup(pos, flags, io, deps = {}) {
|
|
|
47603
47639
|
if (operation !== "setup" && operation !== "status") return refuse2(io, "invalid_notify_operation");
|
|
47604
47640
|
const flagError = pushSetupFlagError(flags);
|
|
47605
47641
|
if (flagError) return refuse2(io, flagError);
|
|
47606
|
-
const dir = resolve38(
|
|
47642
|
+
const dir = resolve38(text5(flags.content) ?? process.cwd());
|
|
47607
47643
|
const local = readPushOnboarding(dir);
|
|
47608
47644
|
const progress = local.push;
|
|
47609
47645
|
const content = pushInputsFromContent(dir);
|
|
@@ -47635,26 +47671,10 @@ async function runNotifySetup(pos, flags, io, deps = {}) {
|
|
|
47635
47671
|
return 2;
|
|
47636
47672
|
}
|
|
47637
47673
|
if (operation === "setup" && ascGroupError(input)) return refuse2(io, "incomplete_asc_group");
|
|
47638
|
-
|
|
47639
|
-
|
|
47640
|
-
|
|
47641
|
-
|
|
47642
|
-
return refuse2(io, "unknown_target", "Select a configured serve target.");
|
|
47643
|
-
}
|
|
47644
|
-
if (credentialHostRefusal(target.control)) return refuse2(io, "insecure_target");
|
|
47645
|
-
const previewTag = text4(flags["preview-tag"]) ?? process.env.HIMI_SERVE_PREVIEW_TAG;
|
|
47646
|
-
if (previewTag !== void 0 && !/^dp-[a-z0-9]+$/.test(previewTag)) return refuse2(io, "invalid_preview_tag");
|
|
47647
|
-
const token = target.controlToken;
|
|
47648
|
-
if (!token || !decodeControlTokenClaims(token)?.sub) return refuse2(io, "owner_login_required", "Run himi login --browser --target wix.");
|
|
47649
|
-
let wixToken;
|
|
47650
|
-
try {
|
|
47651
|
-
wixToken = await (deps.wixToken ?? accountToken)();
|
|
47652
|
-
} catch {
|
|
47653
|
-
return refuse2(io, "wix_account_token_required", "Browser login supplies a Himi control token only. Sign into the Wix CLI with wix login, using the same Wix account, then retry. You can continue building without push setup.", 1);
|
|
47654
|
-
}
|
|
47655
|
-
if (!wixToken) return refuse2(io, "wix_account_token_required", "Run wix login using the same Wix account.", 1);
|
|
47656
|
-
if (!target.ownerIssue) return refuse2(io, "owner_issuer_required");
|
|
47657
|
-
const ownershipArgs = { ownerIssue: target.ownerIssue, controlToken: token, declaredName: input.app, fetchImpl: deps.fetchImpl };
|
|
47674
|
+
const owner = await hostedOwnerSession(flags, deps);
|
|
47675
|
+
if (!owner.ok) return refuse2(io, owner.error, owner.hint, owner.exit);
|
|
47676
|
+
const { target, controlToken: token, wixToken, previewTag, ownerIssue } = owner.session;
|
|
47677
|
+
const ownershipArgs = { ownerIssue, controlToken: token, declaredName: input.app, fetchImpl: deps.fetchImpl };
|
|
47658
47678
|
let ownership = await verifyAppOwnership(ownershipArgs);
|
|
47659
47679
|
logOwnershipFailure(io, ownership);
|
|
47660
47680
|
if (ownership.state !== "owned" && ownership.state !== "not_claimed") return refuse2(io, `app_ownership_${ownership.state}`, "Setup requires the app owner; retry after ownership is verified.", 1);
|
|
@@ -47700,7 +47720,7 @@ async function runNotifySetup(pos, flags, io, deps = {}) {
|
|
|
47700
47720
|
}
|
|
47701
47721
|
}
|
|
47702
47722
|
if (ownership.state === "not_claimed") {
|
|
47703
|
-
const claim = await autoClaimSlug({ ownerIssue
|
|
47723
|
+
const claim = await autoClaimSlug({ ownerIssue, controlToken: token, app: input.app, accountId: text5(flags["account-id"]), fetchImpl: deps.fetchImpl });
|
|
47704
47724
|
if (claim.fatal) return refuse2(io, "app_claim_refused", "Check app ownership and the selected account.", 1);
|
|
47705
47725
|
ownership = await verifyAppOwnership({ ...ownershipArgs, declaredName: claim.slug ?? input.app });
|
|
47706
47726
|
logOwnershipFailure(io, ownership);
|
|
@@ -47750,17 +47770,15 @@ async function offerPushSetup(options) {
|
|
|
47750
47770
|
return { status: "needs_attention", resume: resume(contentDir2) };
|
|
47751
47771
|
}
|
|
47752
47772
|
}
|
|
47753
|
-
var
|
|
47773
|
+
var text5, obj2, STATES, CHECKS, CHECK_STATES, STAGES, ERROR_CODES, PROMPTS, later, resume;
|
|
47754
47774
|
var init_notify_setup = __esm({
|
|
47755
47775
|
"src/notify-setup.ts"() {
|
|
47756
|
-
init_config();
|
|
47757
|
-
init_credential_hosts();
|
|
47758
47776
|
init_push_setup();
|
|
47759
47777
|
init_push_setup_apple_credentials();
|
|
47760
|
-
init_wix_owner();
|
|
47761
47778
|
init_owner_claim();
|
|
47779
|
+
init_hosted_owner();
|
|
47762
47780
|
init_push_onboarding();
|
|
47763
|
-
|
|
47781
|
+
text5 = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
47764
47782
|
obj2 = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
47765
47783
|
STATES = /* @__PURE__ */ new Set(["not_configured", "provisioning", "ready", "needs_attention"]);
|
|
47766
47784
|
CHECKS = ["brandedRecord", "pingApns", "pingFcm", "appleSigning", "nativeBuild", "himiRouting", "deviceDelivery"];
|
|
@@ -48455,7 +48473,7 @@ function thin(items, max) {
|
|
|
48455
48473
|
function planReel(dir) {
|
|
48456
48474
|
const memory = loadMemory2(dir);
|
|
48457
48475
|
const { entries } = readProgress(dir);
|
|
48458
|
-
const
|
|
48476
|
+
const summary2 = summarizeProgress(entries);
|
|
48459
48477
|
const manifest = readJson5(join45(dir, "himalaya.content.json"));
|
|
48460
48478
|
const tokens = readJson5(join45(dir, "tokens.json"));
|
|
48461
48479
|
const accentToken = tokens?.colors?.accentPrimary;
|
|
@@ -48475,9 +48493,9 @@ function planReel(dir) {
|
|
|
48475
48493
|
by: isCliEntry(e) ? CLI_AUTHOR : null,
|
|
48476
48494
|
files: (Array.isArray(e.images) ? e.images : []).map((i) => isObj3(i) && typeof i.file === "string" ? basename13(i.file) : "").filter((f) => HISTORY_NAME.test(f))
|
|
48477
48495
|
}));
|
|
48478
|
-
const times = [
|
|
48496
|
+
const times = [summary2.startedAt, ...timeline.map((t) => t.ts), memory?.createdAt].filter((x) => !!x).sort();
|
|
48479
48497
|
const startedAt = times[0] ?? null;
|
|
48480
|
-
const endedAt = [
|
|
48498
|
+
const endedAt = [summary2.lastAt, ...timeline.map((t) => t.ts)].filter((x) => !!x).sort().pop() ?? null;
|
|
48481
48499
|
return {
|
|
48482
48500
|
app: typeof manifest?.name === "string" && manifest.name ? manifest.name : basename13(dir),
|
|
48483
48501
|
accent,
|
|
@@ -48660,13 +48678,21 @@ __export(milestones_exports, {
|
|
|
48660
48678
|
CONSENT_PREFLIGHT_STEP: () => CONSENT_PREFLIGHT_STEP,
|
|
48661
48679
|
FIRST_LOOK_BUDGET_MS: () => FIRST_LOOK_BUDGET_MS,
|
|
48662
48680
|
INIT_SOURCE_FILE: () => INIT_SOURCE_FILE,
|
|
48681
|
+
INIT_WATCH_IDLE_MINUTES: () => INIT_WATCH_IDLE_MINUTES,
|
|
48663
48682
|
REMOTE_FIRST_LOOK_SKIP: () => REMOTE_FIRST_LOOK_SKIP,
|
|
48683
|
+
REMOTE_WATCH_SKIP: () => REMOTE_WATCH_SKIP,
|
|
48664
48684
|
STARTING_POINT_REF: () => STARTING_POINT_REF,
|
|
48685
|
+
STILL_BUILDING_AFTER_MS: () => STILL_BUILDING_AFTER_MS,
|
|
48686
|
+
STILL_BUILDING_MESSAGE: () => STILL_BUILDING_MESSAGE,
|
|
48687
|
+
WATCH_LOG: () => WATCH_LOG,
|
|
48665
48688
|
WATCH_REMINDER_EVERY_MS: () => WATCH_REMINDER_EVERY_MS,
|
|
48689
|
+
absoluteLoaderArgs: () => absoluteLoaderArgs,
|
|
48666
48690
|
afterBrowserTestRun: () => afterBrowserTestRun,
|
|
48667
48691
|
afterDraftPush: () => afterDraftPush,
|
|
48692
|
+
afterHandoverPostcard: () => afterHandoverPostcard,
|
|
48668
48693
|
afterRenderPng: () => afterRenderPng,
|
|
48669
48694
|
afterTestRun: () => afterTestRun,
|
|
48695
|
+
defaultStartWatchDeps: () => defaultStartWatchDeps,
|
|
48670
48696
|
displayPath: () => displayPath,
|
|
48671
48697
|
doNowBlock: () => doNowBlock,
|
|
48672
48698
|
draftPictureGap: () => draftPictureGap,
|
|
@@ -48675,21 +48701,25 @@ __export(milestones_exports, {
|
|
|
48675
48701
|
homeShotForPostcard: () => homeShotForPostcard,
|
|
48676
48702
|
homeShotPath: () => homeShotPath,
|
|
48677
48703
|
initDivergence: () => initDivergence,
|
|
48704
|
+
liveWatch: () => liveWatch,
|
|
48678
48705
|
liveWatchRunning: () => liveWatchRunning,
|
|
48679
48706
|
openBoardForSession: () => openBoardForSession,
|
|
48680
48707
|
progressCommand: () => progressCommand,
|
|
48681
48708
|
recordInitSource: () => recordInitSource,
|
|
48682
48709
|
sessionSteps: () => sessionSteps,
|
|
48710
|
+
startWatchAtInit: () => startWatchAtInit,
|
|
48683
48711
|
startingPointMessage: () => startingPointMessage,
|
|
48684
48712
|
testChangeNote: () => testChangeNote,
|
|
48685
48713
|
testPostcardCounts: () => testPostcardCounts,
|
|
48686
48714
|
testPostcardMessage: () => testPostcardMessage,
|
|
48687
48715
|
watchReminder: () => watchReminder,
|
|
48716
|
+
watchUrlFor: () => watchUrlFor,
|
|
48688
48717
|
writeHandoverReel: () => writeHandoverReel
|
|
48689
48718
|
});
|
|
48690
48719
|
import { createHash as createHash26, randomUUID as randomUUID3 } from "node:crypto";
|
|
48691
48720
|
import { spawn as spawn5 } from "node:child_process";
|
|
48692
|
-
import { existsSync as existsSync47, lstatSync as lstatSync11, readFileSync as readFileSync52, statSync as statSync11 } from "node:fs";
|
|
48721
|
+
import { closeSync as closeSync3, existsSync as existsSync47, lstatSync as lstatSync11, readFileSync as readFileSync52, statSync as statSync11 } from "node:fs";
|
|
48722
|
+
import { createServer as createServer4 } from "node:net";
|
|
48693
48723
|
import { basename as basename14, dirname as dirname27, isAbsolute as isAbsolute4, join as join46, relative as relative9, resolve as resolve41, sep as sep12 } from "node:path";
|
|
48694
48724
|
function homeScreenIdOf(dir) {
|
|
48695
48725
|
let src;
|
|
@@ -48765,9 +48795,9 @@ async function firstLookAtInit(dir, opts) {
|
|
|
48765
48795
|
child.on("error", (e) => finish2({ skipped: `the render did not start: ${e.message}` }));
|
|
48766
48796
|
child.on("close", (code) => {
|
|
48767
48797
|
clearTimeout(hardKill);
|
|
48768
|
-
let
|
|
48798
|
+
let report3 = null;
|
|
48769
48799
|
try {
|
|
48770
|
-
|
|
48800
|
+
report3 = JSON.parse(Buffer.concat(out2).toString("utf8"));
|
|
48771
48801
|
} catch {
|
|
48772
48802
|
}
|
|
48773
48803
|
let fresh = false;
|
|
@@ -48776,7 +48806,7 @@ async function firstLookAtInit(dir, opts) {
|
|
|
48776
48806
|
} catch {
|
|
48777
48807
|
}
|
|
48778
48808
|
if (code === 0 && fresh) return finish2({ image: `.himi/shots/${home}.png` });
|
|
48779
|
-
if (
|
|
48809
|
+
if (report3?.pngUnavailable) return finish2({ skipped: "no headless browser to draw the PNG \u2014 `npx playwright install chromium`, then run the render step below" });
|
|
48780
48810
|
let refusal = null;
|
|
48781
48811
|
for (const line of errTail.trim().split("\n").reverse()) {
|
|
48782
48812
|
try {
|
|
@@ -48786,13 +48816,14 @@ async function firstLookAtInit(dir, opts) {
|
|
|
48786
48816
|
}
|
|
48787
48817
|
if (refusal) break;
|
|
48788
48818
|
}
|
|
48789
|
-
const why = typeof
|
|
48819
|
+
const why = typeof report3?.reason === "string" ? report3.reason : typeof report3?.error === "string" ? report3.error : refusal;
|
|
48790
48820
|
finish2({ skipped: `the render made no picture (exit ${code ?? "killed"})${why ? `: ${why.slice(0, 200)}` : ""}` });
|
|
48791
48821
|
});
|
|
48792
48822
|
});
|
|
48793
48823
|
}
|
|
48794
48824
|
function sessionSteps(opts) {
|
|
48795
48825
|
const content = displayPath(opts.dir, opts.cwd);
|
|
48826
|
+
const running = watchRunning(opts.watch);
|
|
48796
48827
|
const home = opts.home ?? "<homeScreenId>";
|
|
48797
48828
|
const shot = `.himi/shots/${home}.png`;
|
|
48798
48829
|
const when = opts.source === "remote" ? "the first picture, once you have read the template's dev/, config.ts and tier5-src/ (it came off the serve plane, and a render runs its dev/index.ts on this machine)" : "the first picture, now";
|
|
@@ -48802,7 +48833,7 @@ function sessionSteps(opts) {
|
|
|
48802
48833
|
`${progressCommand({ what: "where the build starts", step: "implement", image: shot, content })} \u2014 your postcard for the owner, with that picture`
|
|
48803
48834
|
],
|
|
48804
48835
|
CONSENT_PREFLIGHT_STEP,
|
|
48805
|
-
`himi run --watch${contentArg(content)} & \u2014 after the consent preflight: keep it running in the background. The live preview repaints on every save and serves the build board at <its url>/__himi/studio/`
|
|
48836
|
+
running ? `the live preview is already running at ${running.url} \u2014 ${running.by === "init" ? `init started it in the background (pid ${running.pid}, log ${running.log}; it stops itself after ${INIT_WATCH_IDLE_MINUTES} minutes with no save)` : `pid ${running.pid}`}; do not start another. It repaints on every save and serves the build board at ${running.board}. Open it in a browser only after the consent preflight` : `himi run --watch${contentArg(content)} & \u2014 ${opts.source === "remote" ? "once you have read the template's code (init never starts it for a --remote template: the preview runs that code on this machine), and " : ""}after the consent preflight: keep it running in the background. The live preview repaints on every save and serves the build board at <its url>/__himi/studio/`
|
|
48806
48837
|
];
|
|
48807
48838
|
}
|
|
48808
48839
|
async function openBoardForSession(dir, cwd = process.cwd()) {
|
|
@@ -48819,13 +48850,32 @@ function journal(dir, milestone, message, extra) {
|
|
|
48819
48850
|
const r = journalMilestone(dir, { milestone, message, ...extra });
|
|
48820
48851
|
return r.journaled ? { journaled: true, milestone, message: r.entry.message } : void 0;
|
|
48821
48852
|
}
|
|
48853
|
+
function stillBuilding(dir, image, now) {
|
|
48854
|
+
try {
|
|
48855
|
+
const entries = readProgress(dir, { all: true }).entries;
|
|
48856
|
+
if (!entries.length) return void 0;
|
|
48857
|
+
const times = entries.map((e) => Date.parse(e.ts)).filter(Number.isFinite);
|
|
48858
|
+
if (!times.length || !(now.getTime() - Math.max(...times) > STILL_BUILDING_AFTER_MS)) return void 0;
|
|
48859
|
+
const sha = sha256Of(image);
|
|
48860
|
+
const latest = entries[entries.length - 1];
|
|
48861
|
+
if (Array.isArray(latest.images) && latest.images.some((i) => !!i && typeof i === "object" && i.sha256 === sha)) return void 0;
|
|
48862
|
+
return journal(dir, "still-building", STILL_BUILDING_MESSAGE, { ref: `home-${sha.slice(0, 16)}`, image, now });
|
|
48863
|
+
} catch {
|
|
48864
|
+
return void 0;
|
|
48865
|
+
}
|
|
48866
|
+
}
|
|
48867
|
+
function firstLookBeforeOwnPostcard(dir, image, now) {
|
|
48868
|
+
if (!image || !existsSync47(image) || initDivergence(dir).state !== "diverged") return void 0;
|
|
48869
|
+
return journal(dir, "first-look", yourFirstLookMessage(dir), { image, ...now ? { now } : {} });
|
|
48870
|
+
}
|
|
48822
48871
|
function afterRenderPng(dir, input) {
|
|
48823
48872
|
const isHome = input.renderable && input.homeScreenId !== null && input.screenId === input.homeScreenId;
|
|
48824
48873
|
const at2 = input.now ? { now: input.now } : {};
|
|
48825
48874
|
let postcard;
|
|
48826
48875
|
if (isHome) {
|
|
48827
48876
|
const d = initDivergence(dir);
|
|
48828
|
-
postcard = d.state === "unchanged" ? journal(dir, "first-look", startingPointMessage(d.template), { ref: STARTING_POINT_REF, image: input.pngFile, ...at2 }) : d.state === "diverged" ? journal(dir, "first-look",
|
|
48877
|
+
postcard = d.state === "unchanged" ? journal(dir, "first-look", startingPointMessage(d.template), { ref: STARTING_POINT_REF, image: input.pngFile, ...at2 }) : d.state === "diverged" ? journal(dir, "first-look", yourFirstLookMessage(dir), { image: input.pngFile, ...at2 }) : journal(dir, "first-look", `First look at ${appName(dir)}: the home screen, where the build starts.`, { image: input.pngFile, ...at2 });
|
|
48878
|
+
postcard ??= stillBuilding(dir, input.pngFile, input.now ?? /* @__PURE__ */ new Date());
|
|
48829
48879
|
}
|
|
48830
48880
|
return {
|
|
48831
48881
|
...postcard ? { postcard } : {},
|
|
@@ -48878,6 +48928,7 @@ function testChangeNote(headline2, change) {
|
|
|
48878
48928
|
return picture ? `Since the last crawl: ${picture}.` : void 0;
|
|
48879
48929
|
}
|
|
48880
48930
|
function afterTestRun(dir, input) {
|
|
48931
|
+
const firstLook = firstLookBeforeOwnPostcard(dir, input.homeShot, input.now);
|
|
48881
48932
|
const headline2 = testPostcardMessage(input);
|
|
48882
48933
|
const postcard = journal(dir, "test", headline2, {
|
|
48883
48934
|
ref: input.runId,
|
|
@@ -48888,6 +48939,7 @@ function afterTestRun(dir, input) {
|
|
|
48888
48939
|
});
|
|
48889
48940
|
return {
|
|
48890
48941
|
...postcard ? { postcard } : {},
|
|
48942
|
+
...firstLook ? { firstLook } : {},
|
|
48891
48943
|
next: [
|
|
48892
48944
|
input.browserScreen !== void 0 ? "this run crawled one screen in the browser; `himi test` without --browser crawls every screen and draws the build board's App map \u2014 how they link, and any broken link" : input.boardOpen ? "the build board's App map now shows every screen this crawl reached, how they link, and any broken link \u2014 .himi/studio/index.html, or /__himi/studio/ under `himi run --watch`" : "`himi studio` opens the build board, whose App map draws every screen this crawl reached, how they link, and any broken link"
|
|
48893
48945
|
]
|
|
@@ -48929,6 +48981,7 @@ function draftPictureGap(input) {
|
|
|
48929
48981
|
}
|
|
48930
48982
|
function afterDraftPush(dir, input) {
|
|
48931
48983
|
const current = !!input.homeShot && shotIsCurrent(input.homeShot, input.sourceChangedAtMs);
|
|
48984
|
+
const firstLook = current ? firstLookBeforeOwnPostcard(dir, input.homeShot, input.now) : void 0;
|
|
48932
48985
|
const gap = current || input.home === void 0 ? null : draftPictureGap(input);
|
|
48933
48986
|
const why = gap ? ` No picture: ${gap.reason}${gap.render ? ` \u2014 \`himi render ${shellQuote(input.home)} --png\` makes a current one` : ""}.` : "";
|
|
48934
48987
|
const postcard = journal(dir, "draft", `Draft pushed: release ${input.releaseId.slice(0, 12)} of ${input.app} \u2014 owner-private, not live.${why}`, {
|
|
@@ -48941,6 +48994,7 @@ function afterDraftPush(dir, input) {
|
|
|
48941
48994
|
const postcardLine = `${progressCommand({ what: "what the owner can review in this draft", step: "review", content: input.content })} \u2014 your postcard for the draft; share the preview link in chat, never in the journal. ${handover}`;
|
|
48942
48995
|
return {
|
|
48943
48996
|
...postcard ? { postcard } : {},
|
|
48997
|
+
...firstLook ? { firstLook } : {},
|
|
48944
48998
|
...reel ? { reel } : {},
|
|
48945
48999
|
...gap ? { picture: { skipped: gap.reason, ...gap.render ? { render: gap.render } : {} } } : {},
|
|
48946
49000
|
// With a render to run, it comes first: then the agent's postcard carries the current picture itself.
|
|
@@ -48989,7 +49043,7 @@ function initDivergence(dir) {
|
|
|
48989
49043
|
function startingPointMessage(template) {
|
|
48990
49044
|
return template ? `Starting point: the ${template} template, before any of your changes.` : "Starting point: the starter app, before any of your changes.";
|
|
48991
49045
|
}
|
|
48992
|
-
function homeShotForPostcard(dir) {
|
|
49046
|
+
function homeShotForPostcard(dir, opts = {}) {
|
|
48993
49047
|
const home = homeScreenIdOf(dir);
|
|
48994
49048
|
if (!home) return { skipped: "no literal homeScreenId in dev/index.ts, so there is no home shot to attach" };
|
|
48995
49049
|
const shot = homeShotPath(dir, home);
|
|
@@ -49003,6 +49057,7 @@ function homeShotForPostcard(dir) {
|
|
|
49003
49057
|
changed = void 0;
|
|
49004
49058
|
}
|
|
49005
49059
|
if (!shotIsCurrent(shot, changed)) return { skipped: `the home shot is older than the last source change \u2014 ${render} makes a current one` };
|
|
49060
|
+
if (opts.repeatOk) return { image: shot };
|
|
49006
49061
|
try {
|
|
49007
49062
|
const sha = sha256Of(shot);
|
|
49008
49063
|
const entries = readProgress(dir, { all: true }).entries;
|
|
@@ -49014,15 +49069,38 @@ function homeShotForPostcard(dir) {
|
|
|
49014
49069
|
}
|
|
49015
49070
|
return { image: shot };
|
|
49016
49071
|
}
|
|
49017
|
-
function
|
|
49072
|
+
function readInitWatch(dir) {
|
|
49073
|
+
const file = join46(dir, INIT_WATCH_FILE);
|
|
49074
|
+
try {
|
|
49075
|
+
if (!lstatSync11(file).isFile()) return null;
|
|
49076
|
+
const r = JSON.parse(readFileSync52(file, "utf8"));
|
|
49077
|
+
if (r.v !== 1 || typeof r.pid !== "number" || !Number.isInteger(r.pid) || r.pid < 1) return null;
|
|
49078
|
+
if (typeof r.url !== "string" || !/^http:\/\/127\.0\.0\.1:\d+\//.test(r.url) || typeof r.board !== "string" || typeof r.log !== "string" || typeof r.startedAt !== "string") return null;
|
|
49079
|
+
return r;
|
|
49080
|
+
} catch {
|
|
49081
|
+
return null;
|
|
49082
|
+
}
|
|
49083
|
+
}
|
|
49084
|
+
function liveWatch(dir, isAlive = pidAlive, now = /* @__PURE__ */ new Date()) {
|
|
49018
49085
|
const r = readRunRecord(dir);
|
|
49019
|
-
|
|
49086
|
+
if (r) {
|
|
49087
|
+
if (r.watching === true && isAlive(r.pid)) return { url: r.url, board: `${new URL(r.url).origin}/__himi/studio/`, pid: r.pid, by: "run" };
|
|
49088
|
+
return null;
|
|
49089
|
+
}
|
|
49090
|
+
const w = readInitWatch(dir);
|
|
49091
|
+
const age = w ? now.getTime() - Date.parse(w.startedAt) : Number.NaN;
|
|
49092
|
+
if (w && age >= 0 && age < INIT_WATCH_STARTING_MS && isAlive(w.pid)) return { url: w.url, board: w.board, pid: w.pid, by: "init", log: w.log };
|
|
49093
|
+
return null;
|
|
49094
|
+
}
|
|
49095
|
+
function liveWatchRunning(dir, isAlive = pidAlive, now) {
|
|
49096
|
+
return liveWatch(dir, isAlive, now) !== null;
|
|
49020
49097
|
}
|
|
49021
49098
|
function watchReminder(dir, opts = {}) {
|
|
49022
49099
|
try {
|
|
49023
49100
|
if (!existsSync47(join46(dir, "himalaya.content.json"))) return void 0;
|
|
49024
|
-
|
|
49025
|
-
const
|
|
49101
|
+
const nowDate = opts.now ?? /* @__PURE__ */ new Date();
|
|
49102
|
+
const live2 = liveWatch(dir, opts.isAlive, nowDate);
|
|
49103
|
+
const now = nowDate.getTime();
|
|
49026
49104
|
const file = join46(dir, REMINDERS_FILE);
|
|
49027
49105
|
let last = Number.NaN;
|
|
49028
49106
|
try {
|
|
@@ -49033,21 +49111,96 @@ function watchReminder(dir, opts = {}) {
|
|
|
49033
49111
|
if (!realDirIn(dir, ".himi")) return void 0;
|
|
49034
49112
|
writeFileAtomic(file, `${JSON.stringify({ watch: new Date(now).toISOString() })}
|
|
49035
49113
|
`);
|
|
49114
|
+
if (live2) {
|
|
49115
|
+
return `the live preview is running at ${live2.url} (pid ${live2.pid}${live2.by === "init" ? ", started by himi init" : ""}) \u2014 it repaints on every save, and the build board is ${live2.board}; give the owner that link to click around while you work, and do not start another`;
|
|
49116
|
+
}
|
|
49036
49117
|
const content = opts.content ?? displayPath(dir);
|
|
49037
49118
|
return `start the live preview and leave it running: \`himi run --watch --content ${shellQuote(content)} &\` (the consent preflight first, if a worker or config calls Wix at boot) \u2014 it repaints on every save; the build board is ${shellQuote(join46(content, ".himi", "studio", "index.html"))}, or /__himi/studio/ on the preview`;
|
|
49038
49119
|
} catch {
|
|
49039
49120
|
return void 0;
|
|
49040
49121
|
}
|
|
49041
49122
|
}
|
|
49123
|
+
function watchUrlFor(port, app) {
|
|
49124
|
+
const origin = `http://127.0.0.1:${port}`;
|
|
49125
|
+
return { url: `${origin}/preview/a/${encodeURIComponent(app)}`, board: `${origin}/__himi/studio/` };
|
|
49126
|
+
}
|
|
49127
|
+
function absoluteLoaderArgs(args, resolveSpec = (spec) => import.meta.resolve(spec)) {
|
|
49128
|
+
const bare = (v) => !/^(?:\.{1,2}[\\/]|\/|[A-Za-z]:[\\/]|file:|data:|node:)/.test(v);
|
|
49129
|
+
const fix = (v) => {
|
|
49130
|
+
if (!bare(v)) return v;
|
|
49131
|
+
try {
|
|
49132
|
+
return resolveSpec(v);
|
|
49133
|
+
} catch {
|
|
49134
|
+
return v;
|
|
49135
|
+
}
|
|
49136
|
+
};
|
|
49137
|
+
const out2 = [];
|
|
49138
|
+
for (let i = 0; i < args.length; i++) {
|
|
49139
|
+
const m = /^(--(?:import|loader|experimental-loader|require))=(.*)$/.exec(args[i]);
|
|
49140
|
+
if (m) out2.push(`${m[1]}=${fix(m[2])}`);
|
|
49141
|
+
else if (/^(--import|--loader|--experimental-loader|--require|-r)$/.test(args[i]) && i + 1 < args.length) out2.push(args[i], fix(args[++i]));
|
|
49142
|
+
else out2.push(args[i]);
|
|
49143
|
+
}
|
|
49144
|
+
return out2;
|
|
49145
|
+
}
|
|
49146
|
+
async function startWatchAtInit(dir, opts) {
|
|
49147
|
+
if (opts.source !== "bundled") return { skipped: REMOTE_WATCH_SKIP };
|
|
49148
|
+
if (opts.noWatch) return { skipped: "--no-watch" };
|
|
49149
|
+
const env = opts.env ?? process.env;
|
|
49150
|
+
if (ciSet(env)) return { skipped: "the CI environment variable is set, so nothing is left running after init" };
|
|
49151
|
+
if (env.NODE_TEST_CONTEXT) return { skipped: "running under `node --test`, so nothing is left running after init" };
|
|
49152
|
+
const deps = { ...defaultStartWatchDeps, ...opts.deps };
|
|
49153
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
49154
|
+
const live2 = liveWatch(dir, deps.isAlive, now);
|
|
49155
|
+
if (live2 && (live2.by === "init" || await deps.previewRunning(dir))) {
|
|
49156
|
+
return { skipped: `a live preview of this app is already running (pid ${live2.pid})`, url: live2.url, board: live2.board, pid: live2.pid };
|
|
49157
|
+
}
|
|
49158
|
+
try {
|
|
49159
|
+
const port = await deps.freePort(previewPortForDir(dir));
|
|
49160
|
+
const { url, board } = watchUrlFor(port, opts.app);
|
|
49161
|
+
const { fd } = openPreviewLog(dir, "watch.log");
|
|
49162
|
+
let child;
|
|
49163
|
+
try {
|
|
49164
|
+
child = deps.spawn(
|
|
49165
|
+
process.execPath,
|
|
49166
|
+
[...absoluteLoaderArgs(opts.loader ?? []), opts.entry, "run", "--watch", "--content", dir, "--port", String(port), "--no-open", "--idle-exit", String(INIT_WATCH_IDLE_MINUTES), "--skip-version-check"],
|
|
49167
|
+
{ cwd: dir, detached: true, stdio: ["ignore", fd, fd], env: { ...env, HIMI_NO_UPDATE_CHECK: "1" } }
|
|
49168
|
+
);
|
|
49169
|
+
} finally {
|
|
49170
|
+
closeSync3(fd);
|
|
49171
|
+
}
|
|
49172
|
+
child.once("error", () => {
|
|
49173
|
+
});
|
|
49174
|
+
child.unref();
|
|
49175
|
+
if (!child.pid) return { skipped: "the live preview did not start (no process) \u2014 start it yourself: `himi run --watch --content <dir> &`" };
|
|
49176
|
+
const record2 = { v: 1, pid: child.pid, url, board, log: WATCH_LOG, startedAt: now.toISOString() };
|
|
49177
|
+
if (realDirIn(dir, ".himi")) writeFileAtomic(join46(dir, INIT_WATCH_FILE), `${JSON.stringify(record2, null, 2)}
|
|
49178
|
+
`);
|
|
49179
|
+
return { started: true, pid: child.pid, url, board, log: WATCH_LOG };
|
|
49180
|
+
} catch (e) {
|
|
49181
|
+
return { skipped: `the live preview did not start: ${e.message.split("\n")[0]}` };
|
|
49182
|
+
}
|
|
49183
|
+
}
|
|
49184
|
+
function watchRunning(w) {
|
|
49185
|
+
if (!w) return null;
|
|
49186
|
+
if ("started" in w) return { url: w.url, board: w.board, pid: w.pid, log: w.log, by: "init" };
|
|
49187
|
+
return w.url && w.board && w.pid ? { url: w.url, board: w.board, pid: w.pid, by: "run" } : null;
|
|
49188
|
+
}
|
|
49042
49189
|
function doNowBlock(opts) {
|
|
49043
49190
|
const content = displayPath(opts.dir, opts.cwd);
|
|
49044
49191
|
const home = opts.home ?? "<homeScreenId>";
|
|
49045
49192
|
const board = join46(content, ".himi", "studio", "index.html");
|
|
49046
|
-
const
|
|
49193
|
+
const running = watchRunning(opts.watch);
|
|
49194
|
+
const who = running?.by === "init" ? "init started it" : "already running";
|
|
49195
|
+
const watch2 = running ? `${running.url} (${who}; the board: ${running.board}) \u2014 leave it running, and do not start another` : `\`himi run --watch${contentArg(content)} &\` and leave it running (the board: ${shellQuote(board)}, or /__himi/studio/ on the preview)`;
|
|
49047
49196
|
const postcard = `after each milestone, \`himi progress "<what changed>" --step <n>${contentArg(content)}\` \u2014 the current home picture attaches itself`;
|
|
49048
|
-
return opts.pictured ? [
|
|
49197
|
+
return opts.pictured ? [
|
|
49198
|
+
`1. Consent preflight: if a worker or config can call Wix or anything external at boot, authorize or mock it (none: go on).`,
|
|
49199
|
+
running ? `2. The live preview is already running at ${watch2}; open it in a browser only after step 1.` : `2. ${watch2}.`,
|
|
49200
|
+
`3. ${postcard[0].toUpperCase()}${postcard.slice(1)}.`
|
|
49201
|
+
] : [
|
|
49049
49202
|
`1. \`himi render ${shellQuote(home)} --png --device iphone${contentArg(content)}\` \u2014 the starting picture, offline${opts.source === "remote" ? " (read the template's dev/, config.ts and tier5-src/ first)" : ""}.`,
|
|
49050
|
-
`2. Consent preflight (authorize or mock any Wix or external startup request), then ${watch2}.`,
|
|
49203
|
+
running ? `2. Consent preflight (authorize or mock any Wix or external startup request), then open the live preview at ${watch2}.` : `2. Consent preflight (authorize or mock any Wix or external startup request), then ${watch2}.`,
|
|
49051
49204
|
`3. ${postcard[0].toUpperCase()}${postcard.slice(1)}.`
|
|
49052
49205
|
];
|
|
49053
49206
|
}
|
|
@@ -49062,7 +49215,20 @@ function writeHandoverReel(dir) {
|
|
|
49062
49215
|
return { skipped: `the making-of was not written: ${e.message.split("\n")[0]} \u2014 \`himi studio reel\` retries it` };
|
|
49063
49216
|
}
|
|
49064
49217
|
}
|
|
49065
|
-
|
|
49218
|
+
function afterHandoverPostcard(dir, opts = {}) {
|
|
49219
|
+
const file = join46(dir, ".himi", "making-of.html");
|
|
49220
|
+
let fresh = false;
|
|
49221
|
+
try {
|
|
49222
|
+
const st = lstatSync11(file);
|
|
49223
|
+
fresh = st.isFile() && !st.isSymbolicLink() && st.mtimeMs >= statSync11(join46(dir, ".himi", "progress.jsonl")).mtimeMs;
|
|
49224
|
+
} catch {
|
|
49225
|
+
fresh = false;
|
|
49226
|
+
}
|
|
49227
|
+
const makingOf = fresh ? { file, hint: `hand ${basename14(file)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` } : writeHandoverReel(dir);
|
|
49228
|
+
const next = "file" in makingOf ? [`hand the owner the making-of, ${shellQuote(displayPath(makingOf.file))}, beside the preview link \u2014 in your final message and report, never instead of the link`] : [`\`himi studio reel${contentArg(opts.content)}\` writes the making-of to hand the owner beside the preview link (${makingOf.skipped})`];
|
|
49229
|
+
return { makingOf, next };
|
|
49230
|
+
}
|
|
49231
|
+
var contentArg, CONSENT_PREFLIGHT, CONSENT_PREFLIGHT_STEP, REMOTE_FIRST_LOOK_SKIP, FIRST_LOOK_BUDGET_MS, plural, appName, STILL_BUILDING_AFTER_MS, STILL_BUILDING_MESSAGE, yourFirstLookMessage, INIT_SOURCE_FILE, STARTING_POINT_REF, TEMPLATE_NAME, sha256Of, WATCH_REMINDER_EVERY_MS, REMINDERS_FILE, pidAlive, INIT_WATCH_FILE, INIT_WATCH_STARTING_MS, INIT_WATCH_IDLE_MINUTES, WATCH_LOG, REMOTE_WATCH_SKIP, listenOn, defaultStartWatchDeps, ciSet;
|
|
49066
49232
|
var init_milestones = __esm({
|
|
49067
49233
|
"src/milestones.ts"() {
|
|
49068
49234
|
init_app_source();
|
|
@@ -49077,6 +49243,9 @@ var init_milestones = __esm({
|
|
|
49077
49243
|
FIRST_LOOK_BUDGET_MS = 9e4;
|
|
49078
49244
|
plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
49079
49245
|
appName = (dir) => appLabel(dir) ?? basename14(dir);
|
|
49246
|
+
STILL_BUILDING_AFTER_MS = 10 * 60 * 1e3;
|
|
49247
|
+
STILL_BUILDING_MESSAGE = "Still building: here's the home screen now.";
|
|
49248
|
+
yourFirstLookMessage = (dir) => `First look at your app: ${appName(dir)}'s home screen, with your changes.`;
|
|
49080
49249
|
INIT_SOURCE_FILE = join46(".himi", "init-source.json");
|
|
49081
49250
|
STARTING_POINT_REF = "starting-point";
|
|
49082
49251
|
TEMPLATE_NAME = /^[A-Za-z0-9._-]{1,64}$/;
|
|
@@ -49091,6 +49260,30 @@ var init_milestones = __esm({
|
|
|
49091
49260
|
return e.code === "EPERM";
|
|
49092
49261
|
}
|
|
49093
49262
|
};
|
|
49263
|
+
INIT_WATCH_FILE = join46(".himi", "watch.json");
|
|
49264
|
+
INIT_WATCH_STARTING_MS = 5 * 60 * 1e3;
|
|
49265
|
+
INIT_WATCH_IDLE_MINUTES = 30;
|
|
49266
|
+
WATCH_LOG = ".himi/watch.log";
|
|
49267
|
+
REMOTE_WATCH_SKIP = "the template came off the serve plane: the live preview builds and serves its publisher's code on this machine, so init never starts it for one \u2014 read its dev/, config.ts and tier5-src/ first, then start it yourself";
|
|
49268
|
+
listenOn = (port) => new Promise((done) => {
|
|
49269
|
+
const srv = createServer4();
|
|
49270
|
+
srv.once("error", () => done(null));
|
|
49271
|
+
srv.listen(port, "127.0.0.1", () => {
|
|
49272
|
+
const addr = srv.address();
|
|
49273
|
+
const got = typeof addr === "object" && addr ? addr.port : null;
|
|
49274
|
+
srv.close(() => done(got));
|
|
49275
|
+
});
|
|
49276
|
+
});
|
|
49277
|
+
defaultStartWatchDeps = {
|
|
49278
|
+
spawn: (cmd, args, opts) => spawn5(cmd, args, opts),
|
|
49279
|
+
isAlive: (pid) => pidAlive(pid),
|
|
49280
|
+
freePort: async (preferred) => await listenOn(preferred) ?? await listenOn(0) ?? preferred,
|
|
49281
|
+
previewRunning: async (dir) => (await previewStatus(dir)).running
|
|
49282
|
+
};
|
|
49283
|
+
ciSet = (env) => {
|
|
49284
|
+
const v = env.CI?.trim().toLowerCase();
|
|
49285
|
+
return !!v && v !== "0" && v !== "false";
|
|
49286
|
+
};
|
|
49094
49287
|
}
|
|
49095
49288
|
});
|
|
49096
49289
|
|
|
@@ -49578,9 +49771,9 @@ function bundlesOf(block) {
|
|
|
49578
49771
|
walk2(block);
|
|
49579
49772
|
return [...out2];
|
|
49580
49773
|
}
|
|
49581
|
-
function parseJson2(
|
|
49774
|
+
function parseJson2(text8, what) {
|
|
49582
49775
|
try {
|
|
49583
|
-
const value = JSON.parse(
|
|
49776
|
+
const value = JSON.parse(text8);
|
|
49584
49777
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("not an object");
|
|
49585
49778
|
return value;
|
|
49586
49779
|
} catch (e) {
|
|
@@ -49815,25 +50008,25 @@ function pruneChrome(items, removed, ledger) {
|
|
|
49815
50008
|
}
|
|
49816
50009
|
function rewriteNav(nav, removed, homeId) {
|
|
49817
50010
|
const next = structuredClone(nav);
|
|
49818
|
-
const
|
|
50011
|
+
const report3 = { linkable: [], tabs: [], drawer: [], sidebar: [], deepLinkPatterns: [], topBarActions: [], headerActions: [], surfaceDestinations: [], appIconBadgeSources: [], reparented: [] };
|
|
49819
50012
|
const tabItems = asItems(next.tabBar);
|
|
49820
50013
|
const hadTabs = !!tabItems?.length;
|
|
49821
|
-
if (tabItems) next.tabBar.items = pruneChrome(tabItems, removed,
|
|
50014
|
+
if (tabItems) next.tabBar.items = pruneChrome(tabItems, removed, report3.tabs);
|
|
49822
50015
|
const drawerItems = asItems(next.drawer);
|
|
49823
|
-
if (drawerItems) next.drawer.items = pruneChrome(drawerItems, removed,
|
|
50016
|
+
if (drawerItems) next.drawer.items = pruneChrome(drawerItems, removed, report3.drawer);
|
|
49824
50017
|
const sidebarItems = asItems(next.sidebar);
|
|
49825
|
-
if (sidebarItems) next.sidebar.items = pruneChrome(sidebarItems, removed,
|
|
50018
|
+
if (sidebarItems) next.sidebar.items = pruneChrome(sidebarItems, removed, report3.sidebar);
|
|
49826
50019
|
if (hadTabs && !asItems(next.tabBar).length) {
|
|
49827
|
-
throw new TrimRefusal(`no tab would remain \u2014 every tabBar item opens a screen this trim removes (${
|
|
50020
|
+
throw new TrimRefusal(`no tab would remain \u2014 every tabBar item opens a screen this trim removes (${report3.tabs.join(", ")}); keep at least one tab's screen`, { removedTabs: report3.tabs });
|
|
49828
50021
|
}
|
|
49829
50022
|
const remainingRoots = [...asItems(next.tabBar) ?? [], ...asItems(next.sidebar) ?? []].filter((i) => i.role !== "section");
|
|
49830
50023
|
const remainingRootIds = new Set(remainingRoots.map((i) => i.id).filter((id) => typeof id === "string"));
|
|
49831
|
-
const removedRootIds = /* @__PURE__ */ new Set([...
|
|
50024
|
+
const removedRootIds = /* @__PURE__ */ new Set([...report3.tabs, ...report3.sidebar]);
|
|
49832
50025
|
if (Array.isArray(next.deepLinkPatterns)) {
|
|
49833
50026
|
next.deepLinkPatterns = next.deepLinkPatterns.filter((p) => {
|
|
49834
50027
|
const id = p?.params?.screenId ?? p?.screenId;
|
|
49835
50028
|
if (typeof id === "string" && removed(id)) {
|
|
49836
|
-
|
|
50029
|
+
report3.deepLinkPatterns.push(String(p.pattern ?? id));
|
|
49837
50030
|
return false;
|
|
49838
50031
|
}
|
|
49839
50032
|
return true;
|
|
@@ -49844,7 +50037,7 @@ function rewriteNav(nav, removed, homeId) {
|
|
|
49844
50037
|
if (!Array.isArray(actions)) continue;
|
|
49845
50038
|
next[key2].header.actions = actions.filter((a) => {
|
|
49846
50039
|
if (!(typeof a?.screenId === "string" && removed(a.screenId))) return true;
|
|
49847
|
-
|
|
50040
|
+
report3.headerActions.push(`${key2}:${String(a.icon ?? a.screenId)}`);
|
|
49848
50041
|
return false;
|
|
49849
50042
|
});
|
|
49850
50043
|
}
|
|
@@ -49853,7 +50046,7 @@ function rewriteNav(nav, removed, homeId) {
|
|
|
49853
50046
|
next.topBarDefault.actions = topActions.filter((a) => {
|
|
49854
50047
|
const to = openTarget(a?.onTap) ?? openTarget(a?.action);
|
|
49855
50048
|
if (to && removed(to)) {
|
|
49856
|
-
|
|
50049
|
+
report3.topBarActions.push(entryLabel(a));
|
|
49857
50050
|
return false;
|
|
49858
50051
|
}
|
|
49859
50052
|
return true;
|
|
@@ -49868,7 +50061,7 @@ function rewriteNav(nav, removed, homeId) {
|
|
|
49868
50061
|
for (const [dest, screenId] of Object.entries(cfg.screenIds)) {
|
|
49869
50062
|
if (goneDest(dest) || typeof screenId === "string" && removed(screenId)) {
|
|
49870
50063
|
delete cfg.screenIds[dest];
|
|
49871
|
-
|
|
50064
|
+
report3.surfaceDestinations.push(`${surface}:${dest}`);
|
|
49872
50065
|
if (!remainingRootIds.has(dest)) unmapped.add(dest);
|
|
49873
50066
|
}
|
|
49874
50067
|
}
|
|
@@ -49878,24 +50071,24 @@ function rewriteNav(nav, removed, homeId) {
|
|
|
49878
50071
|
const gone = goneDest(id) || typeof id === "string" && unmapped.has(id);
|
|
49879
50072
|
if (!gone) return true;
|
|
49880
50073
|
const entry = `${surface}:${String(id)}`;
|
|
49881
|
-
if (!
|
|
50074
|
+
if (!report3.surfaceDestinations.includes(entry)) report3.surfaceDestinations.push(entry);
|
|
49882
50075
|
return false;
|
|
49883
50076
|
});
|
|
49884
50077
|
}
|
|
49885
50078
|
}
|
|
49886
50079
|
}
|
|
49887
50080
|
if (Array.isArray(next.appIconBadgeSources)) {
|
|
49888
|
-
const goneActions = new Set(
|
|
50081
|
+
const goneActions = new Set(report3.topBarActions);
|
|
49889
50082
|
const idsOf = (items) => new Set((items ?? []).map((i) => i.id).filter((x) => typeof x === "string"));
|
|
49890
|
-
const removedTabs = new Set(
|
|
49891
|
-
const removedDrawer = new Set(
|
|
50083
|
+
const removedTabs = new Set(report3.tabs);
|
|
50084
|
+
const removedDrawer = new Set(report3.drawer);
|
|
49892
50085
|
const remainingTabIds = idsOf(asItems(next.tabBar));
|
|
49893
50086
|
const remainingDrawerIds = idsOf(asItems(next.drawer));
|
|
49894
50087
|
next.appIconBadgeSources = next.appIconBadgeSources.filter((s) => {
|
|
49895
50088
|
if (typeof s !== "string") return true;
|
|
49896
50089
|
const [kind, id] = [s.slice(0, s.indexOf(":")), s.slice(s.indexOf(":") + 1)];
|
|
49897
50090
|
const gone = kind === "topBarAction" ? goneActions.has(id) : kind === "tab" ? removedTabs.has(id) && !remainingTabIds.has(id) : kind === "drawer" ? removedDrawer.has(id) && !remainingDrawerIds.has(id) : false;
|
|
49898
|
-
if (gone)
|
|
50091
|
+
if (gone) report3.appIconBadgeSources.push(s);
|
|
49899
50092
|
return !gone;
|
|
49900
50093
|
});
|
|
49901
50094
|
}
|
|
@@ -49905,7 +50098,7 @@ function rewriteNav(nav, removed, homeId) {
|
|
|
49905
50098
|
for (const l of original) if (typeof l.screenId === "string") byId.set(l.screenId, l);
|
|
49906
50099
|
const kept = original.filter((l) => {
|
|
49907
50100
|
if (typeof l.screenId === "string" && removed(l.screenId)) {
|
|
49908
|
-
|
|
50101
|
+
report3.linkable.push(l.screenId);
|
|
49909
50102
|
return false;
|
|
49910
50103
|
}
|
|
49911
50104
|
return true;
|
|
@@ -49949,7 +50142,7 @@ function rewriteNav(nav, removed, homeId) {
|
|
|
49949
50142
|
const entry = { screenId: String(l.screenId) };
|
|
49950
50143
|
if (toParent !== fromParent) entry.parentScreenId = { from: fromParent, to: toParent };
|
|
49951
50144
|
if (toTab !== fromTab) entry.tabId = { from: fromTab, to: toTab };
|
|
49952
|
-
|
|
50145
|
+
report3.reparented.push(entry);
|
|
49953
50146
|
}
|
|
49954
50147
|
for (const l of kept) {
|
|
49955
50148
|
if (typeof l.parentScreenId === "string") l.parentScreenId = newParent(l);
|
|
@@ -49957,7 +50150,7 @@ function rewriteNav(nav, removed, homeId) {
|
|
|
49957
50150
|
}
|
|
49958
50151
|
next.linkableScreens = kept;
|
|
49959
50152
|
}
|
|
49960
|
-
return { nav: next, report:
|
|
50153
|
+
return { nav: next, report: report3 };
|
|
49961
50154
|
}
|
|
49962
50155
|
function leftoverNavRefs(nav, removedIds) {
|
|
49963
50156
|
const gone = new Set(removedIds);
|
|
@@ -50116,9 +50309,9 @@ function planTrim(src, keepIds, links) {
|
|
|
50116
50309
|
for (const r of keptRoutes) for (const b of r.bundles) keptWorkers.add(b);
|
|
50117
50310
|
const importsOf = (dir) => {
|
|
50118
50311
|
const out2 = [];
|
|
50119
|
-
for (const [rel,
|
|
50312
|
+
for (const [rel, text8] of Object.entries(src.workerFiles)) {
|
|
50120
50313
|
if (!rel.startsWith(`tier5-src/${dir}/`)) continue;
|
|
50121
|
-
for (const m of
|
|
50314
|
+
for (const m of text8.matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']\.\.\/([A-Za-z0-9_.-]+)(?:\/|["'])/g)) out2.push(m[1]);
|
|
50122
50315
|
}
|
|
50123
50316
|
return out2;
|
|
50124
50317
|
};
|
|
@@ -50164,9 +50357,9 @@ function planTrim(src, keepIds, links) {
|
|
|
50164
50357
|
continue;
|
|
50165
50358
|
}
|
|
50166
50359
|
const block = edited.get(r);
|
|
50167
|
-
let
|
|
50168
|
-
if (r === app.home && homeModules?.text !== void 0)
|
|
50169
|
-
if (
|
|
50360
|
+
let text8 = block ? serializeAt(block, app.dev, r.open) : void 0;
|
|
50361
|
+
if (r === app.home && homeModules?.text !== void 0) text8 = homeModules.text;
|
|
50362
|
+
if (text8 !== void 0) cut2(r.open, r.close, text8);
|
|
50170
50363
|
}
|
|
50171
50364
|
if (templatedEmptied) cut2(templatedEmptied[0], templatedEmptied[1], "\n");
|
|
50172
50365
|
dev += app.dev.slice(cursor);
|
|
@@ -50182,7 +50375,7 @@ function planTrim(src, keepIds, links) {
|
|
|
50182
50375
|
for (const e of edits.sort((a, b) => b.start - a.start)) config = config.slice(0, e.start) + e.text + config.slice(e.end);
|
|
50183
50376
|
const keptExact = keptRoutes.filter((r) => r.kind === "screen");
|
|
50184
50377
|
const keptTemplated = keptRoutes.filter((r) => r.kind === "templated");
|
|
50185
|
-
const
|
|
50378
|
+
const report3 = {
|
|
50186
50379
|
...links ? { links } : {},
|
|
50187
50380
|
kept: { screens: sorted(keptExact.map((r) => r.label)), templated: sorted(keptTemplated.map((r) => r.label)) },
|
|
50188
50381
|
implicit: implicit.map((r) => r.label),
|
|
@@ -50212,16 +50405,16 @@ function planTrim(src, keepIds, links) {
|
|
|
50212
50405
|
},
|
|
50213
50406
|
bytes: { before: Buffer.byteLength(app.dev), after: Buffer.byteLength(dev) }
|
|
50214
50407
|
};
|
|
50215
|
-
return { dev, config, removedWorkers, removedIds, report:
|
|
50408
|
+
return { dev, config, removedWorkers, removedIds, report: report3 };
|
|
50216
50409
|
}
|
|
50217
50410
|
function homeModulesPass(app, kept, edited) {
|
|
50218
50411
|
const verticals = Object.entries(EXPLORE_CARDS).filter(([, card2]) => app.exact.has(card2.screenId));
|
|
50219
50412
|
const removedVertical = verticals.some(([, card2]) => !kept.has(app.exact.get(card2.screenId)));
|
|
50220
50413
|
if (!removedVertical) return { dropped: [] };
|
|
50221
50414
|
const block = edited.get(app.home);
|
|
50222
|
-
const
|
|
50415
|
+
const text8 = block ? serializeAt(block, app.dev, app.home.open) : app.dev.slice(app.home.open, app.home.close);
|
|
50223
50416
|
const installed = verticals.filter(([, card2]) => kept.has(app.exact.get(card2.screenId))).map(([v]) => v);
|
|
50224
|
-
const patched = patchHomeModules(
|
|
50417
|
+
const patched = patchHomeModules(text8, installed);
|
|
50225
50418
|
if (!patched.droppedModules.length) return { dropped: [] };
|
|
50226
50419
|
return { text: patched.src, dropped: patched.droppedModules };
|
|
50227
50420
|
}
|
|
@@ -50935,8 +51128,8 @@ function isNoneEntry(entry) {
|
|
|
50935
51128
|
const head = entryHead(entry);
|
|
50936
51129
|
return NONE.test(entry) || head !== "" && NONE.test(head);
|
|
50937
51130
|
}
|
|
50938
|
-
function parseDataAccess(
|
|
50939
|
-
const body =
|
|
51131
|
+
function parseDataAccess(text8, from) {
|
|
51132
|
+
const body = text8.replace(/<!--[\s\S]*?(?:-->|$)/g, "");
|
|
50940
51133
|
const heading = new RegExp(`(?:^|\\n)##[ \\t]+${DATA_ACCESS_HEADING}\\b[^\\n]*([\\s\\S]*?)(?=\\n##[ \\t]|$)`, "i").exec(body);
|
|
50941
51134
|
if (!heading) return null;
|
|
50942
51135
|
const lines = heading[1].split("\n").map((l) => l.trim()).filter((l) => l.startsWith("|"));
|
|
@@ -50963,13 +51156,13 @@ function readDataAccess(dir) {
|
|
|
50963
51156
|
for (const name of ["SPEC.md", "MOBILE-UX.md"]) {
|
|
50964
51157
|
const p = join49(dir, name);
|
|
50965
51158
|
if (!existsSync50(p)) continue;
|
|
50966
|
-
let
|
|
51159
|
+
let text8;
|
|
50967
51160
|
try {
|
|
50968
|
-
|
|
51161
|
+
text8 = readFileSync55(p, "utf8");
|
|
50969
51162
|
} catch {
|
|
50970
51163
|
continue;
|
|
50971
51164
|
}
|
|
50972
|
-
const plan = parseDataAccess(
|
|
51165
|
+
const plan = parseDataAccess(text8, name);
|
|
50973
51166
|
if (plan) return plan;
|
|
50974
51167
|
}
|
|
50975
51168
|
return null;
|
|
@@ -51122,7 +51315,7 @@ function readTestConfig(dir) {
|
|
|
51122
51315
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input) || Object.values(input).some((v) => typeof v !== "string"))) {
|
|
51123
51316
|
throw new Error(`${TEST_CONFIG_FILE}: journeys[${i}].input must be an object of string values \u2014 the screen input a nav edge would send, e.g. { "id": "svc-0001" }`);
|
|
51124
51317
|
}
|
|
51125
|
-
if (!Array.isArray(journey.expectText) || !journey.expectText.length || journey.expectText.some((
|
|
51318
|
+
if (!Array.isArray(journey.expectText) || !journey.expectText.length || journey.expectText.some((text8) => typeof text8 !== "string" || !text8.trim())) {
|
|
51126
51319
|
throw new Error(`${TEST_CONFIG_FILE}: journeys[${i}] needs non-empty expectText values that prove a visible result`);
|
|
51127
51320
|
}
|
|
51128
51321
|
});
|
|
@@ -51183,16 +51376,16 @@ function readCannedResponses() {
|
|
|
51183
51376
|
}
|
|
51184
51377
|
function makeRecordingWixCall(send2, token, app) {
|
|
51185
51378
|
const events = [];
|
|
51186
|
-
const
|
|
51379
|
+
const refused2 = [];
|
|
51187
51380
|
let id = 0;
|
|
51188
51381
|
const call = async (method, path2, body) => {
|
|
51189
51382
|
const verb = method.toUpperCase();
|
|
51190
51383
|
if (/[#\x00-\x1F\x7F]/.test(path2)) {
|
|
51191
|
-
|
|
51384
|
+
refused2.push(`${verb} ${path2}`);
|
|
51192
51385
|
throw new Error(`--record refused ${verb} ${path2}: the path carries a fragment or control character \u2014 not a provable read`);
|
|
51193
51386
|
}
|
|
51194
51387
|
if (!isProvenRead(verb, path2)) {
|
|
51195
|
-
|
|
51388
|
+
refused2.push(`${verb} ${path2}`);
|
|
51196
51389
|
throw new Error(`--record refused ${verb} ${path2}: recording sends only proven reads (GET, or POST ending in /query, /search, /list*, /get*, /count, /aggregate, /facets, /connections) \u2014 if this IS a read, add its rule to dev/net-mocks.json by hand`);
|
|
51197
51390
|
}
|
|
51198
51391
|
const bodyStr = body === void 0 ? void 0 : typeof body === "string" ? body : JSON.stringify(body);
|
|
@@ -51219,7 +51412,7 @@ function makeRecordingWixCall(send2, token, app) {
|
|
|
51219
51412
|
return r.body;
|
|
51220
51413
|
}
|
|
51221
51414
|
};
|
|
51222
|
-
return { call, events, refused };
|
|
51415
|
+
return { call, events, refused: refused2 };
|
|
51223
51416
|
}
|
|
51224
51417
|
async function runRecordPass(opts) {
|
|
51225
51418
|
const bootErrors = [];
|
|
@@ -51623,9 +51816,9 @@ function formatReport(r) {
|
|
|
51623
51816
|
out2.push(`transport: ${r.transport}`);
|
|
51624
51817
|
if (r.nativeDecode) {
|
|
51625
51818
|
for (const platform of ["ios", "android"]) {
|
|
51626
|
-
const
|
|
51627
|
-
if (!
|
|
51628
|
-
out2.push("unavailable" in
|
|
51819
|
+
const summary2 = r.nativeDecode[platform];
|
|
51820
|
+
if (!summary2) continue;
|
|
51821
|
+
out2.push("unavailable" in summary2 ? `native decode ${platform}: unavailable \u2014 ${summary2.reason}` : `native decode ${platform}: ${summary2.checked} checked, ${summary2.failed} failed in ${r.nativeDecode.wallMs}ms`);
|
|
51629
51822
|
}
|
|
51630
51823
|
}
|
|
51631
51824
|
out2.push("");
|
|
@@ -51768,14 +51961,14 @@ import { join as join51 } from "node:path";
|
|
|
51768
51961
|
function mobileUxIssues(dir, strict) {
|
|
51769
51962
|
const file = join51(dir, "MOBILE-UX.md");
|
|
51770
51963
|
if (!existsSync52(file)) return [];
|
|
51771
|
-
let
|
|
51964
|
+
let text8;
|
|
51772
51965
|
try {
|
|
51773
|
-
|
|
51966
|
+
text8 = readFileSync57(file, "utf8");
|
|
51774
51967
|
} catch {
|
|
51775
51968
|
return [];
|
|
51776
51969
|
}
|
|
51777
|
-
if (!
|
|
51778
|
-
return mobileUxMarkerFindings(
|
|
51970
|
+
if (!text8) return [];
|
|
51971
|
+
return mobileUxMarkerFindings(text8, recordedRenderWidths(dir)).map((finding) => {
|
|
51779
51972
|
const scaffoldAdvisory = finding.code === "missing-width-evidence";
|
|
51780
51973
|
return {
|
|
51781
51974
|
screen: null,
|
|
@@ -52070,7 +52263,7 @@ async function pinnedFetch(url, init, addresses, maxResponseBytes) {
|
|
|
52070
52263
|
const headers2 = Object.fromEntries(new Headers(init.headers).entries());
|
|
52071
52264
|
const request = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
52072
52265
|
const hostname2 = url.hostname.replace(/^\[|\]$/g, "");
|
|
52073
|
-
return await new Promise((
|
|
52266
|
+
return await new Promise((resolve53, reject) => {
|
|
52074
52267
|
let settled = false;
|
|
52075
52268
|
const finish2 = (fn) => {
|
|
52076
52269
|
if (settled) return;
|
|
@@ -52107,7 +52300,7 @@ async function pinnedFetch(url, init, addresses, maxResponseBytes) {
|
|
|
52107
52300
|
}
|
|
52108
52301
|
const responseHeaders = new Headers();
|
|
52109
52302
|
for (const [key2, value] of Object.entries(response.headers)) if (value !== void 0) responseHeaders.set(key2, Array.isArray(value) ? value.join(", ") : value);
|
|
52110
|
-
|
|
52303
|
+
resolve53(new Response(bytes2, { status: response.statusCode, statusText: response.statusMessage, headers: responseHeaders }));
|
|
52111
52304
|
}));
|
|
52112
52305
|
});
|
|
52113
52306
|
req.once("error", (error) => finish2(() => reject(error)));
|
|
@@ -52407,7 +52600,7 @@ function createLocalTestSandbox() {
|
|
|
52407
52600
|
}
|
|
52408
52601
|
})();
|
|
52409
52602
|
`;
|
|
52410
|
-
return await new Promise((
|
|
52603
|
+
return await new Promise((resolve53, reject) => {
|
|
52411
52604
|
const { port1, port2 } = new MessageChannel();
|
|
52412
52605
|
const serializableContext = {
|
|
52413
52606
|
auth: context.auth,
|
|
@@ -52483,7 +52676,7 @@ function createLocalTestSandbox() {
|
|
|
52483
52676
|
timer = setTimeout(() => finish2(() => reject(new SandboxError("timeout", `execution exceeded ${limits.timeoutMs}ms`))), Math.max(1, limits.timeoutMs));
|
|
52484
52677
|
return;
|
|
52485
52678
|
}
|
|
52486
|
-
if (message.ok) finish2(() =>
|
|
52679
|
+
if (message.ok) finish2(() => resolve53({ value: message.value, durationMs: Date.now() - started }));
|
|
52487
52680
|
else finish2(() => reject(new SandboxError("execution_failed", message.message ?? "handler failed")));
|
|
52488
52681
|
});
|
|
52489
52682
|
worker.once("error", (error) => finish2(() => reject(new SandboxError("execution_failed", error.message))));
|
|
@@ -53599,12 +53792,12 @@ async function runRemoteFunctionOperation(input) {
|
|
|
53599
53792
|
if (input.boCookie) headers2.cookie = input.boCookie;
|
|
53600
53793
|
if (input.previewToken) headers2["x-himalaya-preview-token"] = input.previewToken;
|
|
53601
53794
|
const response = await (input.fetchImpl ?? fetch)(url, { method, headers: headers2 });
|
|
53602
|
-
const
|
|
53795
|
+
const text8 = await response.text();
|
|
53603
53796
|
let body;
|
|
53604
53797
|
try {
|
|
53605
|
-
body = JSON.parse(
|
|
53798
|
+
body = JSON.parse(text8);
|
|
53606
53799
|
} catch {
|
|
53607
|
-
body =
|
|
53800
|
+
body = text8;
|
|
53608
53801
|
}
|
|
53609
53802
|
return { ok: response.ok, status: response.status, body };
|
|
53610
53803
|
}
|
|
@@ -53692,7 +53885,7 @@ function flagShapedInputs(args) {
|
|
|
53692
53885
|
function guarded(h) {
|
|
53693
53886
|
return async (args, deps) => {
|
|
53694
53887
|
const bad2 = flagShapedInputs(args);
|
|
53695
|
-
if (bad2.length) return
|
|
53888
|
+
if (bad2.length) return text6(`refused: ${bad2.join(", ")} may not start with "-" (it would be read as a himi flag)`, true);
|
|
53696
53889
|
return h(args, deps);
|
|
53697
53890
|
};
|
|
53698
53891
|
}
|
|
@@ -53845,7 +54038,7 @@ function rawTools() {
|
|
|
53845
54038
|
return json3({ ...await d.preview.start(await appDir2(a, d)), hint: PREVIEW_HINT });
|
|
53846
54039
|
} catch (e) {
|
|
53847
54040
|
const log = e.log;
|
|
53848
|
-
return
|
|
54041
|
+
return text6(`${e.message}${typeof log === "string" && log ? `
|
|
53849
54042
|
|
|
53850
54043
|
${log}` : ""}`, true);
|
|
53851
54044
|
}
|
|
@@ -53921,14 +54114,14 @@ ${log}` : ""}`, true);
|
|
|
53921
54114
|
}
|
|
53922
54115
|
];
|
|
53923
54116
|
}
|
|
53924
|
-
var MAX_TEXT2, clip,
|
|
54117
|
+
var MAX_TEXT2, clip, text6, json3, opt, sw, DIR, LEARN, CHECK, LOCAL_WRITE, PUBLISH, PREVIEW_HINT, LOGIN_HINT;
|
|
53925
54118
|
var init_tools = __esm({
|
|
53926
54119
|
"src/mcp/tools.ts"() {
|
|
53927
54120
|
MAX_TEXT2 = 8e4;
|
|
53928
54121
|
clip = (s) => s.length <= MAX_TEXT2 ? s : `${s.slice(0, MAX_TEXT2)}
|
|
53929
54122
|
\u2026 [truncated ${s.length - MAX_TEXT2} chars \u2014 re-run the command in a terminal for the full output]`;
|
|
53930
|
-
|
|
53931
|
-
json3 = (o) =>
|
|
54123
|
+
text6 = (t, isError = false) => ({ content: [{ type: "text", text: t }], ...isError ? { isError: true } : {} });
|
|
54124
|
+
json3 = (o) => text6(JSON.stringify(o, null, 2));
|
|
53932
54125
|
opt = (flag, v) => typeof v === "string" && v ? [flag, v] : typeof v === "number" ? [flag, String(v)] : [];
|
|
53933
54126
|
sw = (flag, v) => v === true ? [flag] : [];
|
|
53934
54127
|
DIR = z.string().optional().describe("The app (content package) directory. Default: the workspace root the host opened, else the server's working directory.");
|
|
@@ -54004,9 +54197,9 @@ async function startMcpServer(opts) {
|
|
|
54004
54197
|
},
|
|
54005
54198
|
readFile: (p) => readFileSync59(p)
|
|
54006
54199
|
};
|
|
54007
|
-
const closed = new Promise((
|
|
54008
|
-
stdin.once("end",
|
|
54009
|
-
stdin.once("close",
|
|
54200
|
+
const closed = new Promise((resolve53) => {
|
|
54201
|
+
stdin.once("end", resolve53);
|
|
54202
|
+
stdin.once("close", resolve53);
|
|
54010
54203
|
});
|
|
54011
54204
|
const handle = serveStdio(
|
|
54012
54205
|
() => {
|
|
@@ -54248,7 +54441,7 @@ function fieldAt(path2) {
|
|
|
54248
54441
|
if (path2 === null) return void 0;
|
|
54249
54442
|
return path2.match(/\.([^.[\]]+)$/)?.[1];
|
|
54250
54443
|
}
|
|
54251
|
-
function mergeNativeDecode(
|
|
54444
|
+
function mergeNativeDecode(report3, run2) {
|
|
54252
54445
|
const platformLabel = run2.platform === "ios" ? "iOS" : "Android";
|
|
54253
54446
|
const nativeFailures = run2.verdicts.flatMap((verdict) => verdict.errors.map((error) => ({
|
|
54254
54447
|
screen: verdict.screenId,
|
|
@@ -54261,18 +54454,18 @@ function mergeNativeDecode(report2, run2) {
|
|
|
54261
54454
|
...error.actual ? { actual: error.actual } : {},
|
|
54262
54455
|
hint: `This descriptor ships as a full-screen \u201CCouldn't load\u201D; fix the typed field and rerun \`himi test --native ${run2.platform}\`.`
|
|
54263
54456
|
})));
|
|
54264
|
-
const failures = [...
|
|
54457
|
+
const failures = [...report3.failures, ...nativeFailures];
|
|
54265
54458
|
const failedScreens = new Set(failures.map((finding) => finding.screen));
|
|
54266
54459
|
return {
|
|
54267
|
-
...
|
|
54268
|
-
ok:
|
|
54460
|
+
...report3,
|
|
54461
|
+
ok: report3.ok && nativeFailures.length === 0,
|
|
54269
54462
|
failures,
|
|
54270
|
-
counts: { ...
|
|
54271
|
-
screens: { ...
|
|
54463
|
+
counts: { ...report3.counts, errors: failures.length },
|
|
54464
|
+
screens: { ...report3.screens, clean: report3.screens.total - failedScreens.size, failed: failedScreens.size },
|
|
54272
54465
|
nativeDecode: {
|
|
54273
|
-
...
|
|
54466
|
+
...report3.nativeDecode,
|
|
54274
54467
|
[run2.platform]: { checked: run2.verdicts.length, failed: run2.verdicts.filter((verdict) => !verdict.ok).length },
|
|
54275
|
-
wallMs: (
|
|
54468
|
+
wallMs: (report3.nativeDecode?.wallMs ?? 0) + run2.wallMs
|
|
54276
54469
|
}
|
|
54277
54470
|
};
|
|
54278
54471
|
}
|
|
@@ -54298,7 +54491,7 @@ __export(browser_authoring_exports, {
|
|
|
54298
54491
|
runBrowserAuthoring: () => runBrowserAuthoring,
|
|
54299
54492
|
sdkSubpathReader: () => sdkSubpathReader
|
|
54300
54493
|
});
|
|
54301
|
-
import { createServer as
|
|
54494
|
+
import { createServer as createServer5 } from "node:http";
|
|
54302
54495
|
import { createHash as createHash29, randomBytes as randomBytes4 } from "node:crypto";
|
|
54303
54496
|
import { existsSync as existsSync55, readdirSync as readdirSync27, readFileSync as readFileSync61, realpathSync as realpathSync7, statSync as statSync13 } from "node:fs";
|
|
54304
54497
|
import { join as join54, posix as posix2, relative as relative11, sep as sep14 } from "node:path";
|
|
@@ -54385,10 +54578,10 @@ function collectSdkModules(files, resolveSubpath) {
|
|
|
54385
54578
|
for (const source of Object.values(files)) visit(source);
|
|
54386
54579
|
return { modules, unsupported };
|
|
54387
54580
|
}
|
|
54388
|
-
function sdkSubpathReader(
|
|
54581
|
+
function sdkSubpathReader(resolve53) {
|
|
54389
54582
|
return (sub) => {
|
|
54390
54583
|
try {
|
|
54391
|
-
return readFileSync61(
|
|
54584
|
+
return readFileSync61(resolve53(`@wix/himalaya/${sub}`), "utf8");
|
|
54392
54585
|
} catch {
|
|
54393
54586
|
return null;
|
|
54394
54587
|
}
|
|
@@ -54466,10 +54659,10 @@ async function runBrowserAuthoring(opts) {
|
|
|
54466
54659
|
const token = mintToken2(payload);
|
|
54467
54660
|
const unusable = await probeAuthoringPage(opts.authoringBase, opts.fetchImpl, opts.probeTimeoutMs);
|
|
54468
54661
|
if (unusable) throw new Error(unusable);
|
|
54469
|
-
return await new Promise((
|
|
54662
|
+
return await new Promise((resolve53, reject) => {
|
|
54470
54663
|
let settled = false;
|
|
54471
54664
|
let url = "";
|
|
54472
|
-
const server =
|
|
54665
|
+
const server = createServer5();
|
|
54473
54666
|
const finish2 = (fn) => {
|
|
54474
54667
|
if (settled) return;
|
|
54475
54668
|
settled = true;
|
|
@@ -54517,8 +54710,8 @@ async function runBrowserAuthoring(opts) {
|
|
|
54517
54710
|
return;
|
|
54518
54711
|
}
|
|
54519
54712
|
res.writeHead(200, { "content-type": "text/plain" }).end("ok");
|
|
54520
|
-
const
|
|
54521
|
-
finish2(() =>
|
|
54713
|
+
const report3 = body.result;
|
|
54714
|
+
finish2(() => resolve53({ report: report3, url }));
|
|
54522
54715
|
})();
|
|
54523
54716
|
});
|
|
54524
54717
|
server.on("error", (e) => finish2(() => reject(e)));
|
|
@@ -55195,10 +55388,10 @@ async function hostedAuthUrl(req, visitorToken, fetchImpl = fetch) {
|
|
|
55195
55388
|
}
|
|
55196
55389
|
})
|
|
55197
55390
|
});
|
|
55198
|
-
const
|
|
55391
|
+
const text8 = await res.text();
|
|
55199
55392
|
let parsed = {};
|
|
55200
55393
|
try {
|
|
55201
|
-
parsed =
|
|
55394
|
+
parsed = text8 ? JSON.parse(text8) : {};
|
|
55202
55395
|
} catch {
|
|
55203
55396
|
}
|
|
55204
55397
|
const fullUrl = parsed.redirectSession?.fullUrl;
|
|
@@ -55218,10 +55411,10 @@ async function exchangeAuthCode(cfg, code, verifier, fetchImpl = fetch) {
|
|
|
55218
55411
|
redirectUri: cfg.redirectUri
|
|
55219
55412
|
})
|
|
55220
55413
|
});
|
|
55221
|
-
const
|
|
55414
|
+
const text8 = await res.text();
|
|
55222
55415
|
let parsed = {};
|
|
55223
55416
|
try {
|
|
55224
|
-
parsed =
|
|
55417
|
+
parsed = text8 ? JSON.parse(text8) : {};
|
|
55225
55418
|
} catch {
|
|
55226
55419
|
}
|
|
55227
55420
|
if (res.status !== 200 || !parsed.access_token) throw new Error(`token exchange failed (${res.status})`);
|
|
@@ -56435,11 +56628,11 @@ async function localizeConfigResponse(res, kind, header) {
|
|
|
56435
56628
|
header
|
|
56436
56629
|
)
|
|
56437
56630
|
};
|
|
56438
|
-
const
|
|
56631
|
+
const text8 = JSON.stringify(next);
|
|
56439
56632
|
const headers2 = new Headers(res.headers);
|
|
56440
56633
|
headers2.delete("etag");
|
|
56441
|
-
headers2.set("content-length", String(new TextEncoder().encode(
|
|
56442
|
-
return new Response(
|
|
56634
|
+
headers2.set("content-length", String(new TextEncoder().encode(text8).length));
|
|
56635
|
+
return new Response(text8, { status: 200, headers: headers2 });
|
|
56443
56636
|
}
|
|
56444
56637
|
function makeRunHandler(opts) {
|
|
56445
56638
|
const mount = opts.chrome === "app" ? "/app" : "/preview";
|
|
@@ -56562,7 +56755,7 @@ var init_run_handler = __esm({
|
|
|
56562
56755
|
});
|
|
56563
56756
|
|
|
56564
56757
|
// ../../core/serve-runtime/src/node-adapter.ts
|
|
56565
|
-
import { createServer as
|
|
56758
|
+
import { createServer as createServer6 } from "node:http";
|
|
56566
56759
|
async function toRequest(req) {
|
|
56567
56760
|
const method = (req.method ?? "GET").toUpperCase();
|
|
56568
56761
|
const host = typeof req.headers.host === "string" && req.headers.host || "serve.local";
|
|
@@ -56606,7 +56799,7 @@ async function writeResponse(res, response) {
|
|
|
56606
56799
|
res.end(ab.byteLength ? Buffer.from(ab) : void 0);
|
|
56607
56800
|
}
|
|
56608
56801
|
function nodeAdapter(handler) {
|
|
56609
|
-
return
|
|
56802
|
+
return createServer6(async (req, res) => {
|
|
56610
56803
|
try {
|
|
56611
56804
|
const response = await handler(await toRequest(req));
|
|
56612
56805
|
await writeResponse(res, response);
|
|
@@ -56627,8 +56820,8 @@ var init_node_adapter = __esm({
|
|
|
56627
56820
|
function authConfigOf(appConfig) {
|
|
56628
56821
|
if (!appConfig) return void 0;
|
|
56629
56822
|
try {
|
|
56630
|
-
const
|
|
56631
|
-
return JSON.parse(
|
|
56823
|
+
const text8 = typeof appConfig === "string" ? appConfig : new TextDecoder().decode(appConfig);
|
|
56824
|
+
return JSON.parse(text8)?.authConfig ?? void 0;
|
|
56632
56825
|
} catch {
|
|
56633
56826
|
return void 0;
|
|
56634
56827
|
}
|
|
@@ -56960,7 +57153,7 @@ __export(run_watch_exports, {
|
|
|
56960
57153
|
watchedContentDir: () => watchedContentDir,
|
|
56961
57154
|
writeRunRecord: () => writeRunRecord
|
|
56962
57155
|
});
|
|
56963
|
-
import { closeSync as
|
|
57156
|
+
import { closeSync as closeSync4, constants, existsSync as existsSync59, fstatSync, lstatSync as lstatSync12, statSync as statSync15, mkdirSync as mkdirSync28, openSync as openSync3, readFileSync as readFileSync66, realpathSync as realpathSync8, renameSync as renameSync6, rmSync as rmSync14, writeFileSync as writeFileSync23, watch as fsWatch } from "node:fs";
|
|
56964
57157
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
56965
57158
|
import { extname as extname4, join as join58, resolve as resolve46, sep as sep15 } from "node:path";
|
|
56966
57159
|
function diffReleases(prev, next) {
|
|
@@ -57159,7 +57352,7 @@ function readConfined(root, rel) {
|
|
|
57159
57352
|
if (!fstatSync(fd).isFile()) return null;
|
|
57160
57353
|
return readFileSync66(fd);
|
|
57161
57354
|
} finally {
|
|
57162
|
-
|
|
57355
|
+
closeSync4(fd);
|
|
57163
57356
|
}
|
|
57164
57357
|
} catch {
|
|
57165
57358
|
return null;
|
|
@@ -57209,7 +57402,7 @@ __export(run_browser_exports, {
|
|
|
57209
57402
|
releasePayloadFromDir: () => releasePayloadFromDir,
|
|
57210
57403
|
runInBrowser: () => runInBrowser
|
|
57211
57404
|
});
|
|
57212
|
-
import { createServer as
|
|
57405
|
+
import { createServer as createServer7 } from "node:http";
|
|
57213
57406
|
import { existsSync as existsSync60, readFileSync as readFileSync67 } from "node:fs";
|
|
57214
57407
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
57215
57408
|
import { dirname as dirname32, resolve as resolve47 } from "node:path";
|
|
@@ -57238,16 +57431,16 @@ async function releasePayloadFromDir(root, app, header) {
|
|
|
57238
57431
|
const manifest = await store.getManifest(app);
|
|
57239
57432
|
if (!manifest) throw new Error(`no release manifest for ${app} under ${root}`);
|
|
57240
57433
|
const b64 = (b) => b ? Buffer.from(b).toString("base64") : void 0;
|
|
57241
|
-
const
|
|
57434
|
+
const text8 = (b) => b ? new TextDecoder().decode(b) : void 0;
|
|
57242
57435
|
const screens = {};
|
|
57243
57436
|
const docs = {};
|
|
57244
57437
|
const addDoc = async (hash) => {
|
|
57245
57438
|
if (!hash || docs[hash] !== void 0) return;
|
|
57246
|
-
const d =
|
|
57439
|
+
const d = text8(await store.getDoc?.(app, hash) ?? null);
|
|
57247
57440
|
if (d !== void 0) docs[hash] = d;
|
|
57248
57441
|
};
|
|
57249
57442
|
for (const [id, meta] of Object.entries(manifest.screens ?? {})) {
|
|
57250
|
-
const s =
|
|
57443
|
+
const s = text8(await store.getScreen(app, id));
|
|
57251
57444
|
if (s !== void 0) screens[id] = s;
|
|
57252
57445
|
await addDoc(meta.descriptorHash);
|
|
57253
57446
|
}
|
|
@@ -57268,10 +57461,10 @@ async function releasePayloadFromDir(root, app, header) {
|
|
|
57268
57461
|
}
|
|
57269
57462
|
const widgets = {};
|
|
57270
57463
|
for (const id of Object.keys(manifest.widgets ?? {})) {
|
|
57271
|
-
const w =
|
|
57464
|
+
const w = text8(await store.getWidget?.(app, id) ?? null);
|
|
57272
57465
|
if (w !== void 0) widgets[id] = w;
|
|
57273
57466
|
}
|
|
57274
|
-
const appConfig =
|
|
57467
|
+
const appConfig = text8(await store.getAppConfig(app));
|
|
57275
57468
|
const wixClientId = appConfigWixClientId(appConfig);
|
|
57276
57469
|
return {
|
|
57277
57470
|
app,
|
|
@@ -57281,7 +57474,7 @@ async function releasePayloadFromDir(root, app, header) {
|
|
|
57281
57474
|
docs,
|
|
57282
57475
|
assets,
|
|
57283
57476
|
widgets,
|
|
57284
|
-
tokens:
|
|
57477
|
+
tokens: text8(await store.getTokens(app)),
|
|
57285
57478
|
appConfig,
|
|
57286
57479
|
icon: b64(await store.getIcon?.(app) ?? null),
|
|
57287
57480
|
header,
|
|
@@ -57294,7 +57487,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
|
|
|
57294
57487
|
const payload = { ...await releasePayloadFromDir(releaseRoot, app, header), chrome: opts.chrome ?? "preview" };
|
|
57295
57488
|
const body = JSON.stringify(payload);
|
|
57296
57489
|
const missed = [];
|
|
57297
|
-
const server =
|
|
57490
|
+
const server = createServer7((req, res) => {
|
|
57298
57491
|
const path2 = (req.url ?? "/").split("?")[0];
|
|
57299
57492
|
if (path2 === RUN_IDENTITY_PATH && opts.identityDir) {
|
|
57300
57493
|
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
|
|
@@ -57811,10 +58004,10 @@ function scalar3(v) {
|
|
|
57811
58004
|
}
|
|
57812
58005
|
return t;
|
|
57813
58006
|
}
|
|
57814
|
-
function parseStoreConfig(
|
|
58007
|
+
function parseStoreConfig(text8) {
|
|
57815
58008
|
const cfg = {};
|
|
57816
58009
|
let section2 = null;
|
|
57817
|
-
for (const raw of
|
|
58010
|
+
for (const raw of text8.split("\n")) {
|
|
57818
58011
|
const line = strip2(raw);
|
|
57819
58012
|
if (!line.trim()) continue;
|
|
57820
58013
|
const indented = /^\s+/.test(raw);
|
|
@@ -57873,10 +58066,10 @@ function profilePath(name, home = homedir5()) {
|
|
|
57873
58066
|
}
|
|
57874
58067
|
return join59(credentialsDir(home), `${name}.json`);
|
|
57875
58068
|
}
|
|
57876
|
-
function parseProfile(
|
|
58069
|
+
function parseProfile(text8, label2) {
|
|
57877
58070
|
let doc;
|
|
57878
58071
|
try {
|
|
57879
|
-
doc = JSON.parse(
|
|
58072
|
+
doc = JSON.parse(text8);
|
|
57880
58073
|
} catch (e) {
|
|
57881
58074
|
throw new Error(`${label2} is not valid JSON: ${e.message}`);
|
|
57882
58075
|
}
|
|
@@ -58485,11 +58678,11 @@ async function registerBundleId(opts) {
|
|
|
58485
58678
|
const j = await res.json();
|
|
58486
58679
|
return { status: "created", id: j.data?.id };
|
|
58487
58680
|
}
|
|
58488
|
-
const
|
|
58489
|
-
if (res.status === 409 || /already (exists|in use)|duplicate|ENTITY_ERROR.*identifier/i.test(
|
|
58681
|
+
const text8 = await res.text();
|
|
58682
|
+
if (res.status === 409 || /already (exists|in use)|duplicate|ENTITY_ERROR.*identifier/i.test(text8)) {
|
|
58490
58683
|
return { status: "exists" };
|
|
58491
58684
|
}
|
|
58492
|
-
throw new Error(`POST /v1/bundleIds \u2192 ${res.status}: ${
|
|
58685
|
+
throw new Error(`POST /v1/bundleIds \u2192 ${res.status}: ${text8.slice(0, 300)}`);
|
|
58493
58686
|
}
|
|
58494
58687
|
async function main2() {
|
|
58495
58688
|
const [app, ...rest] = process.argv.slice(2);
|
|
@@ -58767,12 +58960,12 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
|
|
|
58767
58960
|
body = JSON.stringify({ value });
|
|
58768
58961
|
}
|
|
58769
58962
|
const response = await (options.fetchImpl ?? fetch)(url, { method: operation === "list" ? "GET" : operation === "set" ? "PUT" : "POST", headers: headers2, body });
|
|
58770
|
-
const
|
|
58963
|
+
const text8 = await response.text();
|
|
58771
58964
|
let output;
|
|
58772
58965
|
try {
|
|
58773
|
-
output = JSON.parse(
|
|
58966
|
+
output = JSON.parse(text8);
|
|
58774
58967
|
} catch {
|
|
58775
|
-
output =
|
|
58968
|
+
output = text8;
|
|
58776
58969
|
}
|
|
58777
58970
|
if (!response.ok) throw new Error(typeof output === "string" ? output : JSON.stringify(output));
|
|
58778
58971
|
io.log(JSON.stringify(output));
|
|
@@ -58788,6 +58981,217 @@ var init_secrets2 = __esm({
|
|
|
58788
58981
|
}
|
|
58789
58982
|
});
|
|
58790
58983
|
|
|
58984
|
+
// ../serve-cli/src/push-send.ts
|
|
58985
|
+
async function pushSendRequest(options) {
|
|
58986
|
+
const refusal = localRefusal(options);
|
|
58987
|
+
if (refusal) return { ok: false, status: 0, body: null, error: refusal };
|
|
58988
|
+
try {
|
|
58989
|
+
const response = await (options.fetchImpl ?? fetch)(`${options.target.control.replace(/\/+$/, "")}/v1/admin/push/send`, {
|
|
58990
|
+
method: "POST",
|
|
58991
|
+
headers: headersFor(options),
|
|
58992
|
+
redirect: "error",
|
|
58993
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
58994
|
+
body: JSON.stringify(options.body)
|
|
58995
|
+
});
|
|
58996
|
+
const retryAfterSec = response.status === 429 ? Number(response.headers.get("retry-after")) : NaN;
|
|
58997
|
+
return {
|
|
58998
|
+
ok: response.ok,
|
|
58999
|
+
status: response.status,
|
|
59000
|
+
body: objectFrom(await response.text()),
|
|
59001
|
+
...Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? { retryAfterSec } : {}
|
|
59002
|
+
};
|
|
59003
|
+
} catch {
|
|
59004
|
+
return { ok: false, status: 0, body: null, error: "push_send_unreachable" };
|
|
59005
|
+
}
|
|
59006
|
+
}
|
|
59007
|
+
function localRefusal(options) {
|
|
59008
|
+
if (!options.target.controlToken) return "control_token_required";
|
|
59009
|
+
if (!options.wixToken) return "wix_account_token_required";
|
|
59010
|
+
if (!options.siteToken) return "site_token_required";
|
|
59011
|
+
if (credentialHostRefusal(options.target.control)) return "insecure_target";
|
|
59012
|
+
if (options.previewTag !== void 0 && !PREVIEW_TAG2.test(options.previewTag)) return "invalid_preview_tag";
|
|
59013
|
+
return void 0;
|
|
59014
|
+
}
|
|
59015
|
+
function headersFor(options) {
|
|
59016
|
+
return {
|
|
59017
|
+
"x-control-token": options.target.controlToken,
|
|
59018
|
+
// RAW, never `Bearer ` — what normalizeWixAuthorization and token-info expect.
|
|
59019
|
+
Authorization: options.wixToken,
|
|
59020
|
+
[SITE_TOKEN_HEADER]: options.siteToken,
|
|
59021
|
+
accept: "application/json",
|
|
59022
|
+
"content-type": "application/json",
|
|
59023
|
+
...options.previewTag ? { "x-wix-route-serve": options.previewTag } : {}
|
|
59024
|
+
};
|
|
59025
|
+
}
|
|
59026
|
+
function objectFrom(text8) {
|
|
59027
|
+
try {
|
|
59028
|
+
const value = text8 ? JSON.parse(text8) : null;
|
|
59029
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
59030
|
+
} catch {
|
|
59031
|
+
return null;
|
|
59032
|
+
}
|
|
59033
|
+
}
|
|
59034
|
+
var SITE_TOKEN_HEADER, PREVIEW_TAG2, TIMEOUT_MS;
|
|
59035
|
+
var init_push_send = __esm({
|
|
59036
|
+
"../serve-cli/src/push-send.ts"() {
|
|
59037
|
+
init_credential_hosts();
|
|
59038
|
+
SITE_TOKEN_HEADER = "x-himi-site-token";
|
|
59039
|
+
PREVIEW_TAG2 = /^dp-[a-z0-9]+$/;
|
|
59040
|
+
TIMEOUT_MS = 6e4;
|
|
59041
|
+
}
|
|
59042
|
+
});
|
|
59043
|
+
|
|
59044
|
+
// ../../core/serve-control/src/push-send-service.ts
|
|
59045
|
+
var PUSH_SEND_ERROR_CODES;
|
|
59046
|
+
var init_push_send_service = __esm({
|
|
59047
|
+
"../../core/serve-control/src/push-send-service.ts"() {
|
|
59048
|
+
PUSH_SEND_ERROR_CODES = [
|
|
59049
|
+
"unauthorized",
|
|
59050
|
+
"token_revoked",
|
|
59051
|
+
"revocation_check_unavailable",
|
|
59052
|
+
"method_not_allowed",
|
|
59053
|
+
"rate_limited",
|
|
59054
|
+
"forbidden_cross_tenant",
|
|
59055
|
+
"forbidden_site",
|
|
59056
|
+
"site_check_unavailable",
|
|
59057
|
+
"push_send_unconfigured",
|
|
59058
|
+
"push_send_unavailable",
|
|
59059
|
+
"invalid_input",
|
|
59060
|
+
"payload_too_large",
|
|
59061
|
+
"unknown_field",
|
|
59062
|
+
"invalid_app_id",
|
|
59063
|
+
"invalid_site_id",
|
|
59064
|
+
"invalid_member_email",
|
|
59065
|
+
"invalid_title",
|
|
59066
|
+
"invalid_body",
|
|
59067
|
+
"invalid_link",
|
|
59068
|
+
"invalid_request_id",
|
|
59069
|
+
"invalid_dry_run",
|
|
59070
|
+
"link_not_routable",
|
|
59071
|
+
"app_config_unavailable",
|
|
59072
|
+
"push_not_ready",
|
|
59073
|
+
"site_token_required",
|
|
59074
|
+
"site_token_invalid",
|
|
59075
|
+
"site_token_unverifiable",
|
|
59076
|
+
"member_not_found",
|
|
59077
|
+
"member_ambiguous",
|
|
59078
|
+
"member_not_active",
|
|
59079
|
+
"member_read_forbidden",
|
|
59080
|
+
"member_lookup_unavailable",
|
|
59081
|
+
"ping_unconfigured",
|
|
59082
|
+
"ping_permission_denied",
|
|
59083
|
+
"ping_rejected",
|
|
59084
|
+
"ping_rate_limited",
|
|
59085
|
+
"send_outcome_unknown"
|
|
59086
|
+
];
|
|
59087
|
+
}
|
|
59088
|
+
});
|
|
59089
|
+
|
|
59090
|
+
// src/notify-ping.ts
|
|
59091
|
+
var notify_ping_exports = {};
|
|
59092
|
+
__export(notify_ping_exports, {
|
|
59093
|
+
runNotifyPing: () => runNotifyPing
|
|
59094
|
+
});
|
|
59095
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
59096
|
+
import { resolve as resolve49 } from "node:path";
|
|
59097
|
+
function refuse4(io, error, hint, exit = 2, extra = {}) {
|
|
59098
|
+
io.error(JSON.stringify({ ok: false, error, ...hint ? { hint } : {}, ...extra }));
|
|
59099
|
+
return exit;
|
|
59100
|
+
}
|
|
59101
|
+
async function runNotifyPing(flags, io, deps = {}) {
|
|
59102
|
+
const flagError = flagRefusal(flags);
|
|
59103
|
+
if (flagError) return refuse4(io, flagError);
|
|
59104
|
+
const input = mergePushInputs(pushInputsFromContent(resolve49(text7(flags.content) ?? process.cwd())), flags);
|
|
59105
|
+
const missing = [...input.app ? [] : ["app"], ...input.site ? [] : ["site"], ...REQUIRED_FLAGS.filter((f) => !text7(flags[f]))];
|
|
59106
|
+
if (missing.length) return refuse4(io, "needs_input", "Name the app and site (or --content), the member's login email, a title and a link.", 2, { missing });
|
|
59107
|
+
const requestId2 = text7(flags["request-id"]) ?? (deps.newRequestId ?? randomUUID5)();
|
|
59108
|
+
if (!UUID_RE.test(requestId2)) return refuse4(io, "invalid_request_id", "Reuse the requestId a previous send printed.");
|
|
59109
|
+
const owner = await hostedOwnerSession(flags, deps);
|
|
59110
|
+
if (!owner.ok) return refuse4(io, owner.error, owner.hint, owner.exit);
|
|
59111
|
+
const { session } = owner;
|
|
59112
|
+
const ownership = await verifyAppOwnership({ ownerIssue: session.ownerIssue, controlToken: session.controlToken, declaredName: input.app, fetchImpl: deps.fetchImpl });
|
|
59113
|
+
if (ownership.state !== "owned") return refuse4(io, `app_ownership_${ownership.state}`, "Sending requires the app owner.", 1);
|
|
59114
|
+
let siteToken2;
|
|
59115
|
+
try {
|
|
59116
|
+
siteToken2 = await (deps.siteToken ?? siteToken)(input.site);
|
|
59117
|
+
} catch {
|
|
59118
|
+
return refuse4(io, "wix_site_token_required", "Run wix login as the site owner; the site token reads this site's members.", 1);
|
|
59119
|
+
}
|
|
59120
|
+
const body = {
|
|
59121
|
+
app: ownership.slug,
|
|
59122
|
+
site: input.site,
|
|
59123
|
+
memberEmail: text7(flags["member-email"]),
|
|
59124
|
+
title: text7(flags.title),
|
|
59125
|
+
...text7(flags.body) ? { body: text7(flags.body) } : {},
|
|
59126
|
+
link: text7(flags.link),
|
|
59127
|
+
requestId: requestId2,
|
|
59128
|
+
...flags["dry-run"] === true ? { dryRun: true } : {}
|
|
59129
|
+
};
|
|
59130
|
+
const http = await pushSendRequest({ target: session.target, wixToken: session.wixToken, siteToken: siteToken2, body, previewTag: session.previewTag, fetchImpl: deps.fetchImpl });
|
|
59131
|
+
return report2(http, body, flags, io);
|
|
59132
|
+
}
|
|
59133
|
+
function flagRefusal(flags) {
|
|
59134
|
+
if (LOCAL_SENDER_FLAGS.some((f) => flags[f] !== void 0)) return "flag_not_supported_with_ping";
|
|
59135
|
+
if (SWITCHES.some((f) => flags[f] !== void 0 && typeof flags[f] !== "boolean")) return "invalid_boolean_flag";
|
|
59136
|
+
return void 0;
|
|
59137
|
+
}
|
|
59138
|
+
function report2(http, sent, flags, io) {
|
|
59139
|
+
const code = refusalCode(http);
|
|
59140
|
+
if (code === "send_outcome_unknown") {
|
|
59141
|
+
return refuse4(io, code, `Ping may have accepted it. Re-run the same command with --request-id ${sent.requestId}: Ping runs one request at most once per site for 24 hours.`, 1, { requestId: sent.requestId });
|
|
59142
|
+
}
|
|
59143
|
+
if (code === "rate_limited" && http.retryAfterSec) {
|
|
59144
|
+
return refuse4(io, code, `Test sends are limited per owner; retry in ${http.retryAfterSec} seconds.`, 1, { retryAfterSec: http.retryAfterSec });
|
|
59145
|
+
}
|
|
59146
|
+
if (code) return refuse4(io, code, HINTS[code], 1);
|
|
59147
|
+
if (flags.json === true) io.log(JSON.stringify(http.body, null, 2));
|
|
59148
|
+
else io.log(summary(http.body ?? {}, sent));
|
|
59149
|
+
return 0;
|
|
59150
|
+
}
|
|
59151
|
+
function refusalCode(http) {
|
|
59152
|
+
if (http.error) return http.error === "push_send_unreachable" ? "send_outcome_unknown" : http.error;
|
|
59153
|
+
if (http.ok && (http.body?.state === "accepted" || http.body?.state === "dry_run")) return void 0;
|
|
59154
|
+
const code = String(http.body?.error ?? "");
|
|
59155
|
+
if (KNOWN_CODES.has(code)) return code;
|
|
59156
|
+
return http.ok || http.status >= 500 ? "send_outcome_unknown" : "push_send_failed";
|
|
59157
|
+
}
|
|
59158
|
+
function summary(result, sent) {
|
|
59159
|
+
if (result.state === "dry_run") {
|
|
59160
|
+
return `Dry run passed: ${sent.memberEmail} is an approved member, the link routes, and push is ready for ${sent.app}. No notification was sent.`;
|
|
59161
|
+
}
|
|
59162
|
+
if (result.deduplicated === true) {
|
|
59163
|
+
return `Ping already ran request ${sent.requestId}; the earlier send stands and no second notification was sent.`;
|
|
59164
|
+
}
|
|
59165
|
+
return `Ping accepted request ${sent.requestId} for ${sent.memberEmail}. Ping does not confirm delivery: it reaches the member's signed-in devices with notifications allowed.`;
|
|
59166
|
+
}
|
|
59167
|
+
var LOCAL_SENDER_FLAGS, SWITCHES, REQUIRED_FLAGS, UUID_RE, KNOWN_CODES, HINTS, text7;
|
|
59168
|
+
var init_notify_ping = __esm({
|
|
59169
|
+
"src/notify-ping.ts"() {
|
|
59170
|
+
init_push_send();
|
|
59171
|
+
init_push_send_service();
|
|
59172
|
+
init_hosted_owner();
|
|
59173
|
+
init_owner_claim();
|
|
59174
|
+
init_push_onboarding();
|
|
59175
|
+
init_wix_owner();
|
|
59176
|
+
LOCAL_SENDER_FLAGS = ["token", "device", "all", "admin-url", "admin-session", "silent", "badge", "data", "category", "channel", "platform", "bundle-id"];
|
|
59177
|
+
SWITCHES = ["json", "dry-run"];
|
|
59178
|
+
REQUIRED_FLAGS = ["member-email", "title", "link"];
|
|
59179
|
+
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
59180
|
+
KNOWN_CODES = new Set(PUSH_SEND_ERROR_CODES);
|
|
59181
|
+
HINTS = {
|
|
59182
|
+
push_not_ready: "Run himi notify setup for this app and site first; status must be ready.",
|
|
59183
|
+
member_not_found: "Use the login email of an approved member of this site.",
|
|
59184
|
+
member_ambiguous: "More than one member has this login email; resolve it in the site's Members.",
|
|
59185
|
+
member_not_active: "The member must be approved before they can be notified.",
|
|
59186
|
+
link_not_routable: "Link to a screen this app's published release ships, e.g. <scheme>://screen/<screenId>?id=\u2026.",
|
|
59187
|
+
forbidden_site: "The Wix account from wix login must own this site.",
|
|
59188
|
+
site_token_invalid: "Run wix login as the site owner; the site token must belong to the app owner's account.",
|
|
59189
|
+
ping_permission_denied: "Himi Serve is not permitted to send through Ping; this is a platform issue, not your setup."
|
|
59190
|
+
};
|
|
59191
|
+
text7 = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
59192
|
+
}
|
|
59193
|
+
});
|
|
59194
|
+
|
|
58791
59195
|
// src/notify.ts
|
|
58792
59196
|
var notify_exports = {};
|
|
58793
59197
|
__export(notify_exports, {
|
|
@@ -58890,7 +59294,7 @@ async function runNotify(pos, flags, io, deps = {}) {
|
|
|
58890
59294
|
} catch {
|
|
58891
59295
|
reachable = false;
|
|
58892
59296
|
}
|
|
58893
|
-
const
|
|
59297
|
+
const report3 = {
|
|
58894
59298
|
// BOTH platforms must be ready. Considering only iOS meant a server with an APNs
|
|
58895
59299
|
// key but no Firebase credentials reported ok and exited 0, while every Android
|
|
58896
59300
|
// send would fail — from the command whose whole job is to answer that question.
|
|
@@ -58903,15 +59307,15 @@ async function runNotify(pos, flags, io, deps = {}) {
|
|
|
58903
59307
|
...reachable && authed && !senders ? { hint: "this admin-server predates /v1/admin/push-doctor \u2014 upgrade it to see sender readiness" } : {}
|
|
58904
59308
|
};
|
|
58905
59309
|
if (json6) {
|
|
58906
|
-
print(
|
|
58907
|
-
return
|
|
59310
|
+
print(report3);
|
|
59311
|
+
return report3.ok ? 0 : 1;
|
|
58908
59312
|
}
|
|
58909
59313
|
io.log(
|
|
58910
59314
|
`admin ${adminUrl} ${reachable ? authed ? "reachable, authenticated" : "reachable, NOT authenticated" : "UNREACHABLE"}`
|
|
58911
59315
|
);
|
|
58912
59316
|
if (!senders) {
|
|
58913
59317
|
io.log("senders unknown \u2014 credentials live on the admin-server, and it did not report them");
|
|
58914
|
-
if (
|
|
59318
|
+
if (report3.hint) io.log(` ${report3.hint}`);
|
|
58915
59319
|
return 1;
|
|
58916
59320
|
}
|
|
58917
59321
|
for (const [platform, r] of [
|
|
@@ -58921,7 +59325,7 @@ async function runNotify(pos, flags, io, deps = {}) {
|
|
|
58921
59325
|
io.log(`${platform.padEnd(11)} ${r.sender}${r.note ? ` \u2014 ${r.note}` : ""}`);
|
|
58922
59326
|
if (r.missing.length) io.log(` missing on the server: ${r.missing.join(", ")}`);
|
|
58923
59327
|
}
|
|
58924
|
-
return
|
|
59328
|
+
return report3.ok ? 0 : 1;
|
|
58925
59329
|
}
|
|
58926
59330
|
const app = str9(flags.app);
|
|
58927
59331
|
if (!app) {
|
|
@@ -59100,10 +59504,10 @@ function makeTransport(fetchImpl, auth, timeoutMs = DEFAULT_TIMEOUT_MS4) {
|
|
|
59100
59504
|
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
59101
59505
|
signal: AbortSignal.timeout(timeoutMs)
|
|
59102
59506
|
});
|
|
59103
|
-
const
|
|
59104
|
-
let parsed =
|
|
59507
|
+
const text8 = await res.text();
|
|
59508
|
+
let parsed = text8;
|
|
59105
59509
|
try {
|
|
59106
|
-
parsed =
|
|
59510
|
+
parsed = text8 ? JSON.parse(text8) : null;
|
|
59107
59511
|
} catch {
|
|
59108
59512
|
}
|
|
59109
59513
|
if (!res.ok) throw new WixApiError2(res.status, method, path2, parsed);
|
|
@@ -59200,10 +59604,10 @@ async function mintVisitorToken(fetchImpl, clientId) {
|
|
|
59200
59604
|
body: JSON.stringify({ clientId, grantType: "anonymous" }),
|
|
59201
59605
|
signal: AbortSignal.timeout(8e3)
|
|
59202
59606
|
});
|
|
59203
|
-
const
|
|
59204
|
-
let parsed =
|
|
59607
|
+
const text8 = await res.text();
|
|
59608
|
+
let parsed = text8;
|
|
59205
59609
|
try {
|
|
59206
|
-
parsed =
|
|
59610
|
+
parsed = text8 ? JSON.parse(text8) : null;
|
|
59207
59611
|
} catch {
|
|
59208
59612
|
}
|
|
59209
59613
|
if (!res.ok) throw new WixApiError2(res.status, "POST", "/oauth2/token", parsed);
|
|
@@ -59218,28 +59622,28 @@ var init_visitor_auth = __esm({
|
|
|
59218
59622
|
});
|
|
59219
59623
|
|
|
59220
59624
|
// src/site-analyze/copy-clean.ts
|
|
59221
|
-
function isTemplatePlaceholder(
|
|
59222
|
-
return PLACEHOLDER_RE.test(
|
|
59625
|
+
function isTemplatePlaceholder(text8) {
|
|
59626
|
+
return PLACEHOLDER_RE.test(text8);
|
|
59223
59627
|
}
|
|
59224
59628
|
function cleanText(raw, cap) {
|
|
59225
59629
|
const s = String(raw ?? "").replace(/[\u0000-\u001F\u007F]/g, " ").replace(/\s+/g, " ").trim();
|
|
59226
59630
|
return s.length > cap ? `${s.slice(0, cap - 1).trimEnd()}\u2026` : s;
|
|
59227
59631
|
}
|
|
59228
|
-
function stripPii(
|
|
59229
|
-
return
|
|
59632
|
+
function stripPii(text8) {
|
|
59633
|
+
return text8.replace(EMAIL_RE2, "").replace(PHONE_SHAPE_RE, (m) => {
|
|
59230
59634
|
const digits2 = (m.match(ANY_DIGIT_RE) ?? []).length;
|
|
59231
59635
|
return digits2 >= PHONE_MIN_DIGITS && PHONE_DIGIT_RUN_RE.test(m) ? "" : m;
|
|
59232
59636
|
}).replace(/\s+/g, " ").trim();
|
|
59233
59637
|
}
|
|
59234
|
-
function isBoilerplate(
|
|
59235
|
-
return BOILERPLATE_RE.test(
|
|
59638
|
+
function isBoilerplate(text8) {
|
|
59639
|
+
return BOILERPLATE_RE.test(text8) || PLACEHOLDER_RE.test(text8);
|
|
59236
59640
|
}
|
|
59237
59641
|
function copyOrDrop(raw, cap) {
|
|
59238
|
-
const
|
|
59239
|
-
if (
|
|
59240
|
-
if (isBoilerplate(
|
|
59241
|
-
if (!/[A-Za-zͰ--]/.test(
|
|
59242
|
-
return
|
|
59642
|
+
const text8 = stripPii(cleanText(raw, cap));
|
|
59643
|
+
if (text8.length < 2) return "";
|
|
59644
|
+
if (isBoilerplate(text8)) return "";
|
|
59645
|
+
if (!/[A-Za-zͰ--]/.test(text8)) return "";
|
|
59646
|
+
return text8;
|
|
59243
59647
|
}
|
|
59244
59648
|
function dedupe2(values, cap) {
|
|
59245
59649
|
const out2 = [];
|
|
@@ -59273,9 +59677,9 @@ var init_copy_clean = __esm({
|
|
|
59273
59677
|
function genericDescription(it) {
|
|
59274
59678
|
for (const field of DESCRIPTION_FIELDS) {
|
|
59275
59679
|
const raw = it[field];
|
|
59276
|
-
const
|
|
59277
|
-
if (!
|
|
59278
|
-
const plain2 =
|
|
59680
|
+
const text8 = typeof raw === "string" ? raw : str6(raw?.text);
|
|
59681
|
+
if (!text8) continue;
|
|
59682
|
+
const plain2 = text8.replace(/<[^>]*>/g, " ").replace(/ /g, " ");
|
|
59279
59683
|
if (plain2.trim()) return plain2;
|
|
59280
59684
|
}
|
|
59281
59685
|
return void 0;
|
|
@@ -59388,7 +59792,7 @@ function formLabels(x, depth = 0, out2 = []) {
|
|
|
59388
59792
|
if (typeof x !== "object") return out2;
|
|
59389
59793
|
const o = x;
|
|
59390
59794
|
const label2 = str6(o.label ?? o.displayName ?? o.fieldTitle ?? o.title);
|
|
59391
|
-
if (label2 && !
|
|
59795
|
+
if (label2 && !UUID_RE2.test(label2) && label2.length < 80 && !out2.includes(label2)) out2.push(label2);
|
|
59392
59796
|
for (const k of ["fields", "formFields", "form", "schema", "properties"]) {
|
|
59393
59797
|
if (o[k] != null) formLabels(o[k], depth + 1, out2);
|
|
59394
59798
|
}
|
|
@@ -59506,7 +59910,7 @@ function sampleTitle(data) {
|
|
|
59506
59910
|
for (const [k, v] of Object.entries(data)) {
|
|
59507
59911
|
if (k.startsWith("_") || typeof v !== "string") continue;
|
|
59508
59912
|
const s = v.trim();
|
|
59509
|
-
if (s.length > 2 && s.length < 80 && !s.includes("@") && !/^https?:/i.test(s) && !
|
|
59913
|
+
if (s.length > 2 && s.length < 80 && !s.includes("@") && !/^https?:/i.test(s) && !UUID_RE2.test(s)) return s;
|
|
59510
59914
|
}
|
|
59511
59915
|
return void 0;
|
|
59512
59916
|
}
|
|
@@ -59605,7 +60009,7 @@ async function readMemberRoles(call) {
|
|
|
59605
60009
|
if (sawAuthError) return { names: [], status: "error" };
|
|
59606
60010
|
return { names: [], status: "empty" };
|
|
59607
60011
|
}
|
|
59608
|
-
var asArr, str6, DESCRIPTION_FIELDS,
|
|
60012
|
+
var asArr, str6, DESCRIPTION_FIELDS, UUID_RE2, CORE_PROBES, OPTIONAL_PROBES, ALL, CMS_LIST_LIMIT;
|
|
59609
60013
|
var init_probes = __esm({
|
|
59610
60014
|
"src/site-analyze/probes.ts"() {
|
|
59611
60015
|
init_rest();
|
|
@@ -59613,7 +60017,7 @@ var init_probes = __esm({
|
|
|
59613
60017
|
asArr = (x) => Array.isArray(x) ? x : [];
|
|
59614
60018
|
str6 = (x) => typeof x === "string" ? x : "";
|
|
59615
60019
|
DESCRIPTION_FIELDS = ["plainDescription", "description", "shortDescription", "excerpt", "tagLine", "subtitle"];
|
|
59616
|
-
|
|
60020
|
+
UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
59617
60021
|
CORE_PROBES = [
|
|
59618
60022
|
{
|
|
59619
60023
|
vertical: "store",
|
|
@@ -60320,12 +60724,12 @@ function mergeWst(pages) {
|
|
|
60320
60724
|
return acc;
|
|
60321
60725
|
}
|
|
60322
60726
|
function fontSlug(value) {
|
|
60323
|
-
let
|
|
60727
|
+
let text8 = value;
|
|
60324
60728
|
try {
|
|
60325
|
-
|
|
60729
|
+
text8 = decodeURIComponent(value);
|
|
60326
60730
|
} catch {
|
|
60327
60731
|
}
|
|
60328
|
-
return
|
|
60732
|
+
return text8.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
60329
60733
|
}
|
|
60330
60734
|
function fontUrlSlug(url) {
|
|
60331
60735
|
const last = url.split("?")[0].split("#")[0].split("/").pop() ?? "";
|
|
@@ -60499,8 +60903,8 @@ function parsePublishedHtml(html) {
|
|
|
60499
60903
|
const rawHeadings = [];
|
|
60500
60904
|
HEADING_RE.lastIndex = 0;
|
|
60501
60905
|
while (m = HEADING_RE.exec(html)) {
|
|
60502
|
-
const
|
|
60503
|
-
if (
|
|
60906
|
+
const text8 = copyOrDrop(textFromHtml(m[1] ?? ""), 120);
|
|
60907
|
+
if (text8) rawHeadings.push(text8);
|
|
60504
60908
|
if (rawHeadings.length >= 40) break;
|
|
60505
60909
|
}
|
|
60506
60910
|
const headings = dedupe2(rawHeadings, 20);
|
|
@@ -61327,16 +61731,16 @@ function isPrivateV4([a, b, c]) {
|
|
|
61327
61731
|
return a >= 224;
|
|
61328
61732
|
}
|
|
61329
61733
|
function v6Groups(host) {
|
|
61330
|
-
let
|
|
61331
|
-
const dotted = /:((?:\d{1,3}\.){3}\d{1,3})$/.exec(
|
|
61734
|
+
let text8 = host;
|
|
61735
|
+
const dotted = /:((?:\d{1,3}\.){3}\d{1,3})$/.exec(text8);
|
|
61332
61736
|
if (dotted) {
|
|
61333
61737
|
const octets = ipv4Octets(dotted[1]);
|
|
61334
61738
|
if (!octets) return null;
|
|
61335
61739
|
const hi = (octets[0] << 8 | octets[1]).toString(16);
|
|
61336
61740
|
const lo = (octets[2] << 8 | octets[3]).toString(16);
|
|
61337
|
-
|
|
61741
|
+
text8 = `${text8.slice(0, dotted.index)}:${hi}:${lo}`;
|
|
61338
61742
|
}
|
|
61339
|
-
const halves =
|
|
61743
|
+
const halves = text8.split("::");
|
|
61340
61744
|
if (halves.length > 2) return null;
|
|
61341
61745
|
const parts = (part) => part ? part.split(":") : [];
|
|
61342
61746
|
const head = parts(halves[0]);
|
|
@@ -61418,10 +61822,10 @@ function assertPublicSiteUrl(raw) {
|
|
|
61418
61822
|
if (refusal) throw new Error(refusal);
|
|
61419
61823
|
}
|
|
61420
61824
|
function resolveAll(hostname2) {
|
|
61421
|
-
return new Promise((
|
|
61825
|
+
return new Promise((resolve53, reject) => {
|
|
61422
61826
|
dnsLookup(hostname2, { all: true, verbatim: true }, (err, addresses) => {
|
|
61423
61827
|
if (err) reject(err);
|
|
61424
|
-
else
|
|
61828
|
+
else resolve53(addresses);
|
|
61425
61829
|
});
|
|
61426
61830
|
});
|
|
61427
61831
|
}
|
|
@@ -61429,13 +61833,13 @@ function withAbort(start, signal, what) {
|
|
|
61429
61833
|
if (signal?.aborted) return Promise.reject(new Error(`${what} aborted`));
|
|
61430
61834
|
const work = start();
|
|
61431
61835
|
if (!signal) return work;
|
|
61432
|
-
return new Promise((
|
|
61836
|
+
return new Promise((resolve53, reject) => {
|
|
61433
61837
|
const onAbort = () => reject(new Error(`${what} aborted`));
|
|
61434
61838
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
61435
|
-
work.then(
|
|
61839
|
+
work.then(resolve53, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
61436
61840
|
});
|
|
61437
61841
|
}
|
|
61438
|
-
async function pinAddress(url,
|
|
61842
|
+
async function pinAddress(url, resolve53 = resolveAll, signal) {
|
|
61439
61843
|
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
61440
61844
|
if (ipv4Octets(host)) {
|
|
61441
61845
|
if (isBlockedAddress(host)) throw new Error(`${host} is a blocked address`);
|
|
@@ -61445,7 +61849,7 @@ async function pinAddress(url, resolve52 = resolveAll, signal) {
|
|
|
61445
61849
|
if (isBlockedAddress(host)) throw new Error(`${host} is a blocked address`);
|
|
61446
61850
|
return { address: host, family: 6 };
|
|
61447
61851
|
}
|
|
61448
|
-
const addresses = await withAbort(() =>
|
|
61852
|
+
const addresses = await withAbort(() => resolve53(host), signal, `${host} lookup`);
|
|
61449
61853
|
if (!addresses.length) throw new Error(`${host} resolved to no address`);
|
|
61450
61854
|
const blocked = addresses.filter((a) => isBlockedAddress(a.address)).map((a) => a.address);
|
|
61451
61855
|
if (blocked.length) throw new Error(`${host} resolves to a blocked address (${blocked.join(", ")})`);
|
|
@@ -61485,7 +61889,7 @@ function copyIncomingHeaders(headers2) {
|
|
|
61485
61889
|
return out2;
|
|
61486
61890
|
}
|
|
61487
61891
|
function requestOnce(url, pinned, init) {
|
|
61488
|
-
return new Promise((
|
|
61892
|
+
return new Promise((resolve53, reject) => {
|
|
61489
61893
|
const headers2 = { host: url.host, "accept-encoding": "identity" };
|
|
61490
61894
|
new Headers(init.headers ?? {}).forEach((value, key2) => {
|
|
61491
61895
|
if (key2.toLowerCase() !== "host") headers2[key2] = value;
|
|
@@ -61515,7 +61919,7 @@ function requestOnce(url, pinned, init) {
|
|
|
61515
61919
|
reject(err);
|
|
61516
61920
|
return;
|
|
61517
61921
|
}
|
|
61518
|
-
|
|
61922
|
+
resolve53(new Response(body, { status, statusText: res.statusMessage ?? "", headers: out2 }));
|
|
61519
61923
|
}
|
|
61520
61924
|
);
|
|
61521
61925
|
req.on("error", reject);
|
|
@@ -61591,8 +61995,8 @@ var init_net_guard = __esm({
|
|
|
61591
61995
|
function textFields(look) {
|
|
61592
61996
|
if (look.unavailable) return [];
|
|
61593
61997
|
const out2 = [];
|
|
61594
|
-
const add = (
|
|
61595
|
-
if (
|
|
61998
|
+
const add = (text8, where, narrative = false) => {
|
|
61999
|
+
if (text8 && text8.trim()) out2.push({ text: text8, where, narrative });
|
|
61596
62000
|
};
|
|
61597
62001
|
for (const label2 of look.navLabels) add(label2, "nav");
|
|
61598
62002
|
for (const p of look.pages) {
|
|
@@ -61983,8 +62387,8 @@ async function analyzeSite(input, ports) {
|
|
|
61983
62387
|
if (locale) plan.provisioning.locale = locale;
|
|
61984
62388
|
const velo = buildVeloChapter({ look, owner: ownerVelo });
|
|
61985
62389
|
const veloFiles = ownerVelo.files.flatMap((f) => {
|
|
61986
|
-
const
|
|
61987
|
-
return
|
|
62390
|
+
const text8 = f.content ?? f.excerpt;
|
|
62391
|
+
return text8 ? [{ path: f.path, text: text8 }] : [];
|
|
61988
62392
|
}).slice(0, 80);
|
|
61989
62393
|
const permissions = buildPermissionsChapter({
|
|
61990
62394
|
cms,
|
|
@@ -62206,8 +62610,8 @@ function cmsStatusNote(taxonomy) {
|
|
|
62206
62610
|
function yn(v) {
|
|
62207
62611
|
return v == null || v === "" ? "\u2014" : String(v);
|
|
62208
62612
|
}
|
|
62209
|
-
function isAuthPhrase(
|
|
62210
|
-
const t =
|
|
62613
|
+
function isAuthPhrase(text8) {
|
|
62614
|
+
const t = text8.trim();
|
|
62211
62615
|
return AUTH_ONLY_ASCII.test(t) || LOGIN_HE.includes(t);
|
|
62212
62616
|
}
|
|
62213
62617
|
function looksGated(page) {
|
|
@@ -62584,9 +62988,9 @@ function attachVeloSources(book, sources, put) {
|
|
|
62584
62988
|
if (isSecretVeloPath(f.path)) continue;
|
|
62585
62989
|
const rel = veloArtifactRelPath(f.path);
|
|
62586
62990
|
if (!rel) continue;
|
|
62587
|
-
const
|
|
62588
|
-
if (!
|
|
62589
|
-
put(rel,
|
|
62991
|
+
const text8 = byPath.get(f.path) ?? f.excerpt;
|
|
62992
|
+
if (!text8) continue;
|
|
62993
|
+
put(rel, text8);
|
|
62590
62994
|
f.localPath = rel;
|
|
62591
62995
|
}
|
|
62592
62996
|
}
|
|
@@ -62831,14 +63235,14 @@ var init_customer_build = __esm({
|
|
|
62831
63235
|
|
|
62832
63236
|
// src/site-analyze/apply.ts
|
|
62833
63237
|
import { existsSync as existsSync64, readFileSync as readFileSync76, writeFileSync as writeFileSync27, copyFileSync as copyFileSync3, mkdirSync as mkdirSync31, realpathSync as realpathSync9, statSync as statSync17 } from "node:fs";
|
|
62834
|
-
import { dirname as dirname35, isAbsolute as isAbsolute6, join as join63, resolve as
|
|
63238
|
+
import { dirname as dirname35, isAbsolute as isAbsolute6, join as join63, resolve as resolve50, sep as sep16 } from "node:path";
|
|
62835
63239
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
62836
63240
|
function unpairedRedirectNote(overlay) {
|
|
62837
63241
|
if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
|
|
62838
63242
|
return `no OAuth redirect paired with client ${overlay.wix.clientId} \u2014 the app (and prepare:customer) will refuse to start until one is set. Register it with \`himi site client ensure --site ${overlay.siteId?.trim() || "<msid>"} --redirect-uri <uri> --yes\`, then re-run analyze with \`--redirect-uri <uri>\` (or export HIMI_BRANDED_LITE_WIX_REDIRECT_URI)`;
|
|
62839
63243
|
}
|
|
62840
63244
|
function posix3(p) {
|
|
62841
|
-
return
|
|
63245
|
+
return resolve50(p).replace(/\\/g, "/");
|
|
62842
63246
|
}
|
|
62843
63247
|
function committedShellProvisioning(path2) {
|
|
62844
63248
|
return committedShellFor(posix3(path2));
|
|
@@ -62872,7 +63276,7 @@ function assertNotCommitted(path2, forbidden) {
|
|
|
62872
63276
|
`refusing to overwrite committed ${committedProvisioningPath(committed)} \u2014 use prepare:customer --contract`
|
|
62873
63277
|
);
|
|
62874
63278
|
}
|
|
62875
|
-
if (forbidden &&
|
|
63279
|
+
if (forbidden && resolve50(path2) === resolve50(forbidden)) {
|
|
62876
63280
|
const named = committedShellProvisioning(forbidden);
|
|
62877
63281
|
throw new Error(
|
|
62878
63282
|
`refusing to overwrite committed ${named ? committedProvisioningPath(named) : forbidden} \u2014 pass a content-package provisioning.json or use prepare:customer --contract`
|
|
@@ -62928,8 +63332,8 @@ function buildMergedContract(options) {
|
|
|
62928
63332
|
const staged = [];
|
|
62929
63333
|
const contained = (root, rel) => {
|
|
62930
63334
|
if (isAbsolute6(rel)) return void 0;
|
|
62931
|
-
const full =
|
|
62932
|
-
return full.startsWith(
|
|
63335
|
+
const full = resolve50(root, rel);
|
|
63336
|
+
return full.startsWith(resolve50(root) + sep16) ? full : void 0;
|
|
62933
63337
|
};
|
|
62934
63338
|
const stage = (fromRel, toRel) => {
|
|
62935
63339
|
const from = contained(packageDir, fromRel);
|
|
@@ -63018,7 +63422,7 @@ function catalogTypography(overlay, template) {
|
|
|
63018
63422
|
function staticFaqFor(overlay, overlayPath) {
|
|
63019
63423
|
const own3 = overlay.faq;
|
|
63020
63424
|
if (own3 !== void 0) return deriveStaticFaq(own3);
|
|
63021
|
-
const bookPath = join63(dirname35(
|
|
63425
|
+
const bookPath = join63(dirname35(resolve50(overlayPath)), "book.json");
|
|
63022
63426
|
if (!existsSync64(bookPath)) return void 0;
|
|
63023
63427
|
try {
|
|
63024
63428
|
const book = JSON.parse(readFileSync76(bookPath, "utf8"));
|
|
@@ -63032,7 +63436,7 @@ function staticFaqFor(overlay, overlayPath) {
|
|
|
63032
63436
|
}
|
|
63033
63437
|
}
|
|
63034
63438
|
async function applyOverlays(input) {
|
|
63035
|
-
const cwd =
|
|
63439
|
+
const cwd = resolve50(input.cwd);
|
|
63036
63440
|
const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
|
|
63037
63441
|
assertKnownShell(shell);
|
|
63038
63442
|
const overlay = JSON.parse(readFileSync76(input.overlayPath, "utf8"));
|
|
@@ -63056,7 +63460,7 @@ async function applyOverlays(input) {
|
|
|
63056
63460
|
`shell "${shell}" is a site shell: its contract names the customer's website (a \`site\` block), and this overlay has none. Re-run \`himi site analyze --url <site> --shell ${shell}\` to derive the site's page map (edit \`site.pages\` in provisioning.overlay.json if needed), then --apply.`
|
|
63057
63461
|
);
|
|
63058
63462
|
}
|
|
63059
|
-
const packageDir = dirname35(
|
|
63463
|
+
const packageDir = dirname35(resolve50(input.overlayPath));
|
|
63060
63464
|
const { path: contract, staged } = buildMergedContract({ appDir: appDir3, packageDir, overlay });
|
|
63061
63465
|
wrote.push(...staged);
|
|
63062
63466
|
const r = spawnSync4("npm", ["run", "prepare:customer", "--", "--contract", contract], {
|
|
@@ -63151,7 +63555,7 @@ async function applyOverlays(input) {
|
|
|
63151
63555
|
}
|
|
63152
63556
|
}
|
|
63153
63557
|
if (!input.iconPath || !existsSync64(input.iconPath)) {
|
|
63154
|
-
const book = input.iconPath ? dirname35(dirname35(
|
|
63558
|
+
const book = input.iconPath ? dirname35(dirname35(resolve50(input.iconPath))) : void 0;
|
|
63155
63559
|
notes.push(
|
|
63156
63560
|
book ? `no icon installed \u2014 ${siteLogoAbsence(book).hint}` : "no icon installed and no business book to say why \u2014 `himi icon generate` draws one from the accent, or `himi icon set <file.png>` installs the customer's own mark"
|
|
63157
63561
|
);
|
|
@@ -64270,10 +64674,10 @@ function permuteDomain(domain, allowSpecialUseDomain) {
|
|
|
64270
64674
|
}
|
|
64271
64675
|
function createPromiseCallback(cb) {
|
|
64272
64676
|
let callback;
|
|
64273
|
-
let
|
|
64677
|
+
let resolve53;
|
|
64274
64678
|
let reject;
|
|
64275
64679
|
const promise = new Promise((_resolve, _reject) => {
|
|
64276
|
-
|
|
64680
|
+
resolve53 = _resolve;
|
|
64277
64681
|
reject = _reject;
|
|
64278
64682
|
});
|
|
64279
64683
|
if (typeof cb === "function") {
|
|
@@ -64289,7 +64693,7 @@ function createPromiseCallback(cb) {
|
|
|
64289
64693
|
callback = (err, result) => {
|
|
64290
64694
|
try {
|
|
64291
64695
|
if (err) reject(err);
|
|
64292
|
-
else
|
|
64696
|
+
else resolve53(result);
|
|
64293
64697
|
} catch (e) {
|
|
64294
64698
|
reject(e instanceof Error ? e : new Error());
|
|
64295
64699
|
}
|
|
@@ -66356,8 +66760,8 @@ function copyFromEval(evaled) {
|
|
|
66356
66760
|
evaled.meta?.ogTitle,
|
|
66357
66761
|
evaled.meta?.ogDescription
|
|
66358
66762
|
].map((raw) => {
|
|
66359
|
-
const
|
|
66360
|
-
return
|
|
66763
|
+
const text8 = cleanText(raw, 400);
|
|
66764
|
+
return text8 && isTemplatePlaceholder(text8) ? stripPii(cleanText(text8, 90)) : "";
|
|
66361
66765
|
}),
|
|
66362
66766
|
8
|
|
66363
66767
|
);
|
|
@@ -66889,7 +67293,7 @@ var init_crawl_look = __esm({
|
|
|
66889
67293
|
});
|
|
66890
67294
|
|
|
66891
67295
|
// src/site-analyze/site-function.ts
|
|
66892
|
-
import { randomUUID as
|
|
67296
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
66893
67297
|
function createRevisionUrl(msid, esi) {
|
|
66894
67298
|
const q = new URLSearchParams({ editorSessionId: esi, esi, metaSiteId: msid });
|
|
66895
67299
|
return `https://editor.wix.com/html/v1/create_revision?${q}`;
|
|
@@ -67256,13 +67660,13 @@ async function veloCall(fetchImpl, method, url, opts = {}) {
|
|
|
67256
67660
|
body: opts.body === void 0 ? void 0 : JSON.stringify(opts.body),
|
|
67257
67661
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 3e4)
|
|
67258
67662
|
});
|
|
67259
|
-
const
|
|
67260
|
-
let json6 =
|
|
67663
|
+
const text8 = await res.text();
|
|
67664
|
+
let json6 = text8;
|
|
67261
67665
|
try {
|
|
67262
|
-
json6 =
|
|
67666
|
+
json6 = text8 ? JSON.parse(text8) : null;
|
|
67263
67667
|
} catch {
|
|
67264
67668
|
}
|
|
67265
|
-
return { status: res.status, json: json6, text:
|
|
67669
|
+
return { status: res.status, json: json6, text: text8 };
|
|
67266
67670
|
}
|
|
67267
67671
|
function snippet2(x, cap = 180) {
|
|
67268
67672
|
const s = typeof x === "string" ? x : JSON.stringify(x) ?? "";
|
|
@@ -67334,7 +67738,7 @@ function codeAppIdFromRevision(json6) {
|
|
|
67334
67738
|
async function snapshotAndPublish(fetchImpl, token, msid, opts = {}) {
|
|
67335
67739
|
const production = opts.production !== false;
|
|
67336
67740
|
const initiator = opts.initiator ?? "himi";
|
|
67337
|
-
const esi =
|
|
67741
|
+
const esi = randomUUID6();
|
|
67338
67742
|
const rev = await veloCall(fetchImpl, "POST", createRevisionUrl(msid, esi), {
|
|
67339
67743
|
token,
|
|
67340
67744
|
msid,
|
|
@@ -67431,7 +67835,7 @@ async function runSiteFunction(opts) {
|
|
|
67431
67835
|
if (!httpOk(patched)) {
|
|
67432
67836
|
return { ...base, error: `UpdateFiles ${patched.status}: ${snippet2(patched.json ?? patched.text)}` };
|
|
67433
67837
|
}
|
|
67434
|
-
let
|
|
67838
|
+
let report3 = {
|
|
67435
67839
|
...base,
|
|
67436
67840
|
ok: true,
|
|
67437
67841
|
source: extractHttpFunction(next, method, name),
|
|
@@ -67440,21 +67844,21 @@ async function runSiteFunction(opts) {
|
|
|
67440
67844
|
if (opts.publish) {
|
|
67441
67845
|
const snap = await snapshotAndPublish(fetchImpl, opts.token, opts.msid, { production: true, initiator: "himi" });
|
|
67442
67846
|
const pubOk = httpOk(snap.rev) && httpOk(snap.live);
|
|
67443
|
-
|
|
67444
|
-
...
|
|
67445
|
-
ok:
|
|
67847
|
+
report3 = {
|
|
67848
|
+
...report3,
|
|
67849
|
+
ok: report3.ok && pubOk,
|
|
67446
67850
|
revision: snap.revision,
|
|
67447
67851
|
codeAppId: snap.codeAppId,
|
|
67448
67852
|
publish: { revision: httpOk(snap.rev), rc: httpOk(snap.rc), live: httpOk(snap.live), status: snap.live?.status },
|
|
67449
|
-
error: pubOk ?
|
|
67853
|
+
error: pubOk ? report3.error : snippet2(snap.live?.json ?? snap.rev.json)
|
|
67450
67854
|
};
|
|
67451
67855
|
}
|
|
67452
67856
|
if (opts.invoke) {
|
|
67453
|
-
if (!opts.siteUrl) return { ...
|
|
67454
|
-
const invoked = await invokeAndReport(fetchImpl, opts,
|
|
67455
|
-
return { ...
|
|
67857
|
+
if (!opts.siteUrl) return { ...report3, ok: false, error: "put --invoke needs --url" };
|
|
67858
|
+
const invoked = await invokeAndReport(fetchImpl, opts, report3, name, method);
|
|
67859
|
+
return { ...report3, ...invoked, op: "put", ok: report3.ok && invoked.ok };
|
|
67456
67860
|
}
|
|
67457
|
-
return
|
|
67861
|
+
return report3;
|
|
67458
67862
|
}
|
|
67459
67863
|
async function invokeAndReport(fetchImpl, opts, base, name, method) {
|
|
67460
67864
|
const siteUrl = opts.siteUrl;
|
|
@@ -67519,7 +67923,7 @@ __export(cli_exports, {
|
|
|
67519
67923
|
runSiteFunctionCli: () => runSiteFunctionCli
|
|
67520
67924
|
});
|
|
67521
67925
|
import { readFileSync as readFileSync77 } from "node:fs";
|
|
67522
|
-
import { resolve as
|
|
67926
|
+
import { resolve as resolve51 } from "node:path";
|
|
67523
67927
|
function assetFetchFor(_opts) {
|
|
67524
67928
|
return pinnedFetch2;
|
|
67525
67929
|
}
|
|
@@ -67539,7 +67943,7 @@ async function runSiteAnalyze(opts) {
|
|
|
67539
67943
|
const assetFetch = assetFetchFor(opts);
|
|
67540
67944
|
const siteFetch = guardedSiteFetch;
|
|
67541
67945
|
const cwd = opts.cwd ?? process.cwd();
|
|
67542
|
-
const outDir =
|
|
67946
|
+
const outDir = resolve51(cwd, opts.outDir ?? ".himi/business-book");
|
|
67543
67947
|
let ownerAuth;
|
|
67544
67948
|
let msid;
|
|
67545
67949
|
const ownerSite = ownerSiteFor(opts);
|
|
@@ -67591,9 +67995,9 @@ async function runSiteAnalyze(opts) {
|
|
|
67591
67995
|
if (opts.apply) {
|
|
67592
67996
|
applied = await applyOverlays({
|
|
67593
67997
|
cwd,
|
|
67594
|
-
overlayPath:
|
|
67595
|
-
tokensPath:
|
|
67596
|
-
iconPath:
|
|
67998
|
+
overlayPath: resolve51(outDir, "provisioning.overlay.json"),
|
|
67999
|
+
tokensPath: resolve51(outDir, "tokens.overlay.json"),
|
|
68000
|
+
iconPath: resolve51(outDir, "media/icon.png"),
|
|
67597
68001
|
shell: opts.shell
|
|
67598
68002
|
});
|
|
67599
68003
|
const note = noteWithWarnings(applied.note, warnings);
|
|
@@ -67629,7 +68033,7 @@ async function runSiteFunctionCli(opts) {
|
|
|
67629
68033
|
const token = opts.token ?? await siteToken(msid);
|
|
67630
68034
|
let source;
|
|
67631
68035
|
if (opts.sourcePath) {
|
|
67632
|
-
const p =
|
|
68036
|
+
const p = resolve51(opts.cwd ?? process.cwd(), opts.sourcePath);
|
|
67633
68037
|
source = readFileSync77(p, "utf8");
|
|
67634
68038
|
}
|
|
67635
68039
|
let body;
|
|
@@ -67685,7 +68089,7 @@ async function accountToken2(spawnImpl = spawn6) {
|
|
|
67685
68089
|
cached2 = fromEnv;
|
|
67686
68090
|
return fromEnv;
|
|
67687
68091
|
}
|
|
67688
|
-
const token = await new Promise((
|
|
68092
|
+
const token = await new Promise((resolve53, reject) => {
|
|
67689
68093
|
let child;
|
|
67690
68094
|
try {
|
|
67691
68095
|
child = spawnImpl("wix", ["token"]);
|
|
@@ -67714,7 +68118,7 @@ async function accountToken2(spawnImpl = spawn6) {
|
|
|
67714
68118
|
const scan = (d) => {
|
|
67715
68119
|
out2 += String(d);
|
|
67716
68120
|
const m = out2.match(TOKEN_RE2);
|
|
67717
|
-
if (m) done(() =>
|
|
68121
|
+
if (m) done(() => resolve53(m[0]));
|
|
67718
68122
|
};
|
|
67719
68123
|
child.stdout?.on("data", scan);
|
|
67720
68124
|
child.stderr?.on("data", scan);
|
|
@@ -67757,10 +68161,10 @@ function accountTransport(fetchImpl = fetch, timeoutMs = DEFAULT_TIMEOUT_MS5, sp
|
|
|
67757
68161
|
}
|
|
67758
68162
|
throw new Error(`${method} ${path2} failed: ${err?.message ?? String(e)}`);
|
|
67759
68163
|
}
|
|
67760
|
-
const
|
|
67761
|
-
let parsed =
|
|
68164
|
+
const text8 = await res.text();
|
|
68165
|
+
let parsed = text8;
|
|
67762
68166
|
try {
|
|
67763
|
-
parsed =
|
|
68167
|
+
parsed = text8 ? JSON.parse(text8) : null;
|
|
67764
68168
|
} catch {
|
|
67765
68169
|
}
|
|
67766
68170
|
if (!res.ok) throw new WixApiError(res.status, method, path2, parsed);
|
|
@@ -67821,16 +68225,16 @@ async function installVerticals(msid, wanted, token, fetchImpl = fetch, timeoutM
|
|
|
67821
68225
|
body: JSON.stringify({ apps: wanted.map((w) => ({ id: w.appDefId })) }),
|
|
67822
68226
|
signal: AbortSignal.timeout(timeoutMs)
|
|
67823
68227
|
});
|
|
67824
|
-
const
|
|
68228
|
+
const text8 = await res.text();
|
|
67825
68229
|
if (!res.ok) {
|
|
67826
68230
|
return {
|
|
67827
68231
|
...empty,
|
|
67828
|
-
errors: wanted.map((w) => ({ ...w, message: `bulk-install -> ${res.status}: ${
|
|
68232
|
+
errors: wanted.map((w) => ({ ...w, message: `bulk-install -> ${res.status}: ${text8.slice(0, 200)}` }))
|
|
67829
68233
|
};
|
|
67830
68234
|
}
|
|
67831
68235
|
let body = {};
|
|
67832
68236
|
try {
|
|
67833
|
-
body =
|
|
68237
|
+
body = text8 ? JSON.parse(text8) : {};
|
|
67834
68238
|
} catch {
|
|
67835
68239
|
}
|
|
67836
68240
|
const nameOf = (appDefId) => wanted.find((w) => w.appDefId === appDefId)?.vertical ?? appDefId;
|
|
@@ -68433,9 +68837,9 @@ function redactVeloSource(source, knownSecrets = []) {
|
|
|
68433
68837
|
const visitLiterals = (node) => {
|
|
68434
68838
|
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isTemplateExpression(node)) {
|
|
68435
68839
|
const start = node.getStart(clean2);
|
|
68436
|
-
const
|
|
68437
|
-
const redacted = redactLiteral(
|
|
68438
|
-
if (redacted !==
|
|
68840
|
+
const text8 = source.slice(start, node.end);
|
|
68841
|
+
const redacted = redactLiteral(text8, start);
|
|
68842
|
+
if (redacted !== text8) {
|
|
68439
68843
|
edits.push({ start, end: node.end, text: redacted });
|
|
68440
68844
|
return;
|
|
68441
68845
|
}
|
|
@@ -68716,9 +69120,9 @@ var init_context = __esm({
|
|
|
68716
69120
|
import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync79, mkdirSync as mkdirSync32, writeFileSync as writeFileSync29, existsSync as existsSync66, rmSync as rmSync15, readdirSync as readdirSync30, statSync as statSync18, lstatSync as lstatSync13, accessSync, constants as constants2, renameSync as renameSync7, cpSync as cpSync3 } from "node:fs";
|
|
68717
69121
|
import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn7 } from "node:child_process";
|
|
68718
69122
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
68719
|
-
import { createHash as createHash31, randomUUID as
|
|
69123
|
+
import { createHash as createHash31, randomUUID as randomUUID7 } from "node:crypto";
|
|
68720
69124
|
import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
|
|
68721
|
-
import { join as join64, resolve as
|
|
69125
|
+
import { join as join64, resolve as resolve52, relative as relative13, basename as basename17, dirname as dirname36 } from "node:path";
|
|
68722
69126
|
import { fileURLToPath as fileURLToPath26 } from "node:url";
|
|
68723
69127
|
|
|
68724
69128
|
// ../serve-cli/src/client.ts
|
|
@@ -68759,10 +69163,10 @@ var ServeCliClient = class {
|
|
|
68759
69163
|
...carriesCredential ? { redirect: "error" } : {},
|
|
68760
69164
|
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
68761
69165
|
});
|
|
68762
|
-
const
|
|
69166
|
+
const text8 = await res.text();
|
|
68763
69167
|
let json6 = null;
|
|
68764
69168
|
try {
|
|
68765
|
-
json6 =
|
|
69169
|
+
json6 = text8 ? JSON.parse(text8) : null;
|
|
68766
69170
|
} catch {
|
|
68767
69171
|
json6 = null;
|
|
68768
69172
|
}
|
|
@@ -68770,7 +69174,7 @@ var ServeCliClient = class {
|
|
|
68770
69174
|
res.headers.forEach((v, k) => {
|
|
68771
69175
|
h[k] = v;
|
|
68772
69176
|
});
|
|
68773
|
-
return { status: res.status, ok: res.ok, json: json6, text:
|
|
69177
|
+
return { status: res.status, ok: res.ok, json: json6, text: text8, headers: h };
|
|
68774
69178
|
}
|
|
68775
69179
|
// ---- serve (device-facing reads) ----
|
|
68776
69180
|
health(plane) {
|
|
@@ -69335,8 +69739,8 @@ init_attestation();
|
|
|
69335
69739
|
|
|
69336
69740
|
// ../../core/admin-web/src/utils/qr-terminal.ts
|
|
69337
69741
|
init_qr();
|
|
69338
|
-
function qrTerminal(
|
|
69339
|
-
const { modules, size } = encodeQr(
|
|
69742
|
+
function qrTerminal(text8, opts = {}) {
|
|
69743
|
+
const { modules, size } = encodeQr(text8);
|
|
69340
69744
|
const quietZone = opts.quietZone ?? 4;
|
|
69341
69745
|
const dim = size + quietZone * 2;
|
|
69342
69746
|
const dark = (x, y) => x >= quietZone && x < quietZone + size && y >= quietZone && y < quietZone + size ? modules[y - quietZone][x - quietZone] : false;
|
|
@@ -69392,9 +69796,9 @@ function dpxSessionPresent(path2 = dpxSessionTokenPath()) {
|
|
|
69392
69796
|
return false;
|
|
69393
69797
|
}
|
|
69394
69798
|
}
|
|
69395
|
-
function isPortInUseError(
|
|
69396
|
-
if (!
|
|
69397
|
-
return /EADDRINUSE/i.test(
|
|
69799
|
+
function isPortInUseError(text8) {
|
|
69800
|
+
if (!text8) return false;
|
|
69801
|
+
return /EADDRINUSE/i.test(text8) || new RegExp(`(:::|:)${DPX_SESSION_PORT}\\b`).test(text8);
|
|
69398
69802
|
}
|
|
69399
69803
|
function parseDpxAuthStatusIdentity(stdout) {
|
|
69400
69804
|
if (!stdout) return void 0;
|
|
@@ -69459,7 +69863,7 @@ async function browserLogin(opts) {
|
|
|
69459
69863
|
const log = opts.log ?? (() => {
|
|
69460
69864
|
});
|
|
69461
69865
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
69462
|
-
return await new Promise((
|
|
69866
|
+
return await new Promise((resolve53, reject) => {
|
|
69463
69867
|
let settled = false;
|
|
69464
69868
|
let loginUrl = "";
|
|
69465
69869
|
const server = createServer();
|
|
@@ -69523,7 +69927,7 @@ async function browserLogin(opts) {
|
|
|
69523
69927
|
return;
|
|
69524
69928
|
}
|
|
69525
69929
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", connection: "close" }).end(CLOSE_PAGE);
|
|
69526
|
-
finish2(() =>
|
|
69930
|
+
finish2(() => resolve53({ token, loginUrl }));
|
|
69527
69931
|
});
|
|
69528
69932
|
});
|
|
69529
69933
|
server.on("error", (err) => finish2(() => reject(err)));
|
|
@@ -69787,7 +70191,7 @@ async function queryCms(opts) {
|
|
|
69787
70191
|
async function createCms(opts) {
|
|
69788
70192
|
const { app, cms, call, dryRun = false, allowDestructive = false, applyPermissions = false } = opts;
|
|
69789
70193
|
const prefix = namespacePrefix(app);
|
|
69790
|
-
const
|
|
70194
|
+
const report3 = {
|
|
69791
70195
|
ok: true,
|
|
69792
70196
|
app,
|
|
69793
70197
|
namespace: prefix,
|
|
@@ -69809,19 +70213,19 @@ async function createCms(opts) {
|
|
|
69809
70213
|
for (const spec of cms?.collections ?? []) {
|
|
69810
70214
|
const id = physicalId(spec, app);
|
|
69811
70215
|
if (spec.wixId) {
|
|
69812
|
-
|
|
70216
|
+
report3.skipped.push({
|
|
69813
70217
|
collection: id,
|
|
69814
70218
|
reason: remoteById.has(id) ? "adopted (read-only) \u2014 himi never changes the schema of a collection it does not own" : `adopted but absent from this site \u2014 create it in the Wix dashboard, or declare it as owned ("${prefix}\u2026")`
|
|
69815
70219
|
});
|
|
69816
|
-
if (!remoteById.has(id))
|
|
70220
|
+
if (!remoteById.has(id)) report3.ok = false;
|
|
69817
70221
|
continue;
|
|
69818
70222
|
}
|
|
69819
70223
|
if (!spec.id?.startsWith(prefix)) {
|
|
69820
|
-
|
|
70224
|
+
report3.refused.push({
|
|
69821
70225
|
collection: id || "<unnamed>",
|
|
69822
70226
|
reason: `"${id}" is outside this app's namespace "${prefix}" \u2014 himi only writes collections it owns. Rename it in config.ts, or bind an existing collection read-only with \`wixId\`.`
|
|
69823
70227
|
});
|
|
69824
|
-
|
|
70228
|
+
report3.ok = false;
|
|
69825
70229
|
continue;
|
|
69826
70230
|
}
|
|
69827
70231
|
const existing = remoteById.get(id);
|
|
@@ -69829,35 +70233,35 @@ async function createCms(opts) {
|
|
|
69829
70233
|
if (!dryRun) {
|
|
69830
70234
|
await call("POST", "/data/v2/collections", { collection: toWixCollection(spec, app) });
|
|
69831
70235
|
}
|
|
69832
|
-
|
|
70236
|
+
report3.created.push(id);
|
|
69833
70237
|
continue;
|
|
69834
70238
|
}
|
|
69835
70239
|
const permDrift = diffPermissions(spec, existing);
|
|
69836
|
-
for (const d of permDrift)
|
|
70240
|
+
for (const d of permDrift) report3.permissionDrift.push({ collection: id, ...d });
|
|
69837
70241
|
const applyingPermissions = applyPermissions && permDrift.length > 0;
|
|
69838
70242
|
const drift = diffFields(spec, existing);
|
|
69839
70243
|
const extras = drift.filter((d) => d.kind === "extra-field");
|
|
69840
70244
|
const mismatches = drift.filter((d) => d.kind === "type-mismatch");
|
|
69841
70245
|
const missing = drift.filter((d) => d.kind === "missing-field");
|
|
69842
70246
|
for (const m of mismatches) {
|
|
69843
|
-
|
|
69844
|
-
|
|
70247
|
+
report3.skipped.push({ collection: id, reason: m.detail });
|
|
70248
|
+
report3.ok = false;
|
|
69845
70249
|
}
|
|
69846
70250
|
for (const e of extras) {
|
|
69847
70251
|
if (allowDestructive) continue;
|
|
69848
|
-
|
|
70252
|
+
report3.skipped.push({ collection: id, reason: e.detail });
|
|
69849
70253
|
}
|
|
69850
70254
|
if (mismatches.length > 0) continue;
|
|
69851
70255
|
if (missing.length === 0 && (extras.length === 0 || !allowDestructive) && !applyingPermissions) {
|
|
69852
|
-
|
|
70256
|
+
report3.unchanged.push(id);
|
|
69853
70257
|
continue;
|
|
69854
70258
|
}
|
|
69855
70259
|
if (!allowDestructive && !Array.isArray(existing.fields)) {
|
|
69856
|
-
|
|
70260
|
+
report3.skipped.push({
|
|
69857
70261
|
collection: id,
|
|
69858
70262
|
reason: "the site returned this collection without a `fields` array, so an update would drop every field (and its data) by omission. Re-run to retry the read; pass --allow-destructive only if narrowing to the declaration is genuinely what you want."
|
|
69859
70263
|
});
|
|
69860
|
-
|
|
70264
|
+
report3.ok = false;
|
|
69861
70265
|
continue;
|
|
69862
70266
|
}
|
|
69863
70267
|
if (applyingPermissions) {
|
|
@@ -69894,14 +70298,14 @@ async function createCms(opts) {
|
|
|
69894
70298
|
}
|
|
69895
70299
|
}
|
|
69896
70300
|
}
|
|
69897
|
-
|
|
70301
|
+
report3.updated.push(id);
|
|
69898
70302
|
}
|
|
69899
|
-
return
|
|
70303
|
+
return report3;
|
|
69900
70304
|
}
|
|
69901
70305
|
async function seedCms(opts) {
|
|
69902
70306
|
const { app, cms, call, dryRun = false, force = false, only, dir } = opts;
|
|
69903
70307
|
const prefix = namespacePrefix(app);
|
|
69904
|
-
const
|
|
70308
|
+
const report3 = { ok: true, app, inserted: {}, sources: {}, skipped: [], refused: [], dryRun };
|
|
69905
70309
|
const fileRows = dir ? readSeedFile(dir) : {};
|
|
69906
70310
|
const rowsFor = (id, spec) => fileRows[id]?.length ? { rows: fileRows[id], source: "cms-seed.json" } : { rows: spec.seed ?? [], source: "config.ts" };
|
|
69907
70311
|
const remoteIds = /* @__PURE__ */ new Set();
|
|
@@ -69918,30 +70322,30 @@ async function seedCms(opts) {
|
|
|
69918
70322
|
const { rows, source } = rowsFor(id, spec);
|
|
69919
70323
|
considered++;
|
|
69920
70324
|
if (!rows.length) {
|
|
69921
|
-
|
|
70325
|
+
report3.skipped.push({
|
|
69922
70326
|
collection: id,
|
|
69923
70327
|
reason: `no seed rows \u2014 add a \`seed\` array to this collection in config.ts, or run \`himi cms seed --ai\` and refine ${dir ? "cms-seed.json" : "the generated cms-seed.json"}`
|
|
69924
70328
|
});
|
|
69925
70329
|
continue;
|
|
69926
70330
|
}
|
|
69927
70331
|
if (!spec.id?.startsWith(prefix)) {
|
|
69928
|
-
|
|
69929
|
-
|
|
70332
|
+
report3.refused.push({ collection: id, reason: `"${id}" is outside this app's namespace "${prefix}" \u2014 himi only seeds collections it owns.` });
|
|
70333
|
+
report3.ok = false;
|
|
69930
70334
|
continue;
|
|
69931
70335
|
}
|
|
69932
70336
|
if (!remoteIds.has(id)) {
|
|
69933
|
-
|
|
69934
|
-
|
|
70337
|
+
report3.skipped.push({ collection: id, reason: `not on the site yet \u2014 run \`himi cms create\` first` });
|
|
70338
|
+
report3.ok = false;
|
|
69935
70339
|
continue;
|
|
69936
70340
|
}
|
|
69937
70341
|
const count2 = await countItems(call, id);
|
|
69938
70342
|
if (count2 === null && !force) {
|
|
69939
|
-
|
|
69940
|
-
|
|
70343
|
+
report3.skipped.push({ collection: id, reason: "could not read the current row count, so seeding might bury existing content. Re-run, or pass --force if you are sure." });
|
|
70344
|
+
report3.ok = false;
|
|
69941
70345
|
continue;
|
|
69942
70346
|
}
|
|
69943
70347
|
if (count2 !== null && count2 > 0 && !force) {
|
|
69944
|
-
|
|
70348
|
+
report3.skipped.push({ collection: id, reason: `already has ${count2} items \u2014 seeding an already-populated collection needs --force so real content is never buried under sample rows` });
|
|
69945
70349
|
continue;
|
|
69946
70350
|
}
|
|
69947
70351
|
if (!dryRun) {
|
|
@@ -69953,7 +70357,7 @@ async function seedCms(opts) {
|
|
|
69953
70357
|
});
|
|
69954
70358
|
const droppedKeys = [...new Set(rows.flatMap((r) => Object.keys(r).filter((k) => !declaredKeys.has(k))))];
|
|
69955
70359
|
if (droppedKeys.length > 0) {
|
|
69956
|
-
|
|
70360
|
+
report3.skipped.push({
|
|
69957
70361
|
collection: id,
|
|
69958
70362
|
reason: `ignored undeclared key(s) in seed rows: ${droppedKeys.join(", ")} \u2014 only declared fields are written (a row cannot set system keys such as _id or _owner)`
|
|
69959
70363
|
});
|
|
@@ -69969,18 +70373,18 @@ async function seedCms(opts) {
|
|
|
69969
70373
|
)
|
|
69970
70374
|
);
|
|
69971
70375
|
}
|
|
69972
|
-
|
|
69973
|
-
|
|
70376
|
+
report3.inserted[id] = rows.length;
|
|
70377
|
+
report3.sources[id] = source;
|
|
69974
70378
|
}
|
|
69975
70379
|
if (considered === 0) {
|
|
69976
70380
|
const adopted = (cms?.collections ?? []).map((spec) => ({ spec, id: physicalId(spec, app) })).filter(({ spec, id }) => !!spec.wixId && (!only || id === only));
|
|
69977
|
-
|
|
70381
|
+
report3.skipped.push({
|
|
69978
70382
|
collection: only ?? "<none>",
|
|
69979
70383
|
reason: adopted.length ? `the selected collection${adopted.length === 1 ? " is" : "s are"} adopted and read-only \u2014 himi never seeds ${adopted.map(({ id }) => `"${id}"`).join(", ")}. Add rows in Wix CMS, or declare an owned collection with an id beginning "${prefix}".` : only ? `--only "${only}" matched no owned collection declared by this app` : "this app declares no owned CMS collections to seed"
|
|
69980
70384
|
});
|
|
69981
|
-
|
|
70385
|
+
report3.ok = false;
|
|
69982
70386
|
}
|
|
69983
|
-
return
|
|
70387
|
+
return report3;
|
|
69984
70388
|
}
|
|
69985
70389
|
function readSeedFile(dir) {
|
|
69986
70390
|
const file = join8(dir, "cms-seed.json");
|
|
@@ -70281,57 +70685,57 @@ function summarizeLint(app, bres, outcome, strict, extraIssues = []) {
|
|
|
70281
70685
|
}
|
|
70282
70686
|
};
|
|
70283
70687
|
const record2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
70284
|
-
const
|
|
70688
|
+
const text8 = (value) => typeof value === "string" && value ? value : void 0;
|
|
70285
70689
|
const message = (value) => {
|
|
70286
70690
|
const item = record2(value);
|
|
70287
|
-
if (
|
|
70288
|
-
if (
|
|
70691
|
+
if (text8(item.message)) return text8(item.message);
|
|
70692
|
+
if (text8(item.error)) return text8(item.error);
|
|
70289
70693
|
if (Array.isArray(item.errors)) {
|
|
70290
70694
|
const diagnostics = item.errors.map((entry) => {
|
|
70291
70695
|
const diagnostic = record2(entry);
|
|
70292
|
-
return
|
|
70696
|
+
return text8(diagnostic.text) ?? text8(diagnostic.message);
|
|
70293
70697
|
}).filter((entry) => !!entry);
|
|
70294
70698
|
if (diagnostics.length) return diagnostics.join("; ");
|
|
70295
70699
|
}
|
|
70296
70700
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
70297
70701
|
};
|
|
70298
|
-
const
|
|
70702
|
+
const report3 = foldLegacyReport(legacy, {
|
|
70299
70703
|
source: "validate",
|
|
70300
70704
|
evidence: { runId: `validate:${outcome.manifest.releaseId}`, ts: (/* @__PURE__ */ new Date()).toISOString() },
|
|
70301
70705
|
findings: [
|
|
70302
70706
|
...issues,
|
|
70303
70707
|
...bres.compileErrors.map((value) => ({
|
|
70304
70708
|
pass: "compile",
|
|
70305
|
-
path:
|
|
70709
|
+
path: text8(record2(value).bundle),
|
|
70306
70710
|
ruleCode: "compile-error",
|
|
70307
70711
|
severity: "error",
|
|
70308
70712
|
message: message(value)
|
|
70309
70713
|
})),
|
|
70310
70714
|
...outcome.failedToBuild.map((value) => ({
|
|
70311
70715
|
pass: "build",
|
|
70312
|
-
screen:
|
|
70716
|
+
screen: text8(record2(value).screenId),
|
|
70313
70717
|
ruleCode: "failed-to-build",
|
|
70314
70718
|
severity: "error",
|
|
70315
70719
|
message: message(value)
|
|
70316
70720
|
})),
|
|
70317
70721
|
...outcome.missingBundles.map((value) => ({
|
|
70318
70722
|
pass: "build",
|
|
70319
|
-
screen:
|
|
70320
|
-
path:
|
|
70723
|
+
screen: text8(record2(value).screen),
|
|
70724
|
+
path: text8(record2(value).bundle),
|
|
70321
70725
|
ruleCode: "missing-bundle",
|
|
70322
70726
|
severity: "error",
|
|
70323
70727
|
message: message(value)
|
|
70324
70728
|
})),
|
|
70325
70729
|
...workerThrows.map((value) => ({
|
|
70326
70730
|
pass: "worker",
|
|
70327
|
-
path:
|
|
70731
|
+
path: text8(record2(value).bundle),
|
|
70328
70732
|
ruleCode: "worker-threw",
|
|
70329
70733
|
severity: "error",
|
|
70330
70734
|
message: message(value.threw)
|
|
70331
70735
|
}))
|
|
70332
70736
|
]
|
|
70333
70737
|
});
|
|
70334
|
-
return { report:
|
|
70738
|
+
return { report: report3, hasErrors };
|
|
70335
70739
|
}
|
|
70336
70740
|
|
|
70337
70741
|
// src/cms-lint.ts
|
|
@@ -70455,8 +70859,8 @@ function surfaceIssues(dir, surfaceNav, strict) {
|
|
|
70455
70859
|
if (requested.length) {
|
|
70456
70860
|
const { rejected } = resolveSurfaceEnvelope(platforms, requested);
|
|
70457
70861
|
for (const r of rejected) {
|
|
70458
|
-
const
|
|
70459
|
-
|
|
70862
|
+
const report3 = surfaceById(r.id) ? warn : error;
|
|
70863
|
+
report3(`navigation.surfaces: ${r.reason} \u2014 the server will drop this key (narrow-only rule, RFC 0051 \xA73.6)`);
|
|
70460
70864
|
}
|
|
70461
70865
|
}
|
|
70462
70866
|
return issues;
|
|
@@ -70603,18 +71007,18 @@ function walkSource(dir, root = dir) {
|
|
|
70603
71007
|
const ext = entry.name.slice(entry.name.lastIndexOf("."));
|
|
70604
71008
|
if (!SOURCE_EXTENSIONS.has(ext)) continue;
|
|
70605
71009
|
const absolute = join25(dir, entry.name);
|
|
70606
|
-
const
|
|
70607
|
-
if (
|
|
71010
|
+
const text8 = readText2(absolute);
|
|
71011
|
+
if (text8 !== null) found.push({ path: relative4(root, absolute), text: text8 });
|
|
70608
71012
|
}
|
|
70609
71013
|
return found;
|
|
70610
71014
|
}
|
|
70611
|
-
function blankMatrixRows(
|
|
70612
|
-
const acceptance = mobileUxSection(mobileUxDocumentBody(
|
|
71015
|
+
function blankMatrixRows(text8) {
|
|
71016
|
+
const acceptance = mobileUxSection(mobileUxDocumentBody(text8), "Implementation acceptance") ?? "";
|
|
70613
71017
|
return /\|\s*(?:Ready|Empty|Add\s*\/\s*edit|Saved\s*\/\s*completed|Error\s*\/\s*recovery)\s*\|\s*\|\s*\|\s*\|\s*\|/i.test(acceptance);
|
|
70614
71018
|
}
|
|
70615
71019
|
function usesAppNetwork(source) {
|
|
70616
|
-
const code = (
|
|
70617
|
-
return source.some(({ text:
|
|
71020
|
+
const code = (text8) => text8.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
|
|
71021
|
+
return source.some(({ text: text8 }) => /\bwixApi\b|(?:from|import)\s*["']@(?:himalaya|wix\/himalaya)\/wix-[^"']+["']/.test(code(text8)));
|
|
70618
71022
|
}
|
|
70619
71023
|
function auditUx(content, strict = false) {
|
|
70620
71024
|
const dir = resolve16(content);
|
|
@@ -70782,7 +71186,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
70782
71186
|
"brief",
|
|
70783
71187
|
"no-agent",
|
|
70784
71188
|
"no-first-look",
|
|
70785
|
-
"no-image"
|
|
71189
|
+
"no-image",
|
|
71190
|
+
"no-watch"
|
|
70786
71191
|
]);
|
|
70787
71192
|
var SHELL_ENUM = SHELL_NAMES2.join("|");
|
|
70788
71193
|
var SWITCH_FLAGS = new Set([...BOOLEAN_FLAGS].filter((name) => name !== "deep"));
|
|
@@ -70879,7 +71284,7 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
70879
71284
|
create/store or inspect CAPTCHA keys (not login tokens).
|
|
70880
71285
|
Setup needs final --ios-bundle-id / --android-package-name.
|
|
70881
71286
|
|
|
70882
|
-
himi init <name> [--dir <parent>] [--template <t>] [--remote] [--push-setup | --no-push-setup] [--no-agent] [--no-first-look]
|
|
71287
|
+
himi init <name> [--dir <parent>] [--template <t>] [--remote] [--push-setup | --no-push-setup] [--no-agent] [--no-first-look] [--no-watch]
|
|
70883
71288
|
scaffold a starter content package (--template <t> starts
|
|
70884
71289
|
from a reference app; --remote fetches it off the serve plane),
|
|
70885
71290
|
with the agent files inside it (AGENTS.md, CLAUDE.md, the himalaya
|
|
@@ -70893,7 +71298,11 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
70893
71298
|
first; doNowAgain is the last key, for a reader of the tail) and
|
|
70894
71299
|
next[] with THIS session's steps: the consent preflight, then
|
|
70895
71300
|
the live preview (and the picture's steps when init could not
|
|
70896
|
-
make it)
|
|
71301
|
+
make it). For a bundled template it also STARTS the live
|
|
71302
|
+
preview itself (\`himi run --watch\`, detached, --no-open, log
|
|
71303
|
+
.himi/watch.log, stops after 30 idle minutes) and reports
|
|
71304
|
+
watch: {started, pid, url, board, log} \u2014 or {skipped} for a
|
|
71305
|
+
--remote template, --no-watch, or a CI environment
|
|
70897
71306
|
himi template list [--remote] list starter templates + metadata (title/description/tags) + each one's start prompt
|
|
70898
71307
|
himi template prompt <name> print the prompt that starts an app from that template
|
|
70899
71308
|
himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted]
|
|
@@ -70938,7 +71347,9 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
70938
71347
|
changed, which journey step, and the image that shows it (copied
|
|
70939
71348
|
into .himi/history/ so later renders cannot replace it). With no
|
|
70940
71349
|
--image the current home shot is attached (never a stale one;
|
|
70941
|
-
--no-image opts out; imageAttached says which
|
|
71350
|
+
--no-image opts out; imageAttached says which; on the
|
|
71351
|
+
--step review handover card it attaches even as a repeat,
|
|
71352
|
+
and the output names the making-of to hand over). Bare
|
|
70942
71353
|
prints the journal, the current step and the elapsed time.
|
|
70943
71354
|
himi ux audit [--content <dir>] [--strict] [--json]
|
|
70944
71355
|
inspect the mobile UX contract, template placeholders, app fixtures,
|
|
@@ -71159,14 +71570,16 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
71159
71570
|
himi mcp [plugin --out <dir>] the himi tools as a local MCP server over stdio, for coding
|
|
71160
71571
|
agents (Claude Code, Cursor, Codex, VS Code). \`himi init\`
|
|
71161
71572
|
configures each; see \`himi docs himi-mcp\`.
|
|
71162
|
-
himi run [<package.himi|dir|url>] [--browser] [--watch] [--lan] [--port <n>] [--chrome preview|app] [--screen <id>] [--surface <id>] [--no-open]
|
|
71573
|
+
himi run [<package.himi|dir|url>] [--browser] [--watch [--idle-exit <min>]] [--lan] [--port <n>] [--chrome preview|app] [--screen <id>] [--surface <id>] [--no-open]
|
|
71163
71574
|
serve a package on localhost + open it in a browser. No serve
|
|
71164
71575
|
plane, no dev-server. With no argument it packs the current
|
|
71165
71576
|
content package first \u2014 edit, run, look at it.
|
|
71166
71577
|
--watch rebuilds on every save and repaints the open tab
|
|
71167
71578
|
(a failed build keeps the last good preview). The build board
|
|
71168
71579
|
is served beside it at /__himi/studio/ with the preview framed
|
|
71169
|
-
in it. Blocks until Ctrl-C
|
|
71580
|
+
in it. Blocks until Ctrl-C (or, with --idle-exit <min>, until
|
|
71581
|
+
that long passes with no source save \u2014 the watch \`himi init\`
|
|
71582
|
+
starts passes 30).
|
|
71170
71583
|
--lan also serves it to the local network and prints a QR:
|
|
71171
71584
|
a phone on the same Wi-Fi opens it as the real app, full
|
|
71172
71585
|
screen. Read-only, but anyone on that network can open it.
|
|
@@ -71319,6 +71732,13 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
71319
71732
|
Requires Himi owner login plus Wix CLI login for site authorization.
|
|
71320
71733
|
himi notify status --content <dir> [--site <msid>] [--refresh] [--json]
|
|
71321
71734
|
Reads server setup status. --refresh checks Branded/Ping live; it does not send push.
|
|
71735
|
+
himi notify send --provider ping --target wix --app <a> --site <msid> --member-email <email>
|
|
71736
|
+
--title <s> [--body <s>] --link <scheme>://screen/<id>[?\u2026] [--request-id <uuid>] [--dry-run] [--json]
|
|
71737
|
+
Hosted test notification to ONE site member's devices: Himi Serve sends it through Ping
|
|
71738
|
+
as Himalaya, so no Apple/Firebase keys or Fire Console. Needs himi login --browser and
|
|
71739
|
+
wix login as the site owner, and a ready notify setup. Acceptance is not delivery.
|
|
71740
|
+
--dry-run checks ownership, member, link and readiness without sending. After an unknown
|
|
71741
|
+
outcome, re-run the same command with the printed --request-id; Ping never runs it twice.
|
|
71322
71742
|
himi notify send --app <a> (--token <t> | --device <id> | --all) --title <s> [--body <s>] [--link <url>]
|
|
71323
71743
|
deliver a REAL push notification to registered devices \u2014
|
|
71324
71744
|
APNs (.p8) for iOS, FCM v1 for Android. --all fans out to
|
|
@@ -71493,7 +71913,7 @@ WHAT A PATCH DOES NOT CARRY \u2014 all reported in the output, never silent
|
|
|
71493
71913
|
himi docs releasing-with-himi-serve`;
|
|
71494
71914
|
var COMMANDS = [
|
|
71495
71915
|
{ name: "captcha", summary: "create and store native CAPTCHA keys in Serve, or inspect setup status; customer setup verifies Wix site ownership; does not enable member CAPTCHA SDKs", required: "setup|status --app <app> [--content <dir>] [--site <msid>] [--ios-bundle-id <id>] [--android-package-name <id>] [--target <t>]" },
|
|
71496
|
-
{ name: "init", summary: "scaffold a starter content package (OTA-only; no native shells). --template <t> starts from a full reference app (branded-lite = consumer storefront over a Wix site; owner-lite = owner CRUD/management) instead of the minimal counter; --templates lists bundled ones; --remote fetches the template off the serve plane (falls back to the bundled copy offline). The new app also gets its agent files (the same set `himi agent-init` writes: AGENTS.md, CLAUDE.md, the himalaya skill, HIMALAYA.md, a SessionStart hook) so the next session started inside it picks the work up; --no-agent skips them. It also makes the build board (.himi/studio/, exactly as `himi studio --no-open` does \u2014 it never opens a browser), so every later render, test, validate, push and progress entry refreshes it; and, for a template bundled with this CLI, it makes the FIRST PICTURE itself (never for a --remote one: that is its publisher's code, and a render runs it on this machine, so next[] leaves the render to the agent after it has read the code): exactly `himi render <homeScreenId> --png --device iphone` (offline \u2014 no request; never --live), run as a child it kills after a time budget, writing .himi/shots/<home>.png and journaling it (by: himi) as the STARTING POINT (\"Starting point: the <template> template, before any of your changes\") so the board and the journal hold a picture when init returns. init also records the source it wrote (.himi/init-source.json: a content fingerprint, Markdown left out), and the first home PNG render after the source diverges from it is journaled once as \"First look at your app\" \u2014 reported as `firstLook: {image}` or `{skipped: <reason>}`, never a failed init; `--no-first-look` skips it (for scripts and CI that render themselves). The envelope LEADS with `doNow`: three lines to run now (the consent preflight, `himi run --watch --content <dir> &` with the board's path
|
|
71916
|
+
{ name: "init", summary: "scaffold a starter content package (OTA-only; no native shells). --template <t> starts from a full reference app (branded-lite = consumer storefront over a Wix site; owner-lite = owner CRUD/management) instead of the minimal counter; --templates lists bundled ones; --remote fetches the template off the serve plane (falls back to the bundled copy offline). The new app also gets its agent files (the same set `himi agent-init` writes: AGENTS.md, CLAUDE.md, the himalaya skill, HIMALAYA.md, a SessionStart hook) so the next session started inside it picks the work up; --no-agent skips them. It also makes the build board (.himi/studio/, exactly as `himi studio --no-open` does \u2014 it never opens a browser), so every later render, test, validate, push and progress entry refreshes it; and, for a template bundled with this CLI, it makes the FIRST PICTURE itself (never for a --remote one: that is its publisher's code, and a render runs it on this machine, so next[] leaves the render to the agent after it has read the code): exactly `himi render <homeScreenId> --png --device iphone` (offline \u2014 no request; never --live), run as a child it kills after a time budget, writing .himi/shots/<home>.png and journaling it (by: himi) as the STARTING POINT (\"Starting point: the <template> template, before any of your changes\") so the board and the journal hold a picture when init returns. init also records the source it wrote (.himi/init-source.json: a content fingerprint, Markdown left out), and the first home PNG render after the source diverges from it is journaled once as \"First look at your app\" \u2014 reported as `firstLook: {image}` or `{skipped: <reason>}`, never a failed init; `--no-first-look` skips it (for scripts and CI that render themselves). For a bundled template (never a --remote one \u2014 the preview would build and serve its publisher's code) init also STARTS the live preview itself: `himi run --watch --content <dir> --port <p> --no-open --idle-exit 30`, detached in its own process group with its output in .himi/watch.log, never waited for (the url is chosen before it builds), recorded in .himi/watch.json, and reported as `watch: {started, pid, url, board, log}` \u2014 or `{skipped: <reason>}` for `--no-watch`, a `CI` environment, a test runner, a watch already running, or a start that failed (never a failed init). The envelope LEADS with `doNow`: three lines to run now (the consent preflight, the live preview \u2014 its url and board when init started it, else `himi run --watch --content <dir> &` with the board's path \u2014 and the postcard line), and ENDS with the same lines as `doNowAgain`, so an agent that reads only the head or only the tail (`| tail`) gets them; at a terminal they are also printed as plain text on stderr, before the JSON and again after it. Its `next[]` LEADS with the steps for THIS session, in this order for every app \u2014 the offline first render and that picture's `himi progress` postcard (only when init could not make the picture), the skill's consent preflight (authorize or mock any Wix or external startup request; with none, go straight on), and only then the live preview (the running one's url, or `himi run --watch &` when init did not start it) \u2014 ahead of the next-session guidance", required: "<name> [--dir] [--template <t>] [--remote] [--no-agent] [--no-first-look] [--no-watch] | --templates" },
|
|
71497
71917
|
{ name: "template", summary: "starter-template catalog + publishing. Every template is a SHOWCASE APP \u2014 a real published release you can open running before you start from it. `list` prints available templates with metadata (title/description/tags) plus each one's start prompt \u2014 pass --remote to include the serve-plane catalog so an agent can pick one; `prompt <name>` prints just that sentence for one template (the skill URL is the public door, or the plane named by --target/--serve-url); `publish` publishes an app AS a `himi init --template` (needs a himalaya.template.json; builds a previewable release + attaches the scaffold source; --source-dir overrides which tree becomes the source, --name sets the template name; bakes a launcher icon \u2014 --icon <png>, or the committed template-icons/<name>.png \u2014 and extracts accent/palette gallery meta from the scaffold's tokens.json; --visibility unlisted keeps it out of the /v1/templates catalog \u2014 templates default to public); `trim --keep <ids>` cuts an app scaffolded from a template down to those screens (the home, and any screen the navigation pins such as a drawer body, always stays) \u2014 it deletes the other SCREENS/TEMPLATED entries whole, prunes navigation (linkableScreens, tabBar, drawer, deep links, surfaces) and drops tier5-src workers no kept screen uses, then runs validate --strict and rolls everything back if it fails (--force keeps it, --dry-run writes nothing). A kept screen that links to a removed one is refused unless --links follow (keep those too) or --links drop (remove the taps)", required: "list [--remote] | prompt <name> | publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--target <t>] | trim --keep <id,id,\u2026> [--content <dir>] [--links follow|drop] [--dry-run] [--force]" },
|
|
71498
71918
|
{ name: "build", summary: "compile tier5-src/<name>/index.ts -> dist/bundles/<name>.bundle.js with esbuild (--binary = resolve published @himalaya/*); semantic TypeScript checking belongs to himi validate", required: "[--content <dir>] [--binary]" },
|
|
71499
71919
|
{ name: "functions", summary: "inspect/invoke server functions and operate durable jobs or schedules", required: "list|describe|openapi|invoke | jobs list|get|cancel|retry|dlq | schedule describe|invoke [--app <a>] [--environment preview|production] [--release <id>]" },
|
|
@@ -71501,7 +71921,7 @@ var COMMANDS = [
|
|
|
71501
71921
|
{ name: "validate", summary: "lint the content package (no push): fixed-contract TypeScript checking for tier5-src workers + schema/proto + token refs + component allowlist + $service bindings + strict-enum foot-guns + capability decls + worker boot dry-run + build/compile failures. Type issues are advisory warnings normally; --strict promotes them and hygiene warnings to errors. Exit 1 on any error. Off-repo, the CLI supplies its compiler and SDK declarations.", required: "[--content <dir>] [--strict]" },
|
|
71502
71922
|
{ name: "test", summary: "DERIVED behavioral crawl \u2014 no test files to write. Boots each screen's real worker, replays its onAppear, then dispatches every action site the descriptor already declares (each from a fresh boot) and checks a narrow set of unambiguous invariants: no uncaught throw, never stuck on `loading`, no `$service:` token or NaN/undefined/[object Object] reaching visible text, no binding aimed at state the worker never defines, no nav param silently dropped, no dead nav target. Catches what validate/render cannot \u2014 a WRONG REDUCER passes both with ok:true. Runs the worker in a DEVICE-PARITY sandbox (no setTimeout/setInterval, as on JSC/WebView) so a bundle that would die on device fails here. Crawls on HERMETIC DATA by default \u2014 zero network: the app's own dev/net-mocks.json first (--net <file> overrides), then the config's CMS `seed:` rows, then a canned fixture of frozen real vertical responses shipped with the CLI (exercises code paths, not API-contract truth) \u2014 so the success/mapping path runs, not just the failure UI. Two boot-only HOSTILE passes (every list empty; every request 500) then check the failure/empty day \u2014 corpus-proven, so their findings are errors. NAV EDGES are followed too: every himi.open site ($item params resolved against row 0 of its list, $service against worker state) and worker _nav directive is harvested, and each baked target is booted WITH the params the source sends \u2014 detail screens finally run their content arm; edge findings are errors tagged edge:<source>. --native runs both real native decoders over the exact baked JSON bytes; --native ios or --native android selects one. It is explicit, runs only from a Himalaya SOURCE CHECKOUT (the decoder oracles are that tree's own HimalayaCore Swift package and Gradle harness, which a published install does not ship), and needs the selected local toolchain. From a source checkout, default runs name missing native verdicts in the coverage ledger; elsewhere the ledger stays silent about them rather than offering a remedy you cannot run. --offline strips all data layers (the old reject-all axis); --no-hostile skips the hostile passes. --record (EXPLICIT, never automatic) refreshes dev/net-mocks.json from live reads first: it boots each screen (load + root onAppear ONLY \u2014 nothing the app's own launch would not fire; the tap/action crawl never sees the live transport, and only proven reads \u2014 GET, or read-shaped POST (/query, /search, /list*, \u2026) \u2014 ever reach the wire), freezes the round-trips, then crawls hermetically on the fresh fixture; record against a QA site, not production. Nodes it cannot judge headless are named, never silently passed. Exit 1 on any error. The CLI carries the authoring runtime off-repo; no per-app SDK install is required. --browser runs the whole loop (build \u2192 extract \u2192 validate \u2192 render \u2192 test) in YOUR browser instead of here, so the source never reaches a pod: it opens one screen's worker + descriptor in a tab, blocks on a loopback callback, and prints the report it posts back. One screen per tab \u2014 name it. --no-open prints the URL rather than launching a browser (SSH); --timeout <ms> bounds the wait, which matters because the page has no execution deadline of its own and an infinite loop in the worker hangs the tab.", required: "[<screenId>\u2026] [--content <dir>] [--net <file>] [--budget <ms>] [--deep [n]] [--record] [--native [ios|android]] [--no-shots] [--offline] [--no-hostile] [--all-screens] [--browser] [--no-open] [--timeout <ms>] [--json]" },
|
|
71503
71923
|
{ name: "ux", summary: "audit or record mobile UX evidence. `audit` checks the public-safe contract, seeded blank acceptance rows, placeholder source copy, app-specific mocks, and iOS/Android audit records. It warns while existing templates migrate; `--strict` promotes missing evidence. `record` stores checks performed on an installed iOS/Android build: `--checks` is required for passed|failed and empty for unavailable, which drove nothing and needs a `--reason` instead. `record` also STAMPS the release it audited (it resolves the id from a build, the same one a push would publish), because an unforced `himi push` of a release with screens refuses when no drive is recorded for THAT release, or when the one that is recorded says `failed` \u2014 the artifact stopped being advice nobody reads (#2955). `unavailable` still satisfies it; only `failed` refuses.", required: "audit [--content <dir>] [--strict] [--json] | record --content <dir> --platform ios|android --status passed|failed|unavailable [--checks back,keyboard,...] [--reason <text>]" },
|
|
71504
|
-
{ name: "progress", summary: 'the build JOURNAL an agent keeps for the owner while it works. `himi progress "<what changed>" --step <1-8|name> --image <png>` appends one postcard to .himi/progress.jsonl \u2014 the step is one of the customer journey\'s eight (discover, classify, plan, implement, brand, validate, review, download) \u2014 and COPIES each image, content-addressed, into .himi/history/, because .himi/shots/ is overwritten by every render and cleared by every full `himi test`, so a path into it would later show a different picture than the one the owner was sent. --image repeats. With NO --image, the home screen\'s current shot is attached automatically, only when it is no older than the last source change and not the picture the latest journaled postcard already shows; `imageAttached` in the output says whether the postcard carries a picture (with `imageNote` saying why not), and `--no-image` sends a text-only postcard. --notify also raises a best-effort desktop notification for an owner who stepped away. A bare `himi progress` prints the journal with the current step and elapsed time \u2014 also how an agent recovers its place after its context was compacted. APPROVALS: `--approve <gate> "<what>"` records one the customer just gave (gates: site-create, connect, private-data, draft, promote, push-signing, qa-build) in .himi/approvals.jsonl, and `--done <gate>` / `--drop <gate>` closes it \u2014 an approval is owed until then, and the chat that held it does not survive a new session. `--brief` prints plain text for a session start: the current step, the last postcard, every open approval, and whether this app\'s agent files are stale \u2014 the SessionStart hook `himi agent-init` installs runs exactly this. The CLI also journals milestones by itself \u2014 the first PNG render of the home screen (for an app a current `himi init` started: once as the template\'s "Starting point" while the source is what init wrote, then once as "First look at your app" after it diverges, Markdown edits not counting; once per app otherwise), a `himi test` crawl whose outcome changed since the last one (its counts or its home picture; an identical re-run writes none, and a repeat names what changed: "The home screen changed since the last crawl." or the changed counts, was \u2192 now), every draft push (with the home picture only when that shot is newer than the last source edit \u2014 otherwise the postcard says why and names `himi render <home> --png`) \
|
|
71924
|
+
{ name: "progress", summary: 'the build JOURNAL an agent keeps for the owner while it works. `himi progress "<what changed>" --step <1-8|name> --image <png>` appends one postcard to .himi/progress.jsonl \u2014 the step is one of the customer journey\'s eight (discover, classify, plan, implement, brand, validate, review, download) \u2014 and COPIES each image, content-addressed, into .himi/history/, because .himi/shots/ is overwritten by every render and cleared by every full `himi test`, so a path into it would later show a different picture than the one the owner was sent. --image repeats. With NO --image, the home screen\'s current shot is attached automatically, only when it is no older than the last source change and not the picture the latest journaled postcard already shows \u2014 except on the HANDOVER card (`--step review`), where a repeat is the point and it attaches anyway; that card\'s output also names the making-of (`makingOf`, .himi/making-of.html, rebuilt when missing or older than the journal so it ends on this card) and its `next` says to hand it to the owner beside the preview link; `imageAttached` in the output says whether the postcard carries a picture (with `imageNote` saying why not), and `--no-image` sends a text-only postcard. --notify also raises a best-effort desktop notification for an owner who stepped away. A bare `himi progress` prints the journal with the current step and elapsed time \u2014 also how an agent recovers its place after its context was compacted. APPROVALS: `--approve <gate> "<what>"` records one the customer just gave (gates: site-create, connect, private-data, draft, promote, push-signing, qa-build) in .himi/approvals.jsonl, and `--done <gate>` / `--drop <gate>` closes it \u2014 an approval is owed until then, and the chat that held it does not survive a new session. `--brief` prints plain text for a session start: the current step, the last postcard, every open approval, and whether this app\'s agent files are stale \u2014 the SessionStart hook `himi agent-init` installs runs exactly this. The CLI also journals milestones by itself \u2014 the first PNG render of the home screen (for an app a current `himi init` started: once as the template\'s "Starting point" while the source is what init wrote, then once as "First look at your app" after it diverges, Markdown edits not counting; once per app otherwise), a `himi test` crawl whose outcome changed since the last one (its counts or its home picture; an identical re-run writes none, and a repeat names what changed: "The home screen changed since the last crawl." or the changed counts, was \u2192 now), every draft push (with the home picture only when that shot is newer than the last source edit \u2014 otherwise the postcard says why and names `himi render <home> --png`), and ONE rule for a long silence: a successful home `render --png` more than ten minutes after the newest postcard of any kind, with a picture the latest postcard does not show, journals "Still building: here\'s the home screen now." with it (never twice within the window). When a crawl\'s or a draft\'s home picture is the first of an app whose source diverged, "First look at your app" is journaled first, from that shot, once. All are marked `"by": "himi"`; they never move the journey step, carry no link or credential, and step aside (folded out of every reader) when the agent journals the same step or picture within five minutes. Local only: nothing is sent anywhere.', required: '["<message>" [--step <1-8|name>] [--image <png>]\u2026 [--no-image] [--notify]] [--content <dir>] | --approve|--done|--drop <gate> ["<note>"] | --brief' },
|
|
71505
71925
|
{ name: "studio", summary: "open the BUILD BOARD \u2014 one local page the owner can leave open while an agent builds their app, which fills itself in: every screen as it renders (NEW/CHANGED badges, and each earlier version kept for a filmstrip), the journey stepper and the latest postcard from `himi progress`, the validate/test/UX/draft checks as they run, the site next to the app's home screen with its palette, and the navigation the test crawl followed. Writes .himi/studio/index.html and opens it; the page re-reads its state every two seconds, so nothing needs to be served. OPT-IN BY EXISTENCE: until this command (or `himi init`, which makes the board for every app it creates, without opening a browser) has run, no other command touches .himi/studio/ \u2014 after it, render/test/validate/push/icon/site/ux/progress each refresh the board as they finish (best-effort; never fails a command). Every image is a content-addressed copy in .himi/history/, never a live path into .himi/shots/. Local only, no secrets: it never reads a preview token, handover code or control token. --no-open prints the path instead (SSH, CI). `himi studio reel` writes the MAKING-OF: one self-contained HTML file (every image inlined, nothing fetched) with a flipbook of the home screen version by version, captioned by the postcard in effect at the time, the final screens, and every postcard \u2014 sized to stay attachable, thinning frames first. Defaults to .himi/making-of.html (never archived by push), which every successful `himi push --draft` also rebuilds and names as `reel` in its output; --out to put it elsewhere. The output names that file as the one to hand the owner (`handover: {file, hint}` in JSON; two plain lines at a terminal) \u2014 beside the preview link, never instead of it.", required: "[--content <dir>] [--no-open] | reel [--out <file>] [--no-open]" },
|
|
71506
71926
|
{ name: "render", summary: "headless-render a screen with the web renderer: render-tree + diagnostics, optional --png [<file>] (bare: .himi/shots/<id>.png, what the build board shows), --scheme dark, --tree. Local by default (content dir). Pass --remote --app --release to hit GET /v1/preview/\u2026/render on the serve plane after a push. Boots the worker so $service bindings resolve; --static raw bindings; --live real Wix fetches; --state injects worker state; --input supplies pre-boot navigation params; --width/--device for PNG viewport; --rtl for dir=rtl + $env:isRTL.", required: "<screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--static] [--live] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--remote --app --release]" },
|
|
71507
71927
|
{ name: "cms", summary: "inspect, provision, and seed the app's CMS (Wix Data) collections, declared as a `cms` block in config.ts. `query` is READ-ONLY \u2014 by default it fetches ONLY the physical ids `config.ts` declares (item counts + schema drift) and reports `siteScanned: false`; it never lists the site, so an UNDECLARED `himi_<app>_` collection this app owns is not reached without `--all`. `--all` SCANS the site \u2014 every collection it lists is labeled `owned`/`adopted`/`foreign`, and it describes every foreign one \u2014 id, non-system field keys + types, and the site's read role \u2014 so an app with no `cms` block at all can DISCOVER a collection to adopt (`--all` is the one case where a missing `cms` block is not 'nothing to do'). Foreign collections are described up to a cap and are never item-counted (`itemCount: null`), because a row count is one query each; `foreign.count` is how many the scan LISTED \u2014 it follows the list endpoint's pages, and `listingIncomplete: true` says when the walk stopped short of the end, with `unlistedCollections` counting what it left out when Wix gave a paging total \u2014 and `foreign.truncated` says how many of those went undescribed, each still NAMED in `foreign.others` (id + displayName), so a collection the site already has is never hidden behind the cap. The scan is OWNER-scoped \u2014 it needs --site <msid> (or WIX_SITE_ID) plus owner auth via `wix token`; a visitor token can read a public collection's ITEMS but can never enumerate collections, so there is no tokenless discovery path. `create` reconciles declared collections additively and idempotently, and REFUSES any id outside the app's `himi_<app>_` namespace, so it is safe against a production site. It reports declared-vs-site `permissionDrift` but never writes permissions unless asked with --apply-permissions, so a field reconcile can't silently revert access control changed in the dashboard. `seed` inserts the git-tracked `seed:` rows into EMPTY owned collections (--force to add to a non-empty one; --ai writes placeholder rows plus a refinement prompt for you to review). Adopted collections (declared by `wixId`) are read-only and never created, updated, or seeded. Site from --site <msid> or WIX_SITE_ID; owner auth via `wix token`.", required: "query|create|seed [--site <msid>] [--content <dir>] [--all] [--dry-run] [--force] [--allow-destructive] [--apply-permissions] [--only <id>] [--ai] [--count <n>]" },
|
|
@@ -71510,7 +71930,7 @@ var COMMANDS = [
|
|
|
71510
71930
|
{ name: "icon", summary: "the app's LAUNCHER icon \u2014 the home-screen / store mark, which is BINARY-floor and never over-the-air (distinct from `himi icons`, the in-screen glyph vocabulary). `status`/`check` report what this app would actually ship and validate it against both stores: exact 1024x1024, no tRNS chunk, non-interlaced, and \u2014 when the optional image toolchain is installed \u2014 no transparent regions (Apple permits an alpha channel but not transparent pixels: ITMS-90717), no pre-masked corners, no letterboxed wordmark; they also print the store-policy checklist no checker can decide. `set <file.png>` installs supplied art, `from-site` installs the logo `himi site analyze` downloaded, and `generate` makes a gradient tile + glyph \u2014 all three trim, flatten onto the app's accent, write the 1024 opaque master plus the 512 Play listing icon (Play wants 32-bit WITH alpha, so it is a separate file), and DECLARE it in himalaya.content.json, without which the release bakes no icon at all", required: "[status|check] | set <file.png> | from-site [--book <dir>] | generate [--glyph <g>] [--accent <hex>] | glyphs [--content <dir>] [--json]" },
|
|
71511
71931
|
{ name: "login", summary: "obtain a scoped serve-control token from Wix identity and store it in ~/.himalaya/serve-targets.json (so push/promote need no hand-pasted token). Three modes: --browser (your own Wix account, no VPN/BO), --wix (HEADLESS \u2014 the machine's own Wix CLI credential; the mode a CI pod or agent container can satisfy), --dpx (Wix employee, reads ~/.dpx/session/token). RFC 0026 WS3.", required: "[--browser|--wix|--dpx] [--target <t>] [--issue-url <url>]" },
|
|
71512
71932
|
{ name: "logout", summary: "remove the stored control token for a target", required: "[--target <t>]" },
|
|
71513
|
-
{ name: "notify", summary: "`setup` optionally configures Branded/Ping from local customer key files; `status --refresh` reads live backend readiness. Setup may be deferred while building. These use serve-control and require owner plus Wix account authentication. `send`, `tokens`, `doctor`: send a REAL push notification to registered devices (RFC 0036 WS5) \u2014 APNs .p8 over HTTP/2 for iOS, FCM v1 for Android. `send` targets one device (--token / --device <registry id>) or fans out to the whole app (--all), reporting a per-device result; a token the provider says is uninstalled (APNs 410 Unregistered / FCM UNREGISTERED) is auto-disabled, while a merely wrong one (BadDeviceToken \u2014 usually a sandbox/production mismatch) is recorded and left enabled. `tokens` lists the registry. `doctor` reports which credentials resolve and which sender each platform picks. Talks to the admin-server via --admin-url/HIMALAYA_ADMIN_URL, authenticated with --admin-session/HIMALAYA_ADMIN_SESSION. NOT `himi push`, which publishes a release.", required: "setup|status [--content <dir>] [--target <t>] [--refresh] [--yes] [--no-push-setup] | send|tokens|doctor [--app <a>] [--token <t> | --device <id> | --all] [--title <s>] [--body <s>] [--link <url>] [--platform ios|android] [--badge <n>] [--channel <id>] [--silent] [--dry-run] [--json]" },
|
|
71933
|
+
{ name: "notify", summary: "`setup` optionally configures Branded/Ping from local customer key files; `status --refresh` reads live backend readiness. Setup may be deferred while building. These use serve-control and require owner plus Wix account authentication. `send`, `tokens`, `doctor`: send a REAL push notification to registered devices (RFC 0036 WS5) \u2014 APNs .p8 over HTTP/2 for iOS, FCM v1 for Android. `send` targets one device (--token / --device <registry id>) or fans out to the whole app (--all), reporting a per-device result; a token the provider says is uninstalled (APNs 410 Unregistered / FCM UNREGISTERED) is auto-disabled, while a merely wrong one (BadDeviceToken \u2014 usually a sandbox/production mismatch) is recorded and left enabled. `tokens` lists the registry. `doctor` reports which credentials resolve and which sender each platform picks. Talks to the admin-server via --admin-url/HIMALAYA_ADMIN_URL, authenticated with --admin-session/HIMALAYA_ADMIN_SESSION. `send --provider ping` is the hosted alternative: the serve plane sends one test notification to one site member through Ping as Himalaya, with owner login instead of provider keys, and reports acceptance, never delivery. NOT `himi push`, which publishes a release.", required: "setup|status [--content <dir>] [--target <t>] [--refresh] [--yes] [--no-push-setup] | send --provider ping [--content <dir>] [--app <a>] [--site <msid>] --member-email <email> --title <s> [--body <s>] --link <url> [--request-id <uuid>] [--dry-run] [--json] | send|tokens|doctor [--app <a>] [--token <t> | --device <id> | --all] [--title <s>] [--body <s>] [--link <url>] [--platform ios|android] [--badge <n>] [--channel <id>] [--silent] [--dry-run] [--json]" },
|
|
71514
71934
|
{ name: "site", summary: "PROVISION a Wix project, then analyze or extend it. `create` is the one door that makes a site instead of adopting one: POST /funnel/projects/v1/create (a WIX site, a HEADLESS backend, a BRANDED_APP, or a VIBE site), and for headless it also mints the OAuth app whose id IS the `WIX_CLIENT_ID` \u2014 with --redirect-uri already registered, because an unregistered redirect makes native signInDirect fail at redirect-session. `--verticals` REQUIRES `--template` and reports only what Wix confirmed: measured, installing onto a project created from the BLANK template returns 200 while the vertical's API answers 428 forever, because there is no site for it to attach to (the install asks for CREATE_SITE). A real template ships its verticals installed and populated, and installing further ones onto it then works. It MUTATES your Wix account and needs --yes; without it you get the payload and nothing is sent. `list` is the read-only site inventory (with each site's premium flag, which is what explains an installed-but-428 vertical). Auth is the account-scoped `wix token` (no --site), falling back to WIX_API_KEY + WIX_ACCOUNT_ID. `client` is the same OAuth step for a site that ALREADY exists \u2014 the one himi adopts rather than makes \u2014 so a clientId no longer means a manual trip to the dashboard: `client ensure` reconciles rather than mints, because a site can already carry a client with NO registered redirect and shipping that one fails signInDirect at runtime; it reuses a client that carries --redirect-uri, PATCHes one that matches --name but lacks it (unioning, so another app's redirect survives), and creates only when there is nothing to reuse (--new forces a fresh one). --name IS the idempotency key. `client` auth is SITE-scoped (`wix token --site <msid>`) because minting is owner-of-that-site, and --yes gates every mutation: a site that already has a usable client resolves WITHOUT it, but --write-provisioning needs --yes even then because it rewrites a manifest on disk (the dry run's planned.provisioning names the file). That query endpoint IGNORES paging (measured: query.paging, cursorPaging and a top-level paging all return the full set at offset 0), so when pagingMetadata reports more clients than came back, `ensure` REFUSES EVERY outcome \u2014 create, patch AND reuse. Reuse is refused too because pickClient breaks ties on the OLDEST createdDate, so an omitted older same-named MOBILE client with the same redirect would win a later complete read and the resolved clientId would MOVE between runs \u2014 and it gets written into provisioning.json; convergence is what makes a stray duplicate untidy rather than harmful. --name cannot narrow the query (no filter is sent; the name is applied to the rows after they arrive), so the recovery is the dashboard; --new skips the query entirely. A `patched` result is verified against what Wix STORED \u2014 the redirect list is read off the PATCH response, and re-read via GET when the response omits it, because falling back to the list himi just sent would make the guard pass vacuously on exactly the silent no-op it exists to catch. `client list` is the read-only inventory of a site's clients: act on its `shippable` column rather than `hasRedirect`, because shippable ALSO requires a MOBILE applicationType and `ensure` refuses a WEB client \u2014 a WEB one carrying your redirect looks right and is not a candidate \u2014 and it sets truncated:true rather than presenting a partial inventory as the whole one. `analyze --read-only` returns API-only generation context as JSON (identity, access probes, CMS permissions and selected redacted Velo source); it needs no URL/browser and never uploads or applies anything. `analyze` reads a site into a business-book folder (REST + Playwright, GET-only); `--url` alone is the visitor-only pass over the published pages, and `--client-id` adds the vertical probes and the app binding `--apply` requires; `site function` list/get/put/publish/invokes that site's Velo HTTP functions and is not `himi functions` (serve-plane jobs). `--apply` bakes into a brandable shell; `--shell` takes the same names `himi dist --shell` does and defaults to branded-lite, and a shell that ships no prepare:customer is refused by name rather than half-built. `--shell brandy` (the site shell, RFC 0062) also derives the site's page map from its published pages and header menu \u2014 Home first, up to four tabs for the pages a visitor returns to, the rest in the drawer \u2014 into the overlay's `site` block, so it can be edited before `--apply`; only a site shell ever receives that block. It never overwrites any shell's committed apps/<shell>/provisioning.json.", required: "create <name> [--type wix|headless|branded-app|vibe] [--template <guid>] [--verticals stores,bookings,\u2026] [--folder <guid>] [--redirect-uri <uri>] [--no-oauth-app] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | list | client ensure|list --site <msid> [--name <name>] [--redirect-uri <uri>] [--new] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | analyze --read-only [--site <msid>] [--client-id <id>] [--json] | analyze --url <https> [--client-id <id>] [--site <msid>] [--out <dir>] [--redirect-uri <uri>] [--apply] [--shell " + SHELL_ENUM + "] | function list|get|put|publish|invoke [--site <msid>] [--url <https>] [--method GET|POST|PUT|DELETE] [--source <file>] [--body <json>] [--publish] [--invoke]" },
|
|
71515
71935
|
{ name: "doctor", summary: 'off-VPN preflight in one shot: probe the PUBLIC data plane (www.wixapis.com \u2014 reachable off-VPN), the INTERNAL serve/control plane for the selected target (VPN-gated), and report auth state (dpx session on disk + stored control token). --dpx additionally runs a live, EADDRINUSE-safe `dpx whoami`. Machine-readable with --json (an unreachable internal plane carries a stable code:"needs_vpn" so an agent branches on the code, not the prose); human-readable otherwise. Never loads the authoring runtime \u2014 works off-repo.', required: "[--target <t>] [--dpx] [--json] [--data-url <url>] [--timeout <ms>]" },
|
|
71516
71936
|
{ name: "upgrade", summary: "install the newest published CLI (`npm i -g @wix/himalaya-cli@latest`) and report what moved. This is the explicit door: himi also updates itself when the running build is more than a week old \u2014 unattended it just does it and re-runs your command, at a terminal it asks first \u2014 and `HIMI_NO_UPDATE_CHECK=1` turns that off, leaving this command as the way to catch up. Reports `unchanged` rather than claiming success when npm exits 0 without moving the version, which is what a registry mirror serving its own pinned copy looks like.", required: "[--json]" },
|
|
@@ -71519,7 +71939,7 @@ var COMMANDS = [
|
|
|
71519
71939
|
{ name: "import", summary: "turn a `himi pack --dev` package back into an EDITABLE content package \u2014 the file counterpart of `himi pull`, needing no serve plane, no account and no network. Refuses an invalid package before trusting its source (the content hash is recomputed over every entry on open, so the archive is provably the one that was packed), and refuses a RELEASE package by naming the absence \u2014 it carries built output and no source \u2014 rather than reporting a missing file. `--name` re-substitutes the app identity through the same machinery `himi init --template` and `himi pull --name` use, so the restored tree is a NEW app rather than a second copy of the same one, and a name that also appears inside a screen id (`feed` in `feed_home`) is left alone. A non-empty destination needs `--force` and a merge NAMES what it kept (`left: [...]`), the same contract `himi pull --force` has; a shape clash is refused whole before anything is written. There is no `--replace`: a file you were handed is not the plane's per-release archive.", required: "<package.himi> [--out <dir>] [--name <new>] [--force]" },
|
|
71520
71940
|
{ name: "pull", summary: "restore an app's SOURCE from the serve plane into an editable content package \u2014 the other half of `himi push`, which archives the content package on every publish. Owner or editor only: a viewer may download the RUNNING app (`/v1/owner/package`), not the codebase it was built from. A PAST release is restorable, not only the live one \u2014 the archive is content-addressed per release, so it has none of the live-single-slot problem that makes `himi pack` current-release-only. Two limits: pruning keeps the current release plus the newest ~20 ledger entries, and a release id identifies OTA content rather than source, so a source-only change republishes under the same id and replaces that id's archive. A release published before source archiving shipped, or pushed with `--no-source`, answers `app_source_not_found` and says to push again. `--name` re-substitutes the app identity through the same machinery `himi init --template` uses, so the restored tree is a NEW app rather than a second copy of the same one, and a name that also appears inside a screen id (`feed` in `feed_home`) is left alone. A NON-EMPTY destination is refused unless you say which you mean: `--force` merges and reports \u2014 it writes the archive over what is there, keeps files the release does not carry, and NAMES every one of them as `left: [...]` so a tree that matches no release is never silent (#2437); `--replace` restores exactly what was archived, removing those files first (`removed: [...]`) and implying `--force`. `--replace` refuses a directory holding no `himalaya.content.json`, so a mistyped `--out` cannot clear an unrelated tree, and the only paths it can remove are the ones a push would have archived \u2014 `.git/`, `node_modules/`, `dist/`, `.env*` and key material are out of its reach by construction.", required: "<app> [--release <id>] [--out <dir>] [--name <new>] [--force|--replace] [--target <t>]" },
|
|
71521
71941
|
{ name: "share", summary: "publish or revoke a content-addressed `.himi` share link. The package must be a validated archive for an already-published release; the resulting https URL is safe to QR, copy, or open in Himi Player.", required: "--file <package.himi> [--expires-at <ISO>] | revoke --app <app> <shareId>" },
|
|
71522
|
-
{ name: "run", summary: "serve a .himi package on localhost and open it in a browser \u2014 no serve plane, no dev-server, no setup. Given no argument it builds the current content package first, so `himi run` is the inner loop: edit, run, look at it. The SPA and /v1 are served from ONE origin because Tier-5 bundle fetches on web are same-origin-only. --chrome preview frames the app in a device chassis; --chrome app serves it as a plain web app. Blocks until Ctrl-C, like any dev server. --browser runs the whole plane INSIDE the tab (service worker + in-memory store) so no app code executes in this process or on any pod. --lan also serves it to the local network and prints a QR, so a phone on the same Wi-Fi opens it as the real app, full screen. --watch keeps the preview LIVE: every save in the content package rebuilds it and repaints the open tab with only what changed (the dev-server's hot-reload events); a build that fails keeps the last good preview on screen and says why. Any `himi run` of a content package (plain, --watch or --browser) frames its preview on the build board (`himi studio`) while it serves, and all but --browser also serve that board at /__himi/studio/ on the preview's own origin. Its .himi/run.json (pid, watching) is also how `himi render` and `himi test` tell a live --watch is running: while none is, they add a `reminder` naming `himi run --watch --content <dir>
|
|
71942
|
+
{ name: "run", summary: "serve a .himi package on localhost and open it in a browser \u2014 no serve plane, no dev-server, no setup. Given no argument it builds the current content package first, so `himi run` is the inner loop: edit, run, look at it. The SPA and /v1 are served from ONE origin because Tier-5 bundle fetches on web are same-origin-only. --chrome preview frames the app in a device chassis; --chrome app serves it as a plain web app. Blocks until Ctrl-C, like any dev server. --browser runs the whole plane INSIDE the tab (service worker + in-memory store) so no app code executes in this process or on any pod. --lan also serves it to the local network and prints a QR, so a phone on the same Wi-Fi opens it as the real app, full screen. --watch keeps the preview LIVE: every save in the content package rebuilds it and repaints the open tab with only what changed (the dev-server's hot-reload events); a build that fails keeps the last good preview on screen and says why. --idle-exit <min> (with --watch only) stops the watch once that many minutes pass with no source save, printing `{\"watch\":\"idle_exit\"}` and removing its .himi/run.json on the way out \u2014 the watch `himi init` starts passes 30; one started by hand runs until Ctrl-C. Stopping (Ctrl-C, SIGTERM, SIGHUP or the idle exit) removes the run's own .himi/run.json. Any `himi run` of a content package (plain, --watch or --browser) frames its preview on the build board (`himi studio`) while it serves, and all but --browser also serve that board at /__himi/studio/ on the preview's own origin. Its .himi/run.json (pid, watching) is also how `himi render` and `himi test` tell a live --watch is running: while none is, they add a `reminder` naming `himi run --watch --content <dir> &`, and while one is (the one `himi init` started, usually, whose .himi/watch.json vouches for it until it serves) the reminder names its url and board instead (either at most once every ten minutes per app) \u2014 first in `next[]` and again as the envelope's LAST key (the last line of `himi test`'s plain report), so an output read with `tail` still carries it.", required: "[<package.himi|dir|url>] [--browser] [--watch] [--idle-exit <min>] [--lan] [--content <dir>] [--port <n>] [--chrome preview|app] [--screen <id>] [--surface <id>] [--no-open] [--spa <url>]" },
|
|
71523
71943
|
{ name: "mcp", summary: "run the himi tools as a local Model Context Protocol server over stdio, for coding agents \u2014 Claude Code, Cursor, Codex, VS Code. Tools: docs/components/sdk/icons, init, build, validate, test (hermetic), render (returns the PNG), progress, a live preview (start/status/stop a `himi run --watch`), whoami, and DRAFT-only push/share/preview links \u2014 never promote, login, dist or secrets. Each tool runs the same command in-process on the content package on disk (default: the host's workspace root). `himi init` and `himi agent-init` write the config for every host (.mcp.json, .cursor/mcp.json, .vscode/mcp.json, .codex/config.toml when absent) plus a `himi-preview` launch configuration (.claude/launch.json) that opens the live preview in Claude Code desktop's browser pane. Never self-updates (npx already runs the current CLI). Blocks until the host closes stdin. `himi mcp plugin --out <dir>` writes the Claude Code plugin (the customer skill + this server) as a folder \u2014 the same plugin the serve plane's marketplace hands out at /.claude-plugin/marketplace.json.", required: "[plugin --out <dir>]" },
|
|
71524
71944
|
{ name: "preview", summary: "print the shareable web URL + himi:// deep link + agent /v1/preview JSON URLs for an app+release; --surface frames the web preview as any catalog surface (?surface=)", required: "--app --release [--screen] [--surface]" },
|
|
71525
71945
|
{ name: "surfaces", summary: "the surface catalog vs this app's envelope (platforms.json): targeted surfaces, the opt-in key for the rest, per-surface preview links with --app --release, and which widget families the package produces with --measure-widgets (which runs its built bundles)", required: "[--content <dir>] [--app --release] [--measure-widgets]" },
|
|
@@ -71603,16 +72023,16 @@ function createScopedRegistryResolver(exec = execNpmConfig) {
|
|
|
71603
72023
|
}
|
|
71604
72024
|
var SCOPED_REGISTRY_TIMEOUT_MS = 5e3;
|
|
71605
72025
|
function execNpmConfig(file, args, options) {
|
|
71606
|
-
return new Promise((
|
|
71607
|
-
execFile(file, args, { ...options, windowsHide: true }, (error, stdout) => error ? reject(error) :
|
|
72026
|
+
return new Promise((resolve53, reject) => {
|
|
72027
|
+
execFile(file, args, { ...options, windowsHide: true }, (error, stdout) => error ? reject(error) : resolve53(String(stdout)));
|
|
71608
72028
|
});
|
|
71609
72029
|
}
|
|
71610
72030
|
var npmScopedWixRegistry = createScopedRegistryResolver();
|
|
71611
72031
|
async function scopedRegistryBefore(lookup2, deadline) {
|
|
71612
72032
|
let cancel = () => {
|
|
71613
72033
|
};
|
|
71614
|
-
const expired = new Promise((
|
|
71615
|
-
const timer = setTimeout(() =>
|
|
72034
|
+
const expired = new Promise((resolve53) => {
|
|
72035
|
+
const timer = setTimeout(() => resolve53(void 0), Math.max(0, deadline - Date.now()));
|
|
71616
72036
|
cancel = () => clearTimeout(timer);
|
|
71617
72037
|
});
|
|
71618
72038
|
try {
|
|
@@ -71677,7 +72097,7 @@ function packageVersion(path2) {
|
|
|
71677
72097
|
}
|
|
71678
72098
|
}
|
|
71679
72099
|
function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
|
|
71680
|
-
let dir =
|
|
72100
|
+
let dir = resolve52(fromDir);
|
|
71681
72101
|
for (; ; ) {
|
|
71682
72102
|
const version2 = packageVersion(join64(dir, "node_modules", "@wix", "himalaya", "package.json"));
|
|
71683
72103
|
if (version2) return { version: version2, source: "local" };
|
|
@@ -71842,7 +72262,7 @@ function thisHost() {
|
|
|
71842
72262
|
return hostname().replace(/\s+/g, "_");
|
|
71843
72263
|
}
|
|
71844
72264
|
function lockStamp(now) {
|
|
71845
|
-
return `${now} ${process.pid} ${thisHost()} ${
|
|
72265
|
+
return `${now} ${process.pid} ${thisHost()} ${randomUUID7()}`;
|
|
71846
72266
|
}
|
|
71847
72267
|
function processAlive(pid) {
|
|
71848
72268
|
try {
|
|
@@ -71996,21 +72416,21 @@ function loaderArgs(execArgv) {
|
|
|
71996
72416
|
}
|
|
71997
72417
|
var spawnUpdateRefresh = createUpdateRefreshSpawner();
|
|
71998
72418
|
function createTrimValidator(deps = {}, spawnFn = spawn7) {
|
|
71999
|
-
const outcome = (code,
|
|
72419
|
+
const outcome = (code, report3, said) => {
|
|
72000
72420
|
let counts;
|
|
72001
72421
|
let errors;
|
|
72002
72422
|
try {
|
|
72003
|
-
const parsed = JSON.parse(
|
|
72423
|
+
const parsed = JSON.parse(report3);
|
|
72004
72424
|
counts = parsed.counts;
|
|
72005
72425
|
if (Array.isArray(parsed.findings)) {
|
|
72006
|
-
const
|
|
72426
|
+
const text8 = (v) => typeof v === "string" && v ? v : void 0;
|
|
72007
72427
|
errors = parsed.findings.filter((f) => f && f.severity === "error" && typeof f.id === "string").map((f) => ({
|
|
72008
72428
|
id: f.id,
|
|
72009
|
-
message:
|
|
72010
|
-
...
|
|
72011
|
-
...
|
|
72012
|
-
...
|
|
72013
|
-
...
|
|
72429
|
+
message: text8(f.message) ?? "",
|
|
72430
|
+
...text8(f.pass) ? { pass: text8(f.pass) } : {},
|
|
72431
|
+
...text8(f.screen) ? { screen: text8(f.screen) } : {},
|
|
72432
|
+
...text8(f.path) ? { path: text8(f.path) } : {},
|
|
72433
|
+
...text8(f.ruleCode) ? { ruleCode: text8(f.ruleCode) } : {}
|
|
72014
72434
|
}));
|
|
72015
72435
|
}
|
|
72016
72436
|
} catch {
|
|
@@ -72018,7 +72438,7 @@ function createTrimValidator(deps = {}, spawnFn = spawn7) {
|
|
|
72018
72438
|
return { code, counts, ...errors ? { errors } : {}, ...code !== 0 ? { tail: said.slice(-4e3) } : {} };
|
|
72019
72439
|
};
|
|
72020
72440
|
return async (dir, opts = {}) => {
|
|
72021
|
-
const argv = ["validate", "--content",
|
|
72441
|
+
const argv = ["validate", "--content", resolve52(dir), "--strict", "--skip-version-check"];
|
|
72022
72442
|
if (!opts.fresh) {
|
|
72023
72443
|
const out2 = [];
|
|
72024
72444
|
const err = [];
|
|
@@ -72634,7 +73054,7 @@ function keptPlan(kept) {
|
|
|
72634
73054
|
return note ? { kept, note } : {};
|
|
72635
73055
|
}
|
|
72636
73056
|
async function initFromRemoteTemplate(name, parentDir, template, c) {
|
|
72637
|
-
const dir =
|
|
73057
|
+
const dir = resolve52(parentDir, name);
|
|
72638
73058
|
if (existsSync66(join64(dir, "himalaya.content.json"))) {
|
|
72639
73059
|
throw new Error(`content package already exists at ${dir}`);
|
|
72640
73060
|
}
|
|
@@ -72647,17 +73067,17 @@ async function initFromRemoteTemplate(name, parentDir, template, c) {
|
|
|
72647
73067
|
return finishScaffold(materializeSource(archive.files, name, dir), template);
|
|
72648
73068
|
}
|
|
72649
73069
|
function contentDir(flags) {
|
|
72650
|
-
return
|
|
73070
|
+
return resolve52(str8(flags.content) ?? process.cwd());
|
|
72651
73071
|
}
|
|
72652
73072
|
function renameRestoredSource(restored, declaredApp, renamed) {
|
|
72653
|
-
const
|
|
73073
|
+
const text8 = {};
|
|
72654
73074
|
for (const entry of restored) {
|
|
72655
73075
|
const decoded = entry.bytes.toString("utf8");
|
|
72656
|
-
if (Buffer.from(decoded, "utf8").equals(entry.bytes))
|
|
73076
|
+
if (Buffer.from(decoded, "utf8").equals(entry.bytes)) text8[entry.path] = decoded;
|
|
72657
73077
|
}
|
|
72658
|
-
const sentineled = sentinelizeArchive(
|
|
73078
|
+
const sentineled = sentinelizeArchive(text8, declaredApp);
|
|
72659
73079
|
for (const entry of restored) {
|
|
72660
|
-
if (!(entry.path in
|
|
73080
|
+
if (!(entry.path in text8)) continue;
|
|
72661
73081
|
const body = sentineled[entry.path];
|
|
72662
73082
|
if (body !== void 0) entry.bytes = Buffer.from(substituteIdentity(body, renamed), "utf8");
|
|
72663
73083
|
}
|
|
@@ -72691,7 +73111,7 @@ function restoreDestinationIssue(dest, force) {
|
|
|
72691
73111
|
return null;
|
|
72692
73112
|
}
|
|
72693
73113
|
function isHimalayaMonorepoCheckout(start) {
|
|
72694
|
-
let dir =
|
|
73114
|
+
let dir = resolve52(start);
|
|
72695
73115
|
for (; ; ) {
|
|
72696
73116
|
if (existsSync66(join64(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync66(join64(dir, "core", "serve", "src", "server.ts"))) return true;
|
|
72697
73117
|
const parent = dirname36(dir);
|
|
@@ -72753,7 +73173,7 @@ function materializeForPreview(appDir3, slug, displayName) {
|
|
|
72753
73173
|
const forBuild = Object.fromEntries(
|
|
72754
73174
|
Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
|
|
72755
73175
|
);
|
|
72756
|
-
const repoRoot2 =
|
|
73176
|
+
const repoRoot2 = resolve52(fileURLToPath26(new URL("../../..", import.meta.url)));
|
|
72757
73177
|
const root = join64(repoRoot2, "node_modules", ".cache", "himi-template-preview");
|
|
72758
73178
|
const stillRunning = (pid) => {
|
|
72759
73179
|
try {
|
|
@@ -72999,7 +73419,7 @@ Seed your coding agent with \`himi agent-init\`. Guides: ${idx.portalUrl}`);
|
|
|
72999
73419
|
return 0;
|
|
73000
73420
|
}
|
|
73001
73421
|
case "agent-init": {
|
|
73002
|
-
const dir =
|
|
73422
|
+
const dir = resolve52(str8(flags.dir) ?? process.cwd());
|
|
73003
73423
|
const { seedAgentFiles: seedAgentFiles2, NotAContentPackage: NotAContentPackage2 } = await Promise.resolve().then(() => (init_agent_context(), agent_context_exports));
|
|
73004
73424
|
let res;
|
|
73005
73425
|
try {
|
|
@@ -73556,7 +73976,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
73556
73976
|
return 2;
|
|
73557
73977
|
}
|
|
73558
73978
|
const template = str8(flags.template);
|
|
73559
|
-
const parentDir =
|
|
73979
|
+
const parentDir = resolve52(str8(flags.dir) ?? process.cwd());
|
|
73560
73980
|
const withAgentFiles = async (dir) => {
|
|
73561
73981
|
const seeded = await seedAgentFilesFor(dir, flags);
|
|
73562
73982
|
if (!seeded.agent || "error" in seeded.agent) return seeded;
|
|
@@ -73576,16 +73996,23 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
|
|
|
73576
73996
|
return { ...seeded, next };
|
|
73577
73997
|
};
|
|
73578
73998
|
const startSession = async (dir, agent2, source) => {
|
|
73579
|
-
const { openBoardForSession: openBoardForSession2, sessionSteps: sessionSteps2, homeScreenIdOf: homeScreenIdOf2, firstLookAtInit: firstLookAtInit2, recordInitSource: recordInitSource2, doNowBlock: doNowBlock2 } = await Promise.resolve().then(() => (init_milestones(), milestones_exports));
|
|
73999
|
+
const { openBoardForSession: openBoardForSession2, sessionSteps: sessionSteps2, homeScreenIdOf: homeScreenIdOf2, firstLookAtInit: firstLookAtInit2, recordInitSource: recordInitSource2, doNowBlock: doNowBlock2, startWatchAtInit: startWatchAtInit2 } = await Promise.resolve().then(() => (init_milestones(), milestones_exports));
|
|
73580
74000
|
const board = await openBoardForSession2(dir);
|
|
73581
74001
|
const home = homeScreenIdOf2(dir);
|
|
73582
74002
|
recordInitSource2(dir, { template: template ?? null });
|
|
73583
74003
|
const firstLook = flags["no-first-look"] === true ? { skipped: "--no-first-look" } : await firstLookAtInit2(dir, { source, home, entry: fileURLToPath26(import.meta.url), loader: loaderArgs(process.execArgv) });
|
|
73584
74004
|
const pictured = "image" in firstLook;
|
|
74005
|
+
const watch2 = await startWatchAtInit2(dir, {
|
|
74006
|
+
source,
|
|
74007
|
+
app: await appId(dir),
|
|
74008
|
+
entry: fileURLToPath26(import.meta.url),
|
|
74009
|
+
loader: loaderArgs(process.execArgv),
|
|
74010
|
+
noWatch: flags["no-watch"] === true
|
|
74011
|
+
});
|
|
73585
74012
|
const { next: later2, ...rest } = agent2;
|
|
73586
|
-
const doNow = doNowBlock2({ dir, home, pictured, source });
|
|
74013
|
+
const doNow = doNowBlock2({ dir, home, pictured, source, watch: watch2 });
|
|
73587
74014
|
sayDoNow(doNow);
|
|
73588
|
-
return { doNow, ...rest, board, firstLook, next: [...sessionSteps2({ dir, home, pictured, source }), ...later2 ?? []] };
|
|
74015
|
+
return { doNow, ...rest, board, firstLook, watch: watch2, next: [...sessionSteps2({ dir, home, pictured, source, watch: watch2 }), ...later2 ?? []] };
|
|
73589
74016
|
};
|
|
73590
74017
|
const sayDoNow = (doNow, again = false) => {
|
|
73591
74018
|
if (io.isTTY === true && flags.json !== true) io.error(`Do these now${again ? " (again)" : ""}:
|
|
@@ -73710,8 +74137,8 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
73710
74137
|
return 2;
|
|
73711
74138
|
}
|
|
73712
74139
|
}
|
|
73713
|
-
const appDir3 =
|
|
73714
|
-
const sourceDir =
|
|
74140
|
+
const appDir3 = resolve52(str8(flags.dir) ?? process.cwd());
|
|
74141
|
+
const sourceDir = resolve52(str8(flags["source-dir"]) ?? appDir3);
|
|
73715
74142
|
let meta;
|
|
73716
74143
|
try {
|
|
73717
74144
|
meta = JSON.parse(readFileSync79(join64(sourceDir, TEMPLATE_META_FILE), "utf8"));
|
|
@@ -73753,7 +74180,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
73753
74180
|
...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
|
|
73754
74181
|
collect: { lint: true, strict }
|
|
73755
74182
|
});
|
|
73756
|
-
const scaffoldSourceFindings =
|
|
74183
|
+
const scaffoldSourceFindings = resolve52(sourceDir) === resolve52(appDir3) ? [] : [
|
|
73757
74184
|
...tokenColorIssues(sourceDir),
|
|
73758
74185
|
...surfaceIssues(sourceDir, void 0, strict),
|
|
73759
74186
|
...await fontIssues(sourceDir, strict),
|
|
@@ -73765,7 +74192,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
73765
74192
|
message: `scaffold source (--source-dir ${sourceDir}): ${issue2.message}`
|
|
73766
74193
|
}));
|
|
73767
74194
|
const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
|
|
73768
|
-
const { report:
|
|
74195
|
+
const { report: report3, hasErrors } = summarizeLint(app, bres, outcome, strict, [
|
|
73769
74196
|
...await cmsIssues(buildDir, app, strict, { local: true }),
|
|
73770
74197
|
...surfaceIssues(buildDir, outcome.surfaceNav, strict),
|
|
73771
74198
|
...tokenColorIssues(buildDir),
|
|
@@ -73775,7 +74202,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
73775
74202
|
...scaffoldSourceFindings
|
|
73776
74203
|
]);
|
|
73777
74204
|
if (hasErrors && !force) {
|
|
73778
|
-
print({ ...
|
|
74205
|
+
print({ ...report3, published: false, hint: "lint found errors \u2014 fix them, or re-run with --force to publish anyway" });
|
|
73779
74206
|
return 1;
|
|
73780
74207
|
}
|
|
73781
74208
|
const payload = await loadReleaseFromDir(app, out2);
|
|
@@ -73796,7 +74223,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
73796
74223
|
}));
|
|
73797
74224
|
return 1;
|
|
73798
74225
|
}
|
|
73799
|
-
const iconPath = str8(flags.icon) ?
|
|
74226
|
+
const iconPath = str8(flags.icon) ? resolve52(str8(flags.icon)) : templateIconPath(name);
|
|
73800
74227
|
let iconHash;
|
|
73801
74228
|
if (iconPath) {
|
|
73802
74229
|
try {
|
|
@@ -74050,7 +74477,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
74050
74477
|
...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
|
|
74051
74478
|
collect: { lint: true, strict }
|
|
74052
74479
|
});
|
|
74053
|
-
const { report:
|
|
74480
|
+
const { report: report3, hasErrors } = summarizeLint(app, bres, outcome, strict, [
|
|
74054
74481
|
...await cmsIssues(dir, app, strict, { local: true }),
|
|
74055
74482
|
...surfaceIssues(dir, outcome.surfaceNav, strict),
|
|
74056
74483
|
...tokenColorIssues(dir),
|
|
@@ -74067,10 +74494,10 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
74067
74494
|
if (authWarning) advisories.push(authWarning);
|
|
74068
74495
|
advisories.push(...await nativeConfigAdvisories(app, configSource));
|
|
74069
74496
|
const finalReport = advisories.length ? {
|
|
74070
|
-
...
|
|
74071
|
-
warnings: [...
|
|
74072
|
-
counts: { ...
|
|
74073
|
-
} :
|
|
74497
|
+
...report3,
|
|
74498
|
+
warnings: [...report3.warnings ?? [], ...advisories],
|
|
74499
|
+
counts: { ...report3.counts, warnings: report3.counts.warnings + advisories.length }
|
|
74500
|
+
} : report3;
|
|
74074
74501
|
await leaveForBoard(dir, "validate", {
|
|
74075
74502
|
ok: !hasErrors,
|
|
74076
74503
|
errors: finalReport.counts.errors,
|
|
@@ -74183,23 +74610,23 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
74183
74610
|
const app = str8(flags.app) ?? loaded.app;
|
|
74184
74611
|
const invoked2 = await invokeRemoteFunction2({ baseUrl: target.baseUrl, app, definition, body: input, params, releaseId: remoteRelease, previewToken: str8(flags["preview-token"]), boCookie: boCookie() });
|
|
74185
74612
|
const response = invoked2.response;
|
|
74186
|
-
const
|
|
74613
|
+
const text9 = await response.text();
|
|
74187
74614
|
let body2;
|
|
74188
74615
|
try {
|
|
74189
|
-
body2 = JSON.parse(
|
|
74616
|
+
body2 = JSON.parse(text9);
|
|
74190
74617
|
} catch {
|
|
74191
|
-
body2 =
|
|
74618
|
+
body2 = text9;
|
|
74192
74619
|
}
|
|
74193
74620
|
print({ ok: response.ok, app, releaseId: remoteRelease ?? null, url: invoked2.url, status: response.status, body: body2 });
|
|
74194
74621
|
return response.ok ? 0 : 1;
|
|
74195
74622
|
}
|
|
74196
74623
|
const invoked = await invokeLocalFunction2(loaded, name, input, params);
|
|
74197
|
-
const
|
|
74624
|
+
const text8 = await invoked.response.text();
|
|
74198
74625
|
let body;
|
|
74199
74626
|
try {
|
|
74200
|
-
body = JSON.parse(
|
|
74627
|
+
body = JSON.parse(text8);
|
|
74201
74628
|
} catch {
|
|
74202
|
-
body =
|
|
74629
|
+
body = text8;
|
|
74203
74630
|
}
|
|
74204
74631
|
print({ ok: invoked.response.ok, app: loaded.app, releaseId: invoked.releaseId, status: invoked.response.status, body });
|
|
74205
74632
|
return invoked.response.ok ? 0 : 1;
|
|
@@ -74218,7 +74645,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
74218
74645
|
return 1;
|
|
74219
74646
|
}
|
|
74220
74647
|
const { himiPluginFiles: himiPluginFiles2 } = await Promise.resolve().then(() => (init_claude_plugin(), claude_plugin_exports));
|
|
74221
|
-
const dir =
|
|
74648
|
+
const dir = resolve52(out2);
|
|
74222
74649
|
const files = himiPluginFiles2(skills.skillMd);
|
|
74223
74650
|
for (const f of files) {
|
|
74224
74651
|
mkdirSync32(dirname36(join64(dir, f.path)), { recursive: true });
|
|
@@ -74240,7 +74667,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
74240
74667
|
}
|
|
74241
74668
|
if (pos[1] === "reel") {
|
|
74242
74669
|
const { buildReel: buildReel2 } = await Promise.resolve().then(() => (init_studio_reel(), studio_reel_exports));
|
|
74243
|
-
const out2 =
|
|
74670
|
+
const out2 = resolve52(str8(flags.out) ?? join64(dir, ".himi", "making-of.html"));
|
|
74244
74671
|
const { html, plan } = buildReel2(dir);
|
|
74245
74672
|
mkdirSync32(dirname36(out2), { recursive: true });
|
|
74246
74673
|
writeFileSync29(out2, html);
|
|
@@ -74252,7 +74679,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
74252
74679
|
openUrl2(url2);
|
|
74253
74680
|
}
|
|
74254
74681
|
const handover = { file: out2, hint: `hand ${basename17(out2)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` };
|
|
74255
|
-
const
|
|
74682
|
+
const report3 = {
|
|
74256
74683
|
ok: true,
|
|
74257
74684
|
file: out2,
|
|
74258
74685
|
url: url2,
|
|
@@ -74265,7 +74692,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
74265
74692
|
...plan.dropped ? { dropped: plan.dropped, note: "pictures were left out to keep the file attachable" } : {},
|
|
74266
74693
|
...plan.frames.length === 0 ? { hint: "no screen versions were recorded \u2014 run `himi studio` before building so the board keeps them" } : {}
|
|
74267
74694
|
};
|
|
74268
|
-
if (flags.json === true || io.isTTY !== true) print(
|
|
74695
|
+
if (flags.json === true || io.isTTY !== true) print(report3);
|
|
74269
74696
|
else io.log(`The making-of: ${out2}
|
|
74270
74697
|
${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
74271
74698
|
return 0;
|
|
@@ -74351,10 +74778,10 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74351
74778
|
}
|
|
74352
74779
|
if (flags.brief === true) {
|
|
74353
74780
|
const { agentContextNote: agentContextNote2 } = await Promise.resolve().then(() => (init_agent_context(), agent_context_exports));
|
|
74354
|
-
const
|
|
74781
|
+
const summary2 = summarizeProgress2(readProgress2(dir).entries);
|
|
74355
74782
|
const open = openApprovals2(readApprovals2(dir).events);
|
|
74356
74783
|
const note = agentContextNote2(dir, cliVersion());
|
|
74357
|
-
io.log(formatBrief2({ appName: appLabel2(dir), isContentPackage, summary, open, ...note ? { agentContextNote: note } : {} }));
|
|
74784
|
+
io.log(formatBrief2({ appName: appLabel2(dir), isContentPackage, summary: summary2, open, ...note ? { agentContextNote: note } : {} }));
|
|
74358
74785
|
return 0;
|
|
74359
74786
|
}
|
|
74360
74787
|
if (!message) {
|
|
@@ -74386,8 +74813,9 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74386
74813
|
return 2;
|
|
74387
74814
|
}
|
|
74388
74815
|
let imageNote;
|
|
74816
|
+
const handover = step === "review";
|
|
74389
74817
|
if (!images.length && flags["no-image"] !== true && isContentPackage) {
|
|
74390
|
-
const auto = (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).homeShotForPostcard(dir);
|
|
74818
|
+
const auto = (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).homeShotForPostcard(dir, { repeatOk: handover });
|
|
74391
74819
|
if ("image" in auto) images.push(auto.image);
|
|
74392
74820
|
else imageNote = `no picture attached: ${auto.skipped}`;
|
|
74393
74821
|
}
|
|
@@ -74396,7 +74824,8 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74396
74824
|
const notified = flags.notify === true ? await notifyDesktop2(`himi \xB7 ${appLabel2(dir) ?? "your app"}`, message) : void 0;
|
|
74397
74825
|
const { count: count2 } = summarizeProgress2(readProgress2(dir).entries);
|
|
74398
74826
|
const imageAttached = (entry.images?.length ?? 0) > 0;
|
|
74399
|
-
|
|
74827
|
+
const handed = handover && isContentPackage ? (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).afterHandoverPostcard(dir, { content: str8(flags.content) }) : null;
|
|
74828
|
+
print({ ok: true, entry, journal: journal2, count: count2, imageAttached, ...!imageAttached && imageNote ? { imageNote } : {}, ...notified === void 0 ? {} : { notified }, ...handed ? { makingOf: handed.makingOf, next: handed.next } : {}, ...hint });
|
|
74400
74829
|
return 0;
|
|
74401
74830
|
} catch (e) {
|
|
74402
74831
|
if (e instanceof ProgressError2) {
|
|
@@ -74410,9 +74839,9 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74410
74839
|
const sub = pos[1] ?? "audit";
|
|
74411
74840
|
const dir = contentDir(flags);
|
|
74412
74841
|
if (sub === "audit") {
|
|
74413
|
-
const
|
|
74414
|
-
print(
|
|
74415
|
-
return
|
|
74842
|
+
const report3 = auditUx(dir, flags.strict === true);
|
|
74843
|
+
print(report3);
|
|
74844
|
+
return report3.ok ? 0 : 1;
|
|
74416
74845
|
}
|
|
74417
74846
|
if (sub !== "record") {
|
|
74418
74847
|
io.error(JSON.stringify({ error: "ux needs audit | record" }));
|
|
@@ -74624,7 +75053,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74624
75053
|
}
|
|
74625
75054
|
const authoringBase = serveBase();
|
|
74626
75055
|
try {
|
|
74627
|
-
const { report:
|
|
75056
|
+
const { report: report3 } = await (deps.browserAuthoring ?? runBrowserAuthoring2)({
|
|
74628
75057
|
authoringBase,
|
|
74629
75058
|
job: {
|
|
74630
75059
|
entry: sources.entry,
|
|
@@ -74641,10 +75070,10 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74641
75070
|
log: (line) => io.error(line)
|
|
74642
75071
|
});
|
|
74643
75072
|
const { afterBrowserTestRun: afterBrowserTestRun2, watchReminder: watchReminder3 } = await Promise.resolve().then(() => (init_milestones(), milestones_exports));
|
|
74644
|
-
const afterRun2 = afterBrowserTestRun2(dir, { screen: screen.screenId, report:
|
|
75073
|
+
const afterRun2 = afterBrowserTestRun2(dir, { screen: screen.screenId, report: report3, boardOpen: boardOpen(dir) });
|
|
74645
75074
|
const browserReminder = watchReminder3(dir, { content: str8(flags.content) });
|
|
74646
75075
|
print({
|
|
74647
|
-
...
|
|
75076
|
+
...report3,
|
|
74648
75077
|
screen: screen.screenId,
|
|
74649
75078
|
ranIn: "browser",
|
|
74650
75079
|
liveRequested: jobWix !== void 0,
|
|
@@ -74653,7 +75082,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74653
75082
|
// Also the envelope's LAST key (`tailLast` keeps it there): an agent may read only the tail.
|
|
74654
75083
|
...browserReminder ? { reminder: browserReminder } : {}
|
|
74655
75084
|
});
|
|
74656
|
-
return
|
|
75085
|
+
return report3.ok ? 0 : 1;
|
|
74657
75086
|
} catch (e) {
|
|
74658
75087
|
io.error(JSON.stringify({ error: e.message }));
|
|
74659
75088
|
return 1;
|
|
@@ -74838,6 +75267,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
|
|
|
74838
75267
|
gaps: ledger,
|
|
74839
75268
|
artifacts: result.shots?.dir ? [{ kind: "shots-dir", path: result.shots.dir }] : []
|
|
74840
75269
|
}),
|
|
75270
|
+
...afterRun.firstLook ? { firstLookPostcard: afterRun.firstLook } : {},
|
|
74841
75271
|
...afterRun.postcard ? { postcard: afterRun.postcard } : {},
|
|
74842
75272
|
next: [...testReminder ? [testReminder] : [], ...afterRun.next],
|
|
74843
75273
|
// Also the envelope's LAST key (`tailLast` keeps it there): an agent may read only the tail.
|
|
@@ -74998,7 +75428,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
|
|
|
74998
75428
|
const pngTarget = typeof flags.png === "string" ? str8(flags.png) : flags.png === true ? shotPath : void 0;
|
|
74999
75429
|
if (pngTarget && r.pngBase64) {
|
|
75000
75430
|
pngFile = writePngFile(pngTarget, r.pngBase64);
|
|
75001
|
-
attestationPngFile =
|
|
75431
|
+
attestationPngFile = resolve52(pngTarget) === resolve52(shotPath) ? pngFile : writePngFile(shotPath, r.pngBase64);
|
|
75002
75432
|
}
|
|
75003
75433
|
if (renderWidth !== void 0 && r.renderable) {
|
|
75004
75434
|
recordRenderWidth(dir, {
|
|
@@ -75090,23 +75520,23 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
|
|
|
75090
75520
|
return 2;
|
|
75091
75521
|
}
|
|
75092
75522
|
const { kitReport: kitReport2 } = await Promise.resolve().then(() => (init_design_kits(), design_kits_exports));
|
|
75093
|
-
const
|
|
75094
|
-
print(
|
|
75095
|
-
return
|
|
75523
|
+
const report3 = kitReport2(pos[2]);
|
|
75524
|
+
print(report3);
|
|
75525
|
+
return report3.ok ? 0 : 1;
|
|
75096
75526
|
}
|
|
75097
75527
|
case "fonts": {
|
|
75098
75528
|
const sub = pos[1];
|
|
75099
75529
|
const dir = contentDir(flags);
|
|
75100
75530
|
const { listReport: listReport3, checkReport: checkReport2, addReport: addReport2 } = await Promise.resolve().then(() => (init_fonts(), fonts_exports));
|
|
75101
75531
|
if (sub === void 0 || sub === "list") {
|
|
75102
|
-
const
|
|
75103
|
-
print(
|
|
75104
|
-
return
|
|
75532
|
+
const report3 = listReport3({ licenses: flags.licenses === true });
|
|
75533
|
+
print(report3);
|
|
75534
|
+
return report3.ok ? 0 : 1;
|
|
75105
75535
|
}
|
|
75106
75536
|
if (sub === "check") {
|
|
75107
|
-
const
|
|
75108
|
-
print(
|
|
75109
|
-
return
|
|
75537
|
+
const report3 = await checkReport2(dir);
|
|
75538
|
+
print(report3);
|
|
75539
|
+
return report3.ok ? 0 : 1;
|
|
75110
75540
|
}
|
|
75111
75541
|
if (sub === "add") {
|
|
75112
75542
|
const family = pos[2];
|
|
@@ -75114,9 +75544,9 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
|
|
|
75114
75544
|
print({ ok: false, problems: ["usage: himi fonts add <Family>"] });
|
|
75115
75545
|
return 2;
|
|
75116
75546
|
}
|
|
75117
|
-
const
|
|
75118
|
-
print(
|
|
75119
|
-
return
|
|
75547
|
+
const report3 = addReport2(dir, family);
|
|
75548
|
+
print(report3);
|
|
75549
|
+
return report3.ok ? 0 : 1;
|
|
75120
75550
|
}
|
|
75121
75551
|
print({ ok: false, problems: [`unknown subcommand "${sub}"`], hint: "himi fonts [list|check|add]" });
|
|
75122
75552
|
return 2;
|
|
@@ -75137,7 +75567,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
|
|
|
75137
75567
|
summarize: summarize4
|
|
75138
75568
|
} = await Promise.resolve().then(() => (init_icon(), icon_exports));
|
|
75139
75569
|
const requireVerified = sub === "check";
|
|
75140
|
-
const
|
|
75570
|
+
const report3 = async (status, extra = {}) => {
|
|
75141
75571
|
const findings = [
|
|
75142
75572
|
// Not a store rule, so it is not in byteIssues — but it is the one `himi dist`
|
|
75143
75573
|
// will refuse, and the point of `check` is to say so before the build does.
|
|
@@ -75175,7 +75605,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
|
|
|
75175
75605
|
}
|
|
75176
75606
|
return blocksRelease2(status, { requireVerified }) ? 1 : 0;
|
|
75177
75607
|
};
|
|
75178
|
-
if (sub === "status" || sub === "check") return
|
|
75608
|
+
if (sub === "status" || sub === "check") return report3(await iconStatus2(dir));
|
|
75179
75609
|
if (sub === "set") {
|
|
75180
75610
|
const file = pos[2];
|
|
75181
75611
|
if (!file) {
|
|
@@ -75183,7 +75613,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
|
|
|
75183
75613
|
return 1;
|
|
75184
75614
|
}
|
|
75185
75615
|
const res = await installIcon2(dir, file, { derivePlay: true });
|
|
75186
|
-
return
|
|
75616
|
+
return report3(res.status, { wrote: res.wrote, normalized: res.normalized, playListingIcon: res.playDerived });
|
|
75187
75617
|
}
|
|
75188
75618
|
if (sub === "from-site") {
|
|
75189
75619
|
const book = str8(flags.book) ?? ".himi/business-book";
|
|
@@ -75198,11 +75628,11 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
|
|
|
75198
75628
|
return 1;
|
|
75199
75629
|
}
|
|
75200
75630
|
const res = await installIcon2(dir, logo, { derivePlay: true });
|
|
75201
|
-
return
|
|
75631
|
+
return report3(res.status, { from: logo, wrote: res.wrote, normalized: res.normalized, playListingIcon: res.playDerived });
|
|
75202
75632
|
}
|
|
75203
75633
|
if (sub === "generate") {
|
|
75204
75634
|
const res = await generateIcon2(dir, { glyph: str8(flags.glyph), background: str8(flags.accent), derivePlay: true });
|
|
75205
|
-
return
|
|
75635
|
+
return report3(res.status, { wrote: res.wrote });
|
|
75206
75636
|
}
|
|
75207
75637
|
if (sub === "glyphs") {
|
|
75208
75638
|
const names = glyphNames2();
|
|
@@ -75228,9 +75658,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75228
75658
|
const dir = contentDir(flags);
|
|
75229
75659
|
const { listReport: listReport3, verifyReport: verifyReport2, addAssetPayload: addAssetPayload2, removeAssetPayload: removeAssetPayload2 } = await Promise.resolve().then(() => (init_assets(), assets_exports));
|
|
75230
75660
|
if (sub === void 0 || sub === "list") {
|
|
75231
|
-
const
|
|
75232
|
-
print(
|
|
75233
|
-
return
|
|
75661
|
+
const report3 = listReport3(dir);
|
|
75662
|
+
print(report3);
|
|
75663
|
+
return report3.ok ? 0 : 1;
|
|
75234
75664
|
}
|
|
75235
75665
|
if (sub === "verify") {
|
|
75236
75666
|
const app = str8(flags.app) ?? await appId(dir);
|
|
@@ -75239,9 +75669,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75239
75669
|
io.error(JSON.stringify(found));
|
|
75240
75670
|
return 1;
|
|
75241
75671
|
}
|
|
75242
|
-
const
|
|
75243
|
-
print(
|
|
75244
|
-
return
|
|
75672
|
+
const report3 = verifyReport2(dir, app, found.manifest);
|
|
75673
|
+
print(report3);
|
|
75674
|
+
return report3.ok ? 0 : 1;
|
|
75245
75675
|
}
|
|
75246
75676
|
if (sub === "add" || sub === "rm") {
|
|
75247
75677
|
const app = str8(flags.app) ?? await appId(dir);
|
|
@@ -75334,7 +75764,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75334
75764
|
return 2;
|
|
75335
75765
|
}
|
|
75336
75766
|
const renamed = str8(flags.name);
|
|
75337
|
-
const dest =
|
|
75767
|
+
const dest = resolve52(str8(flags.out) ?? process.cwd(), renamed ?? wanted);
|
|
75338
75768
|
const replace = flags.replace === true;
|
|
75339
75769
|
const force = replace || flags.force === true;
|
|
75340
75770
|
const linked = symlinkDestinationIssue(dest);
|
|
@@ -75407,7 +75837,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75407
75837
|
let removed = [];
|
|
75408
75838
|
if (replace) removed = removeStaleSource2(restored, dest);
|
|
75409
75839
|
else left = staleSourcePaths2(restored, dest);
|
|
75410
|
-
const
|
|
75840
|
+
const refused2 = deniedArchivePaths2(restored);
|
|
75411
75841
|
const written = writeAppSource2(restored, dest);
|
|
75412
75842
|
const agent = await seedAgentFilesFor(dest, flags, "restore");
|
|
75413
75843
|
print({
|
|
@@ -75418,8 +75848,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75418
75848
|
...agent,
|
|
75419
75849
|
...renamed ? { renamedTo: renamed } : {},
|
|
75420
75850
|
...removed.length ? { removed } : {},
|
|
75421
|
-
...
|
|
75422
|
-
refused,
|
|
75851
|
+
...refused2.length ? {
|
|
75852
|
+
refused: refused2,
|
|
75423
75853
|
refusedNote: "this archive carries paths a restore will not write (the deny-list has grown since it was published) \u2014 they were skipped, not written over"
|
|
75424
75854
|
} : {},
|
|
75425
75855
|
// EVERY path, not a count and not a sample: the whole failure was that a hybrid tree
|
|
@@ -75503,7 +75933,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75503
75933
|
collect: { lint: true, strict }
|
|
75504
75934
|
});
|
|
75505
75935
|
const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
|
|
75506
|
-
const { report:
|
|
75936
|
+
const { report: report3, hasErrors } = summarizeLint(declared, bres, outcome, strict, [
|
|
75507
75937
|
...await cmsIssues(dir, declared, strict, { local: true }),
|
|
75508
75938
|
...surfaceIssues(dir, outcome.surfaceNav, strict),
|
|
75509
75939
|
...tokenColorIssues(dir),
|
|
@@ -75513,7 +75943,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75513
75943
|
...await navIssues2(dir, strict)
|
|
75514
75944
|
]);
|
|
75515
75945
|
if (hasErrors && !force) {
|
|
75516
|
-
print({ ...
|
|
75946
|
+
print({ ...report3, pushed: false, hint: "lint found errors \u2014 fix them, or re-run `himi push --force` to publish anyway" });
|
|
75517
75947
|
return 1;
|
|
75518
75948
|
}
|
|
75519
75949
|
const declaredData = await loadDataResourcesQuietly(dir);
|
|
@@ -75612,7 +76042,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75612
76042
|
// Reaches here ONLY on the forced path — the refusal above returned otherwise — so
|
|
75613
76043
|
// this is the override's record, and the case it matters most for is a `failed` drive.
|
|
75614
76044
|
ux: { evidence: patchUxEvidence, ctx: patchUxContext },
|
|
75615
|
-
lint:
|
|
76045
|
+
lint: report3.counts ?? null,
|
|
75616
76046
|
extra: { selector: parsed.selectors.map((s) => "pattern" in s ? `${s.kind}:${s.pattern}` : s.kind) }
|
|
75617
76047
|
});
|
|
75618
76048
|
}
|
|
@@ -75832,6 +76262,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75832
76262
|
releaseId: release,
|
|
75833
76263
|
draft,
|
|
75834
76264
|
...unchanged ? { unchanged: true, unchangedNote: "the serve plane already holds this exact release under this id, so nothing was written" } : {},
|
|
76265
|
+
...afterDraft?.firstLook ? { firstLookPostcard: afterDraft.firstLook } : {},
|
|
75835
76266
|
...afterDraft?.postcard ? { postcard: afterDraft.postcard } : {},
|
|
75836
76267
|
// No picture on the draft postcard: why, and the render that makes a current one.
|
|
75837
76268
|
...afterDraft?.picture ? { postcardPicture: afterDraft.picture } : {},
|
|
@@ -75863,7 +76294,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75863
76294
|
// print a `himi pull` command that answers app_source_not_found later — the worst
|
|
75864
76295
|
// shape for a backup, which is one you are told you have.
|
|
75865
76296
|
...sourceSummary ? sourceStored === true ? { source: { ...sourceSummary, restore: `himi pull ${app} --release ${release}` } } : sourceStored === false ? { source: { ...sourceSummary, archived: false, note: "this serve target cannot store source archives, so this release cannot be restored with `himi pull`" } } : { source: { ...sourceSummary, archived: "unconfirmed", note: `this serve target did not report whether it stored the source (it may predate the field). Verify with \`himi pull ${app} --release ${release}\` before relying on it.` } } : { source: { archived: false, note: "pushed with --no-source \u2014 this release cannot be restored with `himi pull`" } },
|
|
75866
|
-
lint:
|
|
76297
|
+
lint: report3.counts,
|
|
75867
76298
|
// A push ships content, not backends. If the app declares CMS
|
|
75868
76299
|
// collections, say so and point at the verbs — but never mutate the
|
|
75869
76300
|
// site as a side effect of publishing a release.
|
|
@@ -75895,7 +76326,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75895
76326
|
nondeterministicWorkers: drifting
|
|
75896
76327
|
}),
|
|
75897
76328
|
...loopbackWarning ? { warning: loopbackWarning } : {},
|
|
75898
|
-
...
|
|
76329
|
+
...report3.issues ? { issues: report3.issues } : {},
|
|
75899
76330
|
previewWeb: base ? previewWebUrl(base, app, release, void 0, void 0, previewToken, previewHandover) : null,
|
|
75900
76331
|
// A timestamp alone leaves the reader to work out both how long they have and how to
|
|
75901
76332
|
// get another one. Print the duration and the re-mint command beside it (#2502 item 3).
|
|
@@ -75992,7 +76423,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
75992
76423
|
return 2;
|
|
75993
76424
|
}
|
|
75994
76425
|
try {
|
|
75995
|
-
const res = writePack(
|
|
76426
|
+
const res = writePack(resolve52(releaseDir), only, resolve52(str8(flags.out) ?? process.cwd()));
|
|
75996
76427
|
print({
|
|
75997
76428
|
ok: true,
|
|
75998
76429
|
app: res.app,
|
|
@@ -76048,7 +76479,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76048
76479
|
collect: { lint: true, strict }
|
|
76049
76480
|
});
|
|
76050
76481
|
const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
|
|
76051
|
-
const { report:
|
|
76482
|
+
const { report: report3, hasErrors } = summarizeLint(app, bres, outcome, strict, [
|
|
76052
76483
|
...await cmsIssues(dir, app, strict, { local: true }),
|
|
76053
76484
|
...surfaceIssues(dir, outcome.surfaceNav, strict),
|
|
76054
76485
|
...tokenColorIssues(dir),
|
|
@@ -76059,11 +76490,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76059
76490
|
]);
|
|
76060
76491
|
if (hasErrors && !force) {
|
|
76061
76492
|
rmSync15(built, { recursive: true, force: true });
|
|
76062
|
-
print({ ...
|
|
76493
|
+
print({ ...report3, packed: false, hint: "lint found errors \u2014 fix them, or re-run `himi pack --force` to pack anyway" });
|
|
76063
76494
|
return 1;
|
|
76064
76495
|
}
|
|
76065
76496
|
try {
|
|
76066
|
-
const res = writePack(built, app,
|
|
76497
|
+
const res = writePack(built, app, resolve52(str8(flags.out) ?? dir), sourceArchive);
|
|
76067
76498
|
print({
|
|
76068
76499
|
ok: true,
|
|
76069
76500
|
app: res.app,
|
|
@@ -76073,12 +76504,12 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76073
76504
|
entries: res.entryCount,
|
|
76074
76505
|
format,
|
|
76075
76506
|
launchMode,
|
|
76076
|
-
lint:
|
|
76507
|
+
lint: report3.counts,
|
|
76077
76508
|
contents: withSource ? "dev" : "release",
|
|
76078
76509
|
...sourceSummary ? { source: sourceSummary } : {},
|
|
76079
76510
|
...res.skipped.length ? { skipped: res.skipped } : {},
|
|
76080
76511
|
...hasErrors && force ? { forcedPastErrors: true } : {},
|
|
76081
|
-
...
|
|
76512
|
+
...report3.issues ? { issues: report3.issues } : {},
|
|
76082
76513
|
open: [
|
|
76083
76514
|
...withSource ? [`himi import ${res.out}`] : [],
|
|
76084
76515
|
`himi run ${res.out}`,
|
|
@@ -76108,7 +76539,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76108
76539
|
const { HIMI_PACKAGE_SOURCE_PATH: HIMI_PACKAGE_SOURCE_PATH2, isSafeAppId: isSafeAppId2, packageContents: packageContents2 } = await Promise.resolve().then(() => (init_himi_package(), himi_package_exports));
|
|
76109
76540
|
let opened;
|
|
76110
76541
|
try {
|
|
76111
|
-
opened = openPackageFile2(
|
|
76542
|
+
opened = openPackageFile2(resolve52(file));
|
|
76112
76543
|
} catch (e) {
|
|
76113
76544
|
io.error(JSON.stringify({ error: e.message }));
|
|
76114
76545
|
return 1;
|
|
@@ -76121,7 +76552,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76121
76552
|
io.error(JSON.stringify({
|
|
76122
76553
|
ok: false,
|
|
76123
76554
|
error: "app_source_not_in_package",
|
|
76124
|
-
detail: `${
|
|
76555
|
+
detail: `${resolve52(file)} is a release package: it carries built output (screens, bundles, tokens) and no source`,
|
|
76125
76556
|
hint: "re-pack it from the content package with `himi pack --dev`, or restore from the serve plane with `himi pull <app>`"
|
|
76126
76557
|
}));
|
|
76127
76558
|
return 1;
|
|
@@ -76144,7 +76575,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76144
76575
|
return 1;
|
|
76145
76576
|
}
|
|
76146
76577
|
}
|
|
76147
|
-
const dest =
|
|
76578
|
+
const dest = resolve52(str8(flags.out) ?? process.cwd(), renamed ?? opened.header.app);
|
|
76148
76579
|
const issue2 = restoreDestinationIssue(dest, flags.force === true);
|
|
76149
76580
|
if (issue2) {
|
|
76150
76581
|
io.error(JSON.stringify(issue2));
|
|
@@ -76183,7 +76614,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76183
76614
|
return 1;
|
|
76184
76615
|
}
|
|
76185
76616
|
const left = staleSourcePaths2(restored, dest);
|
|
76186
|
-
const
|
|
76617
|
+
const refused2 = deniedArchivePaths2(restored);
|
|
76187
76618
|
const written = writeAppSource2(restored, dest);
|
|
76188
76619
|
const agent = await seedAgentFilesFor(dest, flags, "restore");
|
|
76189
76620
|
print({
|
|
@@ -76194,8 +76625,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76194
76625
|
files: written.length,
|
|
76195
76626
|
...agent,
|
|
76196
76627
|
...renamed ? { renamedTo: renamed } : {},
|
|
76197
|
-
...
|
|
76198
|
-
refused,
|
|
76628
|
+
...refused2.length ? {
|
|
76629
|
+
refused: refused2,
|
|
76199
76630
|
refusedNote: "this package carries paths a restore will not write (the deny-list has grown since it was packed) \u2014 they were skipped, not written over"
|
|
76200
76631
|
} : {},
|
|
76201
76632
|
...left.length ? {
|
|
@@ -76231,7 +76662,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76231
76662
|
}
|
|
76232
76663
|
try {
|
|
76233
76664
|
const { openPackageFile: openPackageFile2 } = await Promise.resolve().then(() => (init_pack(), pack_exports));
|
|
76234
|
-
const opened = openPackageFile2(
|
|
76665
|
+
const opened = openPackageFile2(resolve52(file));
|
|
76235
76666
|
if (!opened.validation.ok || !opened.header) {
|
|
76236
76667
|
io.error(JSON.stringify({ error: "refusing to share an invalid package", issues: opened.validation.errors }));
|
|
76237
76668
|
return 1;
|
|
@@ -76241,12 +76672,12 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76241
76672
|
io.error(JSON.stringify({
|
|
76242
76673
|
ok: false,
|
|
76243
76674
|
error: "refusing to share a package that carries the app's source",
|
|
76244
|
-
detail: `${
|
|
76675
|
+
detail: `${resolve52(file)} was packed with --dev, and a share link is public and unauthenticated \u2014 anyone with the URL could \`himi import\` your codebase`,
|
|
76245
76676
|
hint: "share a release package instead (`himi pack` with no --dev), or hand the dev package over directly"
|
|
76246
76677
|
}));
|
|
76247
76678
|
return 1;
|
|
76248
76679
|
}
|
|
76249
|
-
const archive = readFileSync79(
|
|
76680
|
+
const archive = readFileSync79(resolve52(file));
|
|
76250
76681
|
const result = await client2().createPackageShare({
|
|
76251
76682
|
app: opened.header.app,
|
|
76252
76683
|
releaseId: opened.header.releaseId,
|
|
@@ -76297,6 +76728,21 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76297
76728
|
return 2;
|
|
76298
76729
|
}
|
|
76299
76730
|
}
|
|
76731
|
+
let idleExitMs;
|
|
76732
|
+
if (flags["idle-exit"] !== void 0) {
|
|
76733
|
+
const raw = str8(flags["idle-exit"]);
|
|
76734
|
+
const minutes = raw === void 0 ? Number.NaN : Number(raw);
|
|
76735
|
+
const IDLE_EXIT_MAX_MINUTES = Math.floor(2147483647 / 6e4);
|
|
76736
|
+
if (!watch2 || !Number.isFinite(minutes) || minutes <= 0 || minutes > IDLE_EXIT_MAX_MINUTES) {
|
|
76737
|
+
io.error(JSON.stringify({
|
|
76738
|
+
ok: false,
|
|
76739
|
+
error: watch2 ? "invalid_idle_exit" : "idle_exit_needs_watch",
|
|
76740
|
+
message: watch2 ? `--idle-exit needs a number of minutes above 0 and at most ${IDLE_EXIT_MAX_MINUTES}, got ${JSON.stringify(raw ?? "")}` : "--idle-exit stops a --watch preview that has seen no save for that long; add --watch, or drop --idle-exit"
|
|
76741
|
+
}));
|
|
76742
|
+
return 2;
|
|
76743
|
+
}
|
|
76744
|
+
idleExitMs = minutes * 6e4;
|
|
76745
|
+
}
|
|
76300
76746
|
let served = null;
|
|
76301
76747
|
let browserCopy = null;
|
|
76302
76748
|
let servedMissing = [];
|
|
@@ -76370,9 +76816,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76370
76816
|
});
|
|
76371
76817
|
await announce(run2);
|
|
76372
76818
|
print({ ok: true, app: run2.app, url: run2.url, port: run2.port, plane: "browser" });
|
|
76373
|
-
await new Promise((
|
|
76819
|
+
await new Promise((resolve53) => {
|
|
76374
76820
|
const stop = () => {
|
|
76375
|
-
void retract().catch((e) => io.error(JSON.stringify({ run: "stop_cleanup_failed", message: e.message }))).finally(() => run2.close().finally(() =>
|
|
76821
|
+
void retract().catch((e) => io.error(JSON.stringify({ run: "stop_cleanup_failed", message: e.message }))).finally(() => run2.close().finally(() => resolve53()));
|
|
76376
76822
|
};
|
|
76377
76823
|
process.once("SIGINT", stop);
|
|
76378
76824
|
process.once("SIGTERM", stop);
|
|
@@ -76399,6 +76845,13 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76399
76845
|
let watcher2;
|
|
76400
76846
|
let rebuild;
|
|
76401
76847
|
let stopping = false;
|
|
76848
|
+
let stopNow;
|
|
76849
|
+
let idleTimer;
|
|
76850
|
+
const armIdle = () => {
|
|
76851
|
+
if (idleExitMs === void 0) return;
|
|
76852
|
+
clearTimeout(idleTimer);
|
|
76853
|
+
idleTimer = setTimeout(() => stopNow?.("idle"), idleExitMs);
|
|
76854
|
+
};
|
|
76402
76855
|
try {
|
|
76403
76856
|
await announce(running);
|
|
76404
76857
|
if (watchKit && bus) {
|
|
@@ -76456,7 +76909,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76456
76909
|
rmSync15(next, { recursive: true, force: true });
|
|
76457
76910
|
}
|
|
76458
76911
|
});
|
|
76459
|
-
watcher2 = watchKit.watchContent(dir, () =>
|
|
76912
|
+
watcher2 = watchKit.watchContent(dir, () => {
|
|
76913
|
+
armIdle();
|
|
76914
|
+
rebuild?.();
|
|
76915
|
+
});
|
|
76460
76916
|
}
|
|
76461
76917
|
} catch (e) {
|
|
76462
76918
|
await retract().catch(() => {
|
|
@@ -76484,11 +76940,19 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76484
76940
|
...watch2 ? { watching: dir } : {},
|
|
76485
76941
|
...recordKit && boardOpen(dir) ? { board: `${running.origin}/__himi/studio/` } : {},
|
|
76486
76942
|
...running.warnings.length ? { warnings: running.warnings } : {},
|
|
76943
|
+
...idleExitMs !== void 0 ? { idleExit: { minutes: idleExitMs / 6e4, note: "stops itself after this long with no source save" } } : {},
|
|
76487
76944
|
stop: "Ctrl-C"
|
|
76488
76945
|
});
|
|
76489
76946
|
await new Promise((done) => {
|
|
76490
|
-
const stop = async () => {
|
|
76947
|
+
const stop = async (why) => {
|
|
76948
|
+
if (stopping) return;
|
|
76491
76949
|
stopping = true;
|
|
76950
|
+
clearTimeout(idleTimer);
|
|
76951
|
+
if (why === "idle") {
|
|
76952
|
+
const { shellQuote: shellQuote3 } = await Promise.resolve().then(() => (init_shell_quote(), shell_quote_exports));
|
|
76953
|
+
io.error(JSON.stringify({ watch: "idle_exit", minutes: idleExitMs / 6e4, message: `no source save for ${idleExitMs / 6e4} min, so this preview stopped \u2014 \`himi run --watch --content ${shellQuote3(dir)} &\` starts it again` }));
|
|
76954
|
+
}
|
|
76955
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) process.removeListener(sig, onSignal);
|
|
76492
76956
|
watcher2?.close();
|
|
76493
76957
|
try {
|
|
76494
76958
|
await rebuild?.stop();
|
|
@@ -76500,8 +76964,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
76500
76964
|
done();
|
|
76501
76965
|
}
|
|
76502
76966
|
};
|
|
76503
|
-
|
|
76504
|
-
|
|
76967
|
+
const onSignal = () => void stop();
|
|
76968
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) process.once(sig, onSignal);
|
|
76969
|
+
stopNow = (why) => void stop(why);
|
|
76970
|
+
armIdle();
|
|
76505
76971
|
});
|
|
76506
76972
|
return 0;
|
|
76507
76973
|
} catch (e) {
|
|
@@ -78392,13 +78858,13 @@ ${label2}`);
|
|
|
78392
78858
|
// advertisable for the target this report is about.
|
|
78393
78859
|
hasOwnerIssuer: (resolved2?.ownerIssue ?? "") !== ""
|
|
78394
78860
|
});
|
|
78395
|
-
const
|
|
78396
|
-
if (targetError)
|
|
78861
|
+
const report3 = buildDoctorReport({ target, version: cliVersion(), data, serve, auth, loginModes, dpxStatus, versions });
|
|
78862
|
+
if (targetError) report3.targetError = targetError;
|
|
78397
78863
|
const { agentContextStatus: agentContextStatus2 } = await Promise.resolve().then(() => (init_agent_context(), agent_context_exports));
|
|
78398
78864
|
const agentContext = agentContextStatus2(contentDir(flags), cliVersion());
|
|
78399
|
-
if (agentContext)
|
|
78865
|
+
if (agentContext) report3.agentContext = agentContext;
|
|
78400
78866
|
if (flags.json === true) {
|
|
78401
|
-
print(
|
|
78867
|
+
print(report3);
|
|
78402
78868
|
return 0;
|
|
78403
78869
|
}
|
|
78404
78870
|
const mark = (r) => r.reachable ? `reachable${r.status ? ` [${r.status}]` : ""}` : `UNREACHABLE${r.error ? ` (${r.error})` : ""}`;
|
|
@@ -78408,7 +78874,7 @@ ${label2}`);
|
|
|
78408
78874
|
io.log(` ${mark(data)}`);
|
|
78409
78875
|
io.log(` serve plane ${serveUrl || "(no serve url)"}`);
|
|
78410
78876
|
io.log(` ${mark(serve)}`);
|
|
78411
|
-
const serveCode =
|
|
78877
|
+
const serveCode = report3.servePlane.code;
|
|
78412
78878
|
if (serveCode) io.log(` ${serveCode}: ${serveUnreachableHint(target)}`);
|
|
78413
78879
|
io.log(` auth dpx session: ${auth.dpxSession ? "yes" : "no"} control token: ${auth.controlToken ? "yes" : controlHostRefused ? "REFUSED" : auth.controlTokenPresent ? "STALE" : "no"}`);
|
|
78414
78880
|
if (auth.controlTokenHint) io.log(` ${auth.controlTokenHint}`);
|
|
@@ -78431,7 +78897,7 @@ ${label2}`);
|
|
|
78431
78897
|
}
|
|
78432
78898
|
io.log(
|
|
78433
78899
|
`
|
|
78434
|
-
${
|
|
78900
|
+
${report3.ok ? "OK \u2014 data + serve plane reachable." : typeof report3.hint === "string" && report3.hint ? report3.hint : serve.reachable ? "Data plane unreachable \u2014 check basic connectivity." : serveUnreachableHint(target)}`
|
|
78435
78901
|
);
|
|
78436
78902
|
return 0;
|
|
78437
78903
|
}
|
|
@@ -78544,9 +79010,9 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
|
|
|
78544
79010
|
}
|
|
78545
79011
|
const scope = { app, siteId: site, environment: environment2 };
|
|
78546
79012
|
try {
|
|
78547
|
-
const
|
|
78548
|
-
print(
|
|
78549
|
-
return
|
|
79013
|
+
const report3 = sub === "plan" ? await planData({ scope, data, kv, call }) : sub === "create" ? await createData({ scope, data, kv, call, dryRun: flags["dry-run"] === true }) : await seedData({ scope, data, kv, call, dir, dryRun: flags["dry-run"] === true, force: flags.force === true });
|
|
79014
|
+
print(report3);
|
|
79015
|
+
return report3.ok ? 0 : 1;
|
|
78550
79016
|
} catch (error) {
|
|
78551
79017
|
io.error(JSON.stringify({ error: error.message, app, site, environment: environment2, hint: "run `himi data plan` to re-read actual state before retrying" }));
|
|
78552
79018
|
return 1;
|
|
@@ -78561,6 +79027,14 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
|
|
|
78561
79027
|
const { runNotifySetup: runNotifySetup2 } = await Promise.resolve().then(() => (init_notify_setup(), notify_setup_exports));
|
|
78562
79028
|
return runNotifySetup2(pos, flags, io);
|
|
78563
79029
|
}
|
|
79030
|
+
if (pos[1] === "send" && flags.provider !== void 0) {
|
|
79031
|
+
if (flags.provider !== "ping") {
|
|
79032
|
+
io.error(JSON.stringify({ ok: false, error: "unknown_provider", hint: "The hosted sender is --provider ping." }));
|
|
79033
|
+
return 2;
|
|
79034
|
+
}
|
|
79035
|
+
const { runNotifyPing: runNotifyPing2 } = await Promise.resolve().then(() => (init_notify_ping(), notify_ping_exports));
|
|
79036
|
+
return runNotifyPing2(flags, io);
|
|
79037
|
+
}
|
|
78564
79038
|
const { runNotify: runNotify2 } = await Promise.resolve().then(() => (init_notify(), notify_exports));
|
|
78565
79039
|
return await runNotify2(pos, flags, io);
|
|
78566
79040
|
case "cms": {
|
|
@@ -78619,9 +79093,9 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
|
|
|
78619
79093
|
return 2;
|
|
78620
79094
|
}
|
|
78621
79095
|
const common = { app, cms, call };
|
|
78622
|
-
let
|
|
79096
|
+
let report3;
|
|
78623
79097
|
try {
|
|
78624
|
-
|
|
79098
|
+
report3 = sub === "query" ? await queryCms({ ...common, all: flags.all === true }) : sub === "create" ? await createCms({ ...common, dryRun: flags["dry-run"] === true, allowDestructive: flags["allow-destructive"] === true, applyPermissions: flags["apply-permissions"] === true }) : await seedCms({ ...common, dir, dryRun: flags["dry-run"] === true, force: flags.force === true, only: str8(flags.only) });
|
|
78625
79099
|
} catch (e) {
|
|
78626
79100
|
const partial = e.partial;
|
|
78627
79101
|
io.error(JSON.stringify({
|
|
@@ -78633,8 +79107,8 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
|
|
|
78633
79107
|
}));
|
|
78634
79108
|
return 1;
|
|
78635
79109
|
}
|
|
78636
|
-
print({ ...
|
|
78637
|
-
return
|
|
79110
|
+
print({ ...report3, site });
|
|
79111
|
+
return report3.ok ? 0 : 1;
|
|
78638
79112
|
}
|
|
78639
79113
|
case "site": {
|
|
78640
79114
|
const sub = pos[1];
|