@wix/himalaya-cli 0.872.0 → 0.873.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 +331 -71
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -5241,7 +5241,6 @@ function resolveTranslation(value, catalog, sourceCatalog) {
|
|
|
5241
5241
|
var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
|
|
5242
5242
|
var init_bindings = __esm({
|
|
5243
5243
|
"../../core/ts/src/bindings.ts"() {
|
|
5244
|
-
"use strict";
|
|
5245
5244
|
BINDING_SIGILS = ["service", "flag", "item", "t"];
|
|
5246
5245
|
BINDING_SIGIL_RE = new RegExp(
|
|
5247
5246
|
`^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
|
|
@@ -37657,17 +37656,204 @@ var init_kits = __esm({
|
|
|
37657
37656
|
// ../../core/dev-server/src/widgets.ts
|
|
37658
37657
|
var widgets_exports = {};
|
|
37659
37658
|
__export(widgets_exports, {
|
|
37659
|
+
WIDGET_BUDGET_MS: () => WIDGET_BUDGET_MS,
|
|
37660
37660
|
buildViewSnapshot: () => buildViewSnapshot,
|
|
37661
37661
|
buildWidgetSnapshot: () => buildWidgetSnapshot,
|
|
37662
37662
|
describeThrown: () => describeThrown,
|
|
37663
37663
|
extractWidgets: () => extractWidgets,
|
|
37664
37664
|
freezeOverrideSnapshot: () => freezeOverrideSnapshot,
|
|
37665
37665
|
frozenViewManifestEntries: () => frozenViewManifestEntries,
|
|
37666
|
-
|
|
37666
|
+
resetWidgetRealm: () => resetWidgetRealm,
|
|
37667
|
+
viewWidgetId: () => viewWidgetId,
|
|
37668
|
+
widgetRealm: () => widgetRealm
|
|
37667
37669
|
});
|
|
37668
37670
|
import { createHash as createHash14 } from "node:crypto";
|
|
37669
37671
|
import { inspect } from "node:util";
|
|
37670
|
-
|
|
37672
|
+
async function widgetRealm() {
|
|
37673
|
+
if (!realmPromise) {
|
|
37674
|
+
const started = (async () => {
|
|
37675
|
+
const { createQuickJsEvaluator: createQuickJsEvaluator2 } = await Promise.resolve().then(() => (init_quickjs_evaluator(), quickjs_evaluator_exports));
|
|
37676
|
+
return createQuickJsEvaluator2({ intl: true });
|
|
37677
|
+
})();
|
|
37678
|
+
realmPromise = started;
|
|
37679
|
+
started.catch(() => {
|
|
37680
|
+
if (realmPromise === started) realmPromise = null;
|
|
37681
|
+
});
|
|
37682
|
+
}
|
|
37683
|
+
try {
|
|
37684
|
+
return await realmPromise;
|
|
37685
|
+
} catch (err) {
|
|
37686
|
+
throw new Error(
|
|
37687
|
+
"the widget producer's isolated realm (QuickJS-in-WASM) could not be loaded, so widget bundles would have been evaluated in node:vm (which is not a security boundary): " + (err instanceof Error ? err.message : String(err))
|
|
37688
|
+
);
|
|
37689
|
+
}
|
|
37690
|
+
}
|
|
37691
|
+
function resetWidgetRealm() {
|
|
37692
|
+
realmPromise = null;
|
|
37693
|
+
}
|
|
37694
|
+
function viewRenderModes() {
|
|
37695
|
+
const out = {};
|
|
37696
|
+
for (const target of [...SURFACE_GROUPS, ...PREVIEW_SURFACES.map((s) => s.id)]) {
|
|
37697
|
+
const mode = viewTargetRenderMode(target);
|
|
37698
|
+
if (mode) out[target] = mode;
|
|
37699
|
+
}
|
|
37700
|
+
return out;
|
|
37701
|
+
}
|
|
37702
|
+
function widgetProgram(bundleJs, clock) {
|
|
37703
|
+
const slot = JSON.stringify(CAPTURE_SLOT);
|
|
37704
|
+
const readbackName = JSON.stringify(READBACK_SERVICE);
|
|
37705
|
+
const modes = JSON.stringify(JSON.stringify(viewRenderModes()));
|
|
37706
|
+
const pinClock = clock === void 0 ? "" : ` globalThis.Date.now = function () { return ${Math.trunc(clock)}; };
|
|
37707
|
+
`;
|
|
37708
|
+
return `
|
|
37709
|
+
(function () {
|
|
37710
|
+
${pinClock}
|
|
37711
|
+
// Captured BEFORE any bundle code runs, for the reason quickjs-evaluator.ts's readback
|
|
37712
|
+
// channel documents at length: anything the emit path looks up AFTER untrusted code has run
|
|
37713
|
+
// is reachable BY it, and every instance of that bug presented as registrations silently
|
|
37714
|
+
// vanishing rather than as an error.
|
|
37715
|
+
var S = {
|
|
37716
|
+
stringify: JSON.stringify,
|
|
37717
|
+
str: String,
|
|
37718
|
+
modes: JSON.parse(${modes}),
|
|
37719
|
+
services: Object.create(null),
|
|
37720
|
+
widgets: [],
|
|
37721
|
+
views: [],
|
|
37722
|
+
};
|
|
37723
|
+
Object.defineProperty(globalThis, ${slot}, {
|
|
37724
|
+
value: S, writable: false, enumerable: false, configurable: false,
|
|
37725
|
+
});
|
|
37726
|
+
|
|
37727
|
+
var host = globalThis.himalaya;
|
|
37728
|
+
var hostDefineService = host.defineService;
|
|
37729
|
+
var defineService = function (name, def) {
|
|
37730
|
+
// Mirrored locally so render() is called with the same service-state map the host used to
|
|
37731
|
+
// hand it, and FORWARDED so the realm's own readback still reports the service.
|
|
37732
|
+
var key;
|
|
37733
|
+
try { key = S.str(name); } catch (e) { key = "<unnameable service>"; }
|
|
37734
|
+
// Explicit undefined/null test, not a truthiness fallback: a service whose declared state
|
|
37735
|
+
// is 0, false or the empty string is valid JSON-serializable state, and "|| {}" would hand
|
|
37736
|
+
// render() an empty object instead of it. No nullish coalescing here on purpose - this
|
|
37737
|
+
// text is inside the realm PROGRAM string and the sandbox JS targets ES6, so ES2020 syntax
|
|
37738
|
+
// would raise the parse floor for every engine that evaluates it. (codebot on #2652.)
|
|
37739
|
+
// NO BACKTICKS IN THIS COMMENT EITHER: it lives inside a template literal, so one would
|
|
37740
|
+
// end the program string. That is how this comment broke the build the first time.
|
|
37741
|
+
S.services[key] = def && def.state !== undefined && def.state !== null ? def.state : {};
|
|
37742
|
+
return hostDefineService(name, def);
|
|
37743
|
+
};
|
|
37744
|
+
var defineWidget = function (id, def) { S.widgets.push([id, def]); };
|
|
37745
|
+
var defineView = function (id, def) { S.views.push([id, def]); };
|
|
37746
|
+
|
|
37747
|
+
// A Proxy OVER the realm's own \`himalaya\`, not a replacement: \`input\`, the capability
|
|
37748
|
+
// chaining and the inert fall-through all still come from the realm's own sandbox.
|
|
37749
|
+
globalThis.himalaya = new Proxy(host, {
|
|
37750
|
+
get: function (t, p) {
|
|
37751
|
+
if (p === "defineService") return defineService;
|
|
37752
|
+
if (p === "defineWidget") return defineWidget;
|
|
37753
|
+
if (p === "defineView") return defineView;
|
|
37754
|
+
return t[p];
|
|
37755
|
+
},
|
|
37756
|
+
});
|
|
37757
|
+
})();
|
|
37758
|
+
${bundleJs}
|
|
37759
|
+
;(function () {
|
|
37760
|
+
var S = globalThis[${slot}];
|
|
37761
|
+
var stringify = S.stringify, str = S.str;
|
|
37762
|
+
|
|
37763
|
+
function why(e) {
|
|
37764
|
+
try { return (e && e.message) ? str(e.message) : str(e); } catch (x) { return "unprintable error"; }
|
|
37765
|
+
}
|
|
37766
|
+
/** A field the bundle owns, reduced to something that cannot break the envelope. */
|
|
37767
|
+
function scalar(v) { return typeof v === "string" ? v : null; }
|
|
37768
|
+
/** Did the registration DECLARE this field? Guarded: a bundle can define a throwing
|
|
37769
|
+
* getter, and a gate that crashes on hostile input is a gate that fails open. */
|
|
37770
|
+
function declared(o, k) { try { return o[k] !== undefined; } catch (e) { return true; } }
|
|
37771
|
+
function json(v) {
|
|
37772
|
+
try { return stringify(v === undefined ? null : v); } catch (e) { return null; }
|
|
37773
|
+
}
|
|
37774
|
+
/** Run one render/timeline callback and serialize its result, isolating both failures. */
|
|
37775
|
+
function call(fn, arg) {
|
|
37776
|
+
var out = { json: undefined, error: undefined, absent: false };
|
|
37777
|
+
if (typeof fn !== "function") { out.absent = true; return out; }
|
|
37778
|
+
var v;
|
|
37779
|
+
try { v = fn(arg); } catch (e) { out.error = why(e); return out; }
|
|
37780
|
+
var s = json(v);
|
|
37781
|
+
if (s === null) {
|
|
37782
|
+
out.error = "the value it returned is not representable as JSON, so it could not be frozen";
|
|
37783
|
+
} else {
|
|
37784
|
+
out.json = s;
|
|
37785
|
+
}
|
|
37786
|
+
return out;
|
|
37787
|
+
}
|
|
37788
|
+
|
|
37789
|
+
function entry(id, def, renderArg, wantTimeline) {
|
|
37790
|
+
// Every field below is a string, boolean or null that THIS code produced, on a
|
|
37791
|
+
// null-prototype object \u2014 so serializing the entry cannot reach a bundle's \`toJSON\`,
|
|
37792
|
+
// cannot throw, and cannot be blanked by poisoning \`Object.prototype\`. The bundle's own
|
|
37793
|
+
// values ride inside the pre-serialized \`payload\`/\`frames\` strings, where a failure costs
|
|
37794
|
+
// that one registration and nothing else.
|
|
37795
|
+
var e = Object.create(null);
|
|
37796
|
+
try { e.id = str(id); } catch (x) { e.id = null; }
|
|
37797
|
+
e.surface = scalar(def.surface);
|
|
37798
|
+
e.family = scalar(def.family);
|
|
37799
|
+
e.size = scalar(def.size);
|
|
37800
|
+
e.deepLink = scalar(def.deepLink);
|
|
37801
|
+
e.refresh = json(def.refresh);
|
|
37802
|
+
e.hasTimeline = typeof def.timeline === "function";
|
|
37803
|
+
// WAS THE FIELD DECLARED AT ALL, separate from what it normalised to. The frozen-only
|
|
37804
|
+
// check on an interactive surface used to reject any field that was not undefined; deriving
|
|
37805
|
+
// it from the normalised value silently narrowed that, because scalar() turns a non-string
|
|
37806
|
+
// into null, json() turns undefined into "null", and hasTimeline only counts a FUNCTION.
|
|
37807
|
+
// So size: 123, refresh: null and timeline: {} all became "not declared" and passed a gate
|
|
37808
|
+
// whose whole job is to catch a widget aimed at the wrong target. (Both reviewers, #2652.)
|
|
37809
|
+
// Booleans only, produced by this code, so the entry stays safe to serialize.
|
|
37810
|
+
e.declSize = declared(def, "size");
|
|
37811
|
+
e.declDeepLink = declared(def, "deepLink");
|
|
37812
|
+
e.declRefresh = declared(def, "refresh");
|
|
37813
|
+
e.declTimeline = declared(def, "timeline");
|
|
37814
|
+
var r = renderArg === null ? { json: undefined, error: undefined, absent: true } : call(def.render, renderArg);
|
|
37815
|
+
e.payload = r.json === undefined ? null : r.json;
|
|
37816
|
+
e.renderError = r.error === undefined ? null : r.error;
|
|
37817
|
+
e.renderAbsent = r.absent === true;
|
|
37818
|
+
e.frames = null;
|
|
37819
|
+
e.timelineError = null;
|
|
37820
|
+
if (wantTimeline && e.hasTimeline) {
|
|
37821
|
+
var t = call(def.timeline, renderArg);
|
|
37822
|
+
e.frames = t.json === undefined ? null : t.json;
|
|
37823
|
+
e.timelineError = t.error === undefined ? null : t.error;
|
|
37824
|
+
}
|
|
37825
|
+
return e;
|
|
37826
|
+
}
|
|
37827
|
+
|
|
37828
|
+
// Hand-concatenated rather than \`[\u2026].join(",")\`: a bundle can poison
|
|
37829
|
+
// \`Array.prototype.join\`, and an envelope that a bundle can empty is the silent-drop
|
|
37830
|
+
// failure this whole channel exists to avoid.
|
|
37831
|
+
var widgetBody = "";
|
|
37832
|
+
for (var i = 0; i < S.widgets.length; i++) {
|
|
37833
|
+
var w = S.widgets[i];
|
|
37834
|
+
widgetBody += (widgetBody ? "," : "") + stringify(entry(w[0], w[1] || {}, S.services, true));
|
|
37835
|
+
}
|
|
37836
|
+
var viewBody = "";
|
|
37837
|
+
for (var j = 0; j < S.views.length; j++) {
|
|
37838
|
+
var v = S.views[j], vdef = v[1] || {};
|
|
37839
|
+
var target = scalar(vdef.surface);
|
|
37840
|
+
var mode = target === null ? undefined : S.modes[target];
|
|
37841
|
+
// An unknown target renders NOTHING \u2014 the host rejects it by name below, and running a
|
|
37842
|
+
// typo'd registration's code is work spent on a bundle that is already failing.
|
|
37843
|
+
var arg = mode === "interactive" ? {} : mode === "frozen" ? S.services : null;
|
|
37844
|
+
var e2 = entry(v[0], vdef, arg, mode === "frozen");
|
|
37845
|
+
e2.mode = mode === undefined ? null : mode;
|
|
37846
|
+
viewBody += (viewBody ? "," : "") + stringify(e2);
|
|
37847
|
+
}
|
|
37848
|
+
|
|
37849
|
+
// Home through the realm's own service channel \u2014 see READBACK_SERVICE. A STRING, so the
|
|
37850
|
+
// envelope survives a poisoned \`Object.prototype.toJSON\`.
|
|
37851
|
+
globalThis.himalaya.defineService(${readbackName}, {
|
|
37852
|
+
state: '{"widgets":[' + widgetBody + '],"views":[' + viewBody + ']}',
|
|
37853
|
+
});
|
|
37854
|
+
})();
|
|
37855
|
+
`;
|
|
37856
|
+
}
|
|
37671
37857
|
function makeInert2() {
|
|
37672
37858
|
const fn = () => inert;
|
|
37673
37859
|
const inert = new Proxy(fn, {
|
|
@@ -37677,56 +37863,58 @@ function makeInert2() {
|
|
|
37677
37863
|
});
|
|
37678
37864
|
return inert;
|
|
37679
37865
|
}
|
|
37680
|
-
function
|
|
37866
|
+
function parseTree(raw) {
|
|
37867
|
+
if (raw.renderError) return { error: raw.renderError };
|
|
37868
|
+
if (raw.renderAbsent) return { error: "def.render is not a function" };
|
|
37869
|
+
if (raw.payload === null) return { error: "render() produced nothing" };
|
|
37870
|
+
try {
|
|
37871
|
+
return { payload: JSON.parse(raw.payload) };
|
|
37872
|
+
} catch {
|
|
37873
|
+
return { error: "the frozen tree did not survive the realm boundary as JSON" };
|
|
37874
|
+
}
|
|
37875
|
+
}
|
|
37876
|
+
function parseTimeline(raw) {
|
|
37877
|
+
if (!raw.hasTimeline) return void 0;
|
|
37878
|
+
if (raw.timelineError) return { error: raw.timelineError };
|
|
37879
|
+
if (raw.frames === null) return { error: "timeline() produced nothing" };
|
|
37880
|
+
try {
|
|
37881
|
+
return { frames: JSON.parse(raw.frames) };
|
|
37882
|
+
} catch {
|
|
37883
|
+
return { error: "the timeline did not survive the realm boundary as JSON" };
|
|
37884
|
+
}
|
|
37885
|
+
}
|
|
37886
|
+
function parseJsonField(value) {
|
|
37887
|
+
if (value === null) return void 0;
|
|
37888
|
+
try {
|
|
37889
|
+
const parsed = JSON.parse(value);
|
|
37890
|
+
return parsed === null ? void 0 : parsed;
|
|
37891
|
+
} catch {
|
|
37892
|
+
return void 0;
|
|
37893
|
+
}
|
|
37894
|
+
}
|
|
37895
|
+
async function extractWidgets(bundleJs, opts = {}) {
|
|
37681
37896
|
const services = {};
|
|
37682
|
-
|
|
37683
|
-
const views = {};
|
|
37897
|
+
let envelope;
|
|
37684
37898
|
const inert = makeInert2();
|
|
37685
37899
|
const himalayaTarget = {
|
|
37900
|
+
input: {},
|
|
37686
37901
|
defineService: (name, def) => {
|
|
37687
|
-
|
|
37902
|
+
const key2 = String(name);
|
|
37903
|
+
if (key2 === READBACK_SERVICE) {
|
|
37904
|
+
envelope = typeof def?.state === "string" ? def.state : void 0;
|
|
37905
|
+
return;
|
|
37906
|
+
}
|
|
37907
|
+
services[key2] = def?.state ?? {};
|
|
37688
37908
|
return new Proxy(
|
|
37689
37909
|
{ getState: () => def?.state },
|
|
37690
|
-
{
|
|
37691
|
-
|
|
37692
|
-
},
|
|
37693
|
-
defineWidget: (id, def) => {
|
|
37694
|
-
widgets[String(id)] = def;
|
|
37695
|
-
},
|
|
37696
|
-
// RFC 0051. Rejected HERE rather than at resolve time: an unknown target is
|
|
37697
|
-
// almost always a typo (`"watchos"` for `"watch"`, `"ios_lock"` without the
|
|
37698
|
-
// `widget:` prefix), and resolving it later would present as the surface
|
|
37699
|
-
// silently falling back to `default` — a wrong screen instead of an error.
|
|
37700
|
-
defineView: (id, def) => {
|
|
37701
|
-
const target = String(def?.surface ?? "");
|
|
37702
|
-
if (!isViewTarget(target)) {
|
|
37703
|
-
const ids = PREVIEW_SURFACES.map((s) => s.id).join(", ");
|
|
37704
|
-
throw new Error(
|
|
37705
|
-
`defineView("${id}") targets unknown surface "${target}". Use a surface id (${ids}) or a group (${SURFACE_GROUPS.join(", ")}).`
|
|
37706
|
-
);
|
|
37707
|
-
}
|
|
37708
|
-
if (viewTargetRenderMode(target) === "interactive") {
|
|
37709
|
-
const frozenOnly = ["size", "deepLink", "refresh", "timeline"].filter(
|
|
37710
|
-
(k) => def?.[k] !== void 0
|
|
37711
|
-
);
|
|
37712
|
-
if (frozenOnly.length) {
|
|
37713
|
-
throw new Error(
|
|
37714
|
-
`defineView("${id}") targets the interactive surface "${target}" but sets frozen-only field(s): ${frozenOnly.join(", ")}. Those belong on a widget:<family> or "frozen" registration.`
|
|
37715
|
-
);
|
|
37910
|
+
{
|
|
37911
|
+
get: (t, k) => t[k] ?? (() => Promise.resolve(def?.state))
|
|
37716
37912
|
}
|
|
37717
|
-
|
|
37718
|
-
const bucket2 = views[String(id)] ??= {};
|
|
37719
|
-
if (bucket2[target]) {
|
|
37720
|
-
throw new Error(`defineView("${id}") registers surface "${target}" twice`);
|
|
37721
|
-
}
|
|
37722
|
-
bucket2[target] = def;
|
|
37913
|
+
);
|
|
37723
37914
|
}
|
|
37724
37915
|
};
|
|
37725
|
-
const himalaya = new Proxy(himalayaTarget, {
|
|
37726
|
-
get: (t, prop) => prop in t ? t[prop] : inert
|
|
37727
|
-
});
|
|
37728
37916
|
const sandbox = {
|
|
37729
|
-
himalaya,
|
|
37917
|
+
himalaya: new Proxy(himalayaTarget, { get: (t, prop) => prop in t ? t[prop] : inert }),
|
|
37730
37918
|
globalThis: void 0,
|
|
37731
37919
|
console: { log() {
|
|
37732
37920
|
}, warn() {
|
|
@@ -37747,7 +37935,78 @@ function extractWidgets(bundleJs) {
|
|
|
37747
37935
|
}
|
|
37748
37936
|
};
|
|
37749
37937
|
sandbox.globalThis = sandbox;
|
|
37750
|
-
|
|
37938
|
+
const evaluator = opts.evaluator ?? await widgetRealm();
|
|
37939
|
+
evaluator.run(widgetProgram(bundleJs, opts.clock), sandbox, {
|
|
37940
|
+
timeoutMs: opts.timeoutMs ?? WIDGET_BUDGET_MS,
|
|
37941
|
+
filename: "widget-bundle.js",
|
|
37942
|
+
captureConsole: false
|
|
37943
|
+
});
|
|
37944
|
+
if (envelope === void 0) {
|
|
37945
|
+
throw new Error("the widget producer's realm returned no registrations envelope");
|
|
37946
|
+
}
|
|
37947
|
+
let parsed;
|
|
37948
|
+
try {
|
|
37949
|
+
parsed = JSON.parse(envelope);
|
|
37950
|
+
} catch (err) {
|
|
37951
|
+
throw new Error(`the widget producer's realm returned a malformed envelope: ${describeThrown(err)}`);
|
|
37952
|
+
}
|
|
37953
|
+
const widgets = {};
|
|
37954
|
+
for (const raw of parsed.widgets ?? []) {
|
|
37955
|
+
widgets[String(raw.id)] = {
|
|
37956
|
+
family: raw.family,
|
|
37957
|
+
size: raw.size,
|
|
37958
|
+
deepLink: raw.deepLink,
|
|
37959
|
+
refresh: parseJsonField(raw.refresh),
|
|
37960
|
+
rendered: parseTree(raw),
|
|
37961
|
+
timeline: parseTimeline(raw)
|
|
37962
|
+
};
|
|
37963
|
+
}
|
|
37964
|
+
const views = {};
|
|
37965
|
+
for (const raw of parsed.views ?? []) {
|
|
37966
|
+
const id = String(raw.id);
|
|
37967
|
+
const target = raw.surface ?? "";
|
|
37968
|
+
if (!isViewTarget(target)) {
|
|
37969
|
+
const ids = PREVIEW_SURFACES.map((s) => s.id).join(", ");
|
|
37970
|
+
throw new Error(
|
|
37971
|
+
`defineView("${id}") targets unknown surface "${target}". Use a surface id (${ids}) or a group (${SURFACE_GROUPS.join(", ")}).`
|
|
37972
|
+
);
|
|
37973
|
+
}
|
|
37974
|
+
const size = raw.size ?? void 0;
|
|
37975
|
+
const deepLink = raw.deepLink ?? void 0;
|
|
37976
|
+
const refresh = parseJsonField(raw.refresh);
|
|
37977
|
+
if (viewTargetRenderMode(target) === "interactive") {
|
|
37978
|
+
const frozenOnly = [
|
|
37979
|
+
// The DECLARATION bits, not the normalised values — see `declared()` in the realm
|
|
37980
|
+
// program. `raw.hasTimeline` still means "is a function"; `declTimeline` means "the
|
|
37981
|
+
// author wrote a timeline field", which is what makes a wrong-target registration
|
|
37982
|
+
// loud instead of silently accepted.
|
|
37983
|
+
["size", raw.declSize === true || size !== void 0],
|
|
37984
|
+
["deepLink", raw.declDeepLink === true || deepLink !== void 0],
|
|
37985
|
+
["refresh", raw.declRefresh === true || refresh !== void 0],
|
|
37986
|
+
["timeline", raw.declTimeline === true || raw.hasTimeline]
|
|
37987
|
+
].filter(([, declared]) => declared).map(([k]) => k);
|
|
37988
|
+
if (frozenOnly.length) {
|
|
37989
|
+
throw new Error(
|
|
37990
|
+
`defineView("${id}") targets the interactive surface "${target}" but sets frozen-only field(s): ${frozenOnly.join(", ")}. Those belong on a widget:<family> or "frozen" registration.`
|
|
37991
|
+
);
|
|
37992
|
+
}
|
|
37993
|
+
}
|
|
37994
|
+
const bucket2 = views[id] ??= {};
|
|
37995
|
+
if (bucket2[target]) {
|
|
37996
|
+
throw new Error(`defineView("${id}") registers surface "${target}" twice`);
|
|
37997
|
+
}
|
|
37998
|
+
const tree = parseTree(raw);
|
|
37999
|
+
bucket2[target] = {
|
|
38000
|
+
surface: target,
|
|
38001
|
+
size,
|
|
38002
|
+
deepLink,
|
|
38003
|
+
refresh,
|
|
38004
|
+
rendered: raw.mode === "frozen" ? tree : {},
|
|
38005
|
+
interactive: raw.mode === "interactive" ? tree : {},
|
|
38006
|
+
timeline: parseTimeline(raw),
|
|
38007
|
+
hasTimeline: raw.hasTimeline
|
|
38008
|
+
};
|
|
38009
|
+
}
|
|
37751
38010
|
return { services, widgets, views };
|
|
37752
38011
|
}
|
|
37753
38012
|
function contentEtag(snapshot2) {
|
|
@@ -37762,30 +38021,26 @@ function contentEtag(snapshot2) {
|
|
|
37762
38021
|
});
|
|
37763
38022
|
return "sha256-" + createHash14("sha256").update(canonical3).digest("base64");
|
|
37764
38023
|
}
|
|
37765
|
-
function buildWidgetSnapshot(bundleJs, widgetId, opts = {}) {
|
|
37766
|
-
const {
|
|
38024
|
+
async function buildWidgetSnapshot(bundleJs, widgetId, opts = {}) {
|
|
38025
|
+
const { widgets } = await extractWidgets(bundleJs, opts);
|
|
37767
38026
|
const def = widgets[widgetId];
|
|
37768
38027
|
if (!def) {
|
|
37769
38028
|
const known = Object.keys(widgets).join(", ") || "none";
|
|
37770
38029
|
throw new Error(`widget "${widgetId}" is not defined by the bundle (defines: ${known})`);
|
|
37771
38030
|
}
|
|
37772
|
-
return freezeCaptured(def,
|
|
38031
|
+
return freezeCaptured(def, widgetId, opts);
|
|
37773
38032
|
}
|
|
37774
|
-
function freezeCaptured(def,
|
|
37775
|
-
|
|
37776
|
-
|
|
37777
|
-
payload = def.render(services);
|
|
37778
|
-
} catch (err) {
|
|
37779
|
-
throw new Error(`widget "${widgetId}" render() threw: ${err.message}`);
|
|
38033
|
+
function freezeCaptured(def, widgetId, opts = {}) {
|
|
38034
|
+
if (def.rendered.error !== void 0 || def.rendered.payload === void 0) {
|
|
38035
|
+
throw new Error(`widget "${widgetId}" render() threw: ${def.rendered.error ?? "it produced no tree"}`);
|
|
37780
38036
|
}
|
|
38037
|
+
let payload = def.rendered.payload;
|
|
37781
38038
|
let frames;
|
|
37782
38039
|
if (def.timeline) {
|
|
37783
|
-
|
|
37784
|
-
|
|
37785
|
-
raw = def.timeline(services);
|
|
37786
|
-
} catch (err) {
|
|
37787
|
-
throw new Error(`widget "${widgetId}" timeline() threw: ${err.message}`);
|
|
38040
|
+
if (def.timeline.error !== void 0) {
|
|
38041
|
+
throw new Error(`widget "${widgetId}" timeline() threw: ${def.timeline.error}`);
|
|
37788
38042
|
}
|
|
38043
|
+
const raw = def.timeline.frames;
|
|
37789
38044
|
if (Array.isArray(raw) && raw.length) {
|
|
37790
38045
|
const sorted = [...raw].filter((f) => f && typeof f.startsAt === "number" && f.component).sort((a, b) => a.startsAt - b.startsAt).slice(0, MAX_TIMELINE_FRAMES);
|
|
37791
38046
|
if (sorted.length) {
|
|
@@ -37874,7 +38129,7 @@ function frozenViewManifestEntries(views, emitted = /* @__PURE__ */ new Set(), o
|
|
|
37874
38129
|
}
|
|
37875
38130
|
return out;
|
|
37876
38131
|
}
|
|
37877
|
-
function buildViewSnapshot(bundleJs, viewId, surfaceId, opts = {}) {
|
|
38132
|
+
async function buildViewSnapshot(bundleJs, viewId, surfaceId, opts = {}) {
|
|
37878
38133
|
const surface = surfaceById(surfaceId);
|
|
37879
38134
|
if (!surface) throw new Error(`unknown surface "${surfaceId}"`);
|
|
37880
38135
|
if (surfaceRenderMode(surface) !== "frozen") {
|
|
@@ -37886,7 +38141,7 @@ function buildViewSnapshot(bundleJs, viewId, surfaceId, opts = {}) {
|
|
|
37886
38141
|
if (!family) {
|
|
37887
38142
|
throw new Error(`frozen surface "${surfaceId}" has no WidgetSnapshot family to ship under`);
|
|
37888
38143
|
}
|
|
37889
|
-
const {
|
|
38144
|
+
const { views } = await extractWidgets(bundleJs, opts);
|
|
37890
38145
|
const bucket2 = views[viewId];
|
|
37891
38146
|
if (!bucket2) return void 0;
|
|
37892
38147
|
const key2 = resolveSurfaceViewKey(surfaceId, Object.keys(bucket2));
|
|
@@ -37906,10 +38161,11 @@ function buildViewSnapshot(bundleJs, viewId, surfaceId, opts = {}) {
|
|
|
37906
38161
|
size: def.size ?? (isProjection ? PROJECTION_SNAPSHOT_SIZE : void 0),
|
|
37907
38162
|
deepLink: def.deepLink,
|
|
37908
38163
|
refresh: def.refresh,
|
|
37909
|
-
|
|
38164
|
+
// The frozen tree the realm produced for THIS registration — rendered over the captured
|
|
38165
|
+
// service state, because the target resolved to a frozen mode.
|
|
38166
|
+
rendered: def.rendered,
|
|
37910
38167
|
...def.timeline ? { timeline: def.timeline } : {}
|
|
37911
38168
|
},
|
|
37912
|
-
services,
|
|
37913
38169
|
viewWidgetId(viewId, family),
|
|
37914
38170
|
opts
|
|
37915
38171
|
);
|
|
@@ -37952,12 +38208,16 @@ function freezeOverrideSnapshot(input, opts = {}) {
|
|
|
37952
38208
|
}
|
|
37953
38209
|
return snapshot2;
|
|
37954
38210
|
}
|
|
37955
|
-
var MAX_TIMELINE_FRAMES;
|
|
38211
|
+
var MAX_TIMELINE_FRAMES, WIDGET_BUDGET_MS, realmPromise, CAPTURE_SLOT, READBACK_SERVICE;
|
|
37956
38212
|
var init_widgets = __esm({
|
|
37957
38213
|
"../../core/dev-server/src/widgets.ts"() {
|
|
37958
38214
|
init_validator();
|
|
37959
38215
|
init_preview_surfaces();
|
|
37960
38216
|
MAX_TIMELINE_FRAMES = 48;
|
|
38217
|
+
WIDGET_BUDGET_MS = 5e3;
|
|
38218
|
+
realmPromise = null;
|
|
38219
|
+
CAPTURE_SLOT = "himi.widget.capture";
|
|
38220
|
+
READBACK_SERVICE = "himi.widget.readback";
|
|
37961
38221
|
}
|
|
37962
38222
|
});
|
|
37963
38223
|
|
|
@@ -38020,7 +38280,7 @@ __export(validate_descriptor_exports, {
|
|
|
38020
38280
|
});
|
|
38021
38281
|
import { readFileSync as readFileSync38, existsSync as existsSync34, readdirSync as readdirSync18 } from "node:fs";
|
|
38022
38282
|
import { resolve as resolve28 } from "node:path";
|
|
38023
|
-
import { runInNewContext
|
|
38283
|
+
import { runInNewContext } from "node:vm";
|
|
38024
38284
|
async function loadAllowedComponents(appDir2) {
|
|
38025
38285
|
const modPath = resolve28(appDir2, "components.ts");
|
|
38026
38286
|
if (!existsSync34(modPath)) return void 0;
|
|
@@ -38111,7 +38371,7 @@ var init_validate_descriptor = __esm({
|
|
|
38111
38371
|
init_validate_descriptor_core();
|
|
38112
38372
|
nodeVmEvaluator = {
|
|
38113
38373
|
run(code, sandbox, opts) {
|
|
38114
|
-
|
|
38374
|
+
runInNewContext(code, sandbox, { timeout: opts.timeoutMs, filename: opts.filename });
|
|
38115
38375
|
}
|
|
38116
38376
|
};
|
|
38117
38377
|
setDefaultEvaluator(nodeVmEvaluator);
|
|
@@ -40372,7 +40632,7 @@ async function scanWidgets(dir) {
|
|
|
40372
40632
|
if (!loaded) continue;
|
|
40373
40633
|
try {
|
|
40374
40634
|
const js = loaded.bytes.toString("utf8");
|
|
40375
|
-
const { widgets, views } = extractWidgets(js);
|
|
40635
|
+
const { widgets, views } = await extractWidgets(js);
|
|
40376
40636
|
for (const [widgetId, def] of Object.entries(widgets)) {
|
|
40377
40637
|
out.push({ widgetId, bundle: name, family: def.family, size: def.size, refresh: def.refresh });
|
|
40378
40638
|
}
|
|
@@ -40398,11 +40658,11 @@ async function buildSnapshotForId(widgetId, dir) {
|
|
|
40398
40658
|
try {
|
|
40399
40659
|
const js = loaded.bytes.toString("utf8");
|
|
40400
40660
|
if (entry.view) {
|
|
40401
|
-
const snapshot2 = buildViewSnapshot(js, entry.view.viewId, entry.view.surfaceId);
|
|
40661
|
+
const snapshot2 = await buildViewSnapshot(js, entry.view.viewId, entry.view.surfaceId);
|
|
40402
40662
|
if (!snapshot2) return { ok: false, status: 404, error: "widget_not_found" };
|
|
40403
40663
|
return { ok: true, snapshot: snapshot2 };
|
|
40404
40664
|
}
|
|
40405
|
-
return { ok: true, snapshot: buildWidgetSnapshot(js, widgetId) };
|
|
40665
|
+
return { ok: true, snapshot: await buildWidgetSnapshot(js, widgetId) };
|
|
40406
40666
|
} catch (err) {
|
|
40407
40667
|
return { ok: false, status: 422, error: "widget_invalid", message: err.message };
|
|
40408
40668
|
}
|
|
@@ -63961,20 +64221,20 @@ async function producedWidgetFamilies(dir) {
|
|
|
63961
64221
|
if (js === void 0) continue;
|
|
63962
64222
|
let extracted;
|
|
63963
64223
|
try {
|
|
63964
|
-
extracted = widgetsModule.extractWidgets(js);
|
|
64224
|
+
extracted = await widgetsModule.extractWidgets(js);
|
|
63965
64225
|
} catch {
|
|
63966
64226
|
continue;
|
|
63967
64227
|
}
|
|
63968
64228
|
for (const [widgetId, def] of Object.entries(extracted.widgets)) {
|
|
63969
64229
|
try {
|
|
63970
|
-
widgetsModule.buildWidgetSnapshot(js, widgetId);
|
|
64230
|
+
await widgetsModule.buildWidgetSnapshot(js, widgetId);
|
|
63971
64231
|
families.add(def.family);
|
|
63972
64232
|
} catch {
|
|
63973
64233
|
}
|
|
63974
64234
|
}
|
|
63975
64235
|
for (const entry of widgetsModule.frozenViewManifestEntries(extracted.views, emitted)) {
|
|
63976
64236
|
try {
|
|
63977
|
-
if (widgetsModule.buildViewSnapshot(js, entry.view.viewId, entry.view.surfaceId)) families.add(entry.family);
|
|
64237
|
+
if (await widgetsModule.buildViewSnapshot(js, entry.view.viewId, entry.view.surfaceId)) families.add(entry.family);
|
|
63978
64238
|
} catch {
|
|
63979
64239
|
}
|
|
63980
64240
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.873.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
|
|
6
6
|
"keywords": [
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"artifactId": "himalaya-cli"
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
"falconPackageHash": "
|
|
66
|
+
"falconPackageHash": "e317363609876f9814b32d6a6ca7319bdd4527bfba2889d9027735e6"
|
|
67
67
|
}
|