@valbuild/core 0.130.0 → 0.136.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.
@@ -8793,6 +8793,317 @@ function localesSectionErrors(src) {
8793
8793
  return errors;
8794
8794
  }
8795
8795
 
8796
+ /**
8797
+ * Type-level-only slot carrying the SOURCE TYPE of the module a view names.
8798
+ *
8799
+ * Optional, and that is what makes the marker writable: `{ view: "/foo.val.ts" }`
8800
+ * is a plain literal, and an optional symbol-keyed property costs the author
8801
+ * nothing to satisfy. It exists so the read side can say WHAT is behind the
8802
+ * pointer — `ValView<FooSrc>` rather than `ValView<unknown>` — without the
8803
+ * author having to write a type they do not have.
8804
+ */
8805
+
8806
+ /**
8807
+ * The source of a view field: a pointer to another module, and nothing else.
8808
+ *
8809
+ * Written as a plain object, like media and for the same reason — the same
8810
+ * value has to work in a `.val.ts` and in a `*.val.json` entry, and a literal
8811
+ * survives the static extraction that a constructor call does not. See
8812
+ * `architecture/media.md`.
8813
+ *
8814
+ * `Id` is the target's module file path as a LITERAL type, inferred from the
8815
+ * module handed to `s.view(...)`. So the path autocompletes at the call site,
8816
+ * and a source naming a different module than its schema does is a type error.
8817
+ *
8818
+ * Nothing decides "this is a view" by looking at the value; the SCHEMA does
8819
+ * (`type === "view"`). {@link isValViewSource} exists for the few places that have
8820
+ * a value and no schema, and is not the normal way to ask.
8821
+ */
8822
+
8823
+ /**
8824
+ * Is this value shaped like a view marker?
8825
+ *
8826
+ * A shape test, not an identity: `view` is a reserved object key
8827
+ * (`ObjectSchemaProps`), so no ordinary object source can take this shape — but
8828
+ * hand-written JSON can, which is why validation checks the value against the
8829
+ * schema rather than trusting it.
8830
+ */
8831
+ function isValViewSource(value) {
8832
+ return _typeof(value) === "object" && value !== null && !Array.isArray(value) && "view" in value && typeof value.view === "string";
8833
+ }
8834
+
8835
+ /** The source type of the module a selector points at. */
8836
+
8837
+ /**
8838
+ * A field that points at ANOTHER module, for the Val editor's benefit.
8839
+ *
8840
+ * The source is `{ view: "/other.val.ts" }` — a pointer, and nothing else. The
8841
+ * module it names keeps its own source, its own patches, its own validation and
8842
+ * its own address; this field puts a row on THIS module's screen that an editor
8843
+ * clicks through to it.
8844
+ *
8845
+ * Two type parameters, each earning its place:
8846
+ *
8847
+ * - `Id` — the target's module file path as a literal, so the author writes
8848
+ * `{ view: "/other.val.ts" }` with autocomplete, and naming a different module
8849
+ * than the schema does is a type error rather than a validation error.
8850
+ * - `T` — the target's source type, carried on a phantom slot so the READ side
8851
+ * can say `ValView<T>` rather than `ValView<unknown>`. Nothing reads it yet;
8852
+ * it is here so that resolving a view through `useVal` can be added without
8853
+ * changing what is stored.
8854
+ */
8855
+ var ValViewSchema = /*#__PURE__*/function (_Schema) {
8856
+ function ValViewSchema(moduleFilePath) {
8857
+ var _this;
8858
+ var valModule = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
8859
+ var isReadonly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8860
+ var isHidden = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
8861
+ var description = arguments.length > 4 ? arguments[4] : undefined;
8862
+ var renderInput = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
8863
+ _classCallCheck(this, ValViewSchema);
8864
+ _this = _callSuper(this, ValViewSchema);
8865
+ _this.moduleFilePath = moduleFilePath;
8866
+ _this.valModule = valModule;
8867
+ _this.isReadonly = isReadonly;
8868
+ _this.isHidden = isHidden;
8869
+ _this.description = description;
8870
+ _this.renderInput = renderInput;
8871
+ return _this;
8872
+ }
8873
+
8874
+ /**
8875
+ * Describe this field.
8876
+ *
8877
+ * Shown next to the field's label in the Val editor — here, the place to say
8878
+ * WHY the other module is on this screen, since an editor who follows the row
8879
+ * is about to change content that other pages use too.
8880
+ *
8881
+ * @example
8882
+ * import otherVal from "./other.val"; // another module
8883
+ * const schema = s.object({
8884
+ * shared: s.view(otherVal).describe("Shared by every page"),
8885
+ * title: s.string(),
8886
+ * });
8887
+ * export default c.define("/example.val.ts", schema, {
8888
+ * shared: { view: "/other.val.ts" },
8889
+ * title: "Hello",
8890
+ * });
8891
+ */
8892
+ _inherits(ValViewSchema, _Schema);
8893
+ return _createClass(ValViewSchema, [{
8894
+ key: "describe",
8895
+ value: function describe(description) {
8896
+ return new ValViewSchema(this.moduleFilePath, this.valModule, this.isReadonly, this.isHidden, description !== null && description !== void 0 ? description : undefined, this.renderInput);
8897
+ }
8898
+
8899
+ /**
8900
+ * The stored pointer must name the module the SCHEMA names.
8901
+ *
8902
+ * It cannot disagree in a `.val.ts` — the source type is the literal path, so
8903
+ * a mismatch does not compile. It can disagree in hand-written JSON (a
8904
+ * `.jsonValues()` entry, an external record), which is what this is for.
8905
+ */
8906
+ }, {
8907
+ key: "executeValidate",
8908
+ value: function executeValidate(path, src) {
8909
+ if (!isValViewSource(src)) {
8910
+ return _defineProperty({}, path, [{
8911
+ message: "Expected a view pointer ({ view: \"".concat(this.moduleFilePath, "\" }), got '").concat(src === null ? "null" : _typeof(src), "'"),
8912
+ value: src,
8913
+ fixes: ["view:check-module"]
8914
+ }]);
8915
+ }
8916
+ if (src.view !== this.moduleFilePath) {
8917
+ return _defineProperty({}, path, [{
8918
+ message: "This view points at '".concat(src.view, "', but its schema says '").concat(this.moduleFilePath, "'"),
8919
+ value: src,
8920
+ fixes: ["view:check-module"]
8921
+ }]);
8922
+ }
8923
+ return false;
8924
+ }
8925
+ }, {
8926
+ key: "executeCustomValidateAt",
8927
+ value: function executeCustomValidateAt(_path, _src) {
8928
+ return [];
8929
+ }
8930
+ }, {
8931
+ key: "executeAssert",
8932
+ value: function executeAssert(path, src) {
8933
+ if (isValViewSource(src)) {
8934
+ return {
8935
+ success: true,
8936
+ data: src
8937
+ };
8938
+ }
8939
+ return {
8940
+ success: false,
8941
+ errors: _defineProperty({}, path, [{
8942
+ message: "Expected a view pointer ({ view: string }), got '".concat(src === null ? "null" : _typeof(src), "'"),
8943
+ typeError: true
8944
+ }])
8945
+ };
8946
+ }
8947
+
8948
+ /**
8949
+ * Not available: a view points at a module, and `null` is not a module.
8950
+ * Throws if called.
8951
+ *
8952
+ * @example
8953
+ * import otherVal from "./other.val"; // another module
8954
+ * // `s.view(otherVal).nullable()` throws — a view is never nullable.
8955
+ * const schema = s.object({ shared: s.view(otherVal), title: s.string() });
8956
+ * export default c.define("/example.val.ts", schema, {
8957
+ * shared: { view: "/other.val.ts" },
8958
+ * title: "Hello",
8959
+ * });
8960
+ */
8961
+ }, {
8962
+ key: "nullable",
8963
+ value: function nullable() {
8964
+ throw new Error("s.view() cannot be nullable: it points at a module");
8965
+ }
8966
+
8967
+ /**
8968
+ * Marks the row read-only.
8969
+ *
8970
+ * The view's OWN flag, and it says nothing about the module it names: that
8971
+ * module has its own `readonly`, which is what governs editing once an
8972
+ * editor is there. A view is a link rather than an editor, so this changes
8973
+ * nothing about the row today — it exists because every schema has it, and
8974
+ * because a view that renders its target inline would need it.
8975
+ *
8976
+ * @example
8977
+ * import otherVal from "./other.val"; // another module
8978
+ * const schema = s.object({
8979
+ * shared: s.view(otherVal).readonly(),
8980
+ * title: s.string(),
8981
+ * });
8982
+ * export default c.define("/example.val.ts", schema, {
8983
+ * shared: { view: "/other.val.ts" },
8984
+ * title: "Hello",
8985
+ * });
8986
+ */
8987
+ }, {
8988
+ key: "readonly",
8989
+ value: function readonly() {
8990
+ var isReadonly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
8991
+ return new ValViewSchema(this.moduleFilePath, this.valModule, isReadonly, this.isHidden, this.description, this.renderInput);
8992
+ }
8993
+
8994
+ /**
8995
+ * Hide the ROW in the editor.
8996
+ *
8997
+ * The view's OWN flag, and it is the only thing that decides whether the row
8998
+ * is drawn — a view whose TARGET is hidden is still shown, and still leads
8999
+ * there. That is the point of it: a module that many modules `keyOf` into is
9000
+ * noise in the nav and belongs on exactly one page, so it is hidden from the
9001
+ * listing and reached through the view that puts it where it belongs.
9002
+ *
9003
+ * @example
9004
+ * import otherVal from "./other.val"; // another module
9005
+ * const schema = s.object({
9006
+ * shared: s.view(otherVal).hidden(),
9007
+ * title: s.string(),
9008
+ * });
9009
+ * export default c.define("/example.val.ts", schema, {
9010
+ * shared: { view: "/other.val.ts" },
9011
+ * title: "Hello",
9012
+ * });
9013
+ */
9014
+ }, {
9015
+ key: "hidden",
9016
+ value: function hidden() {
9017
+ var isHidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
9018
+ return new ValViewSchema(this.moduleFilePath, this.valModule, this.isReadonly, isHidden, this.description, this.renderInput);
9019
+ }
9020
+
9021
+ /**
9022
+ * How this field is laid out in the editor. Static configuration, not a
9023
+ * callback — see `render.ts`.
9024
+ *
9025
+ * @example
9026
+ * import otherVal from "./other.val"; // another module
9027
+ * const schema = s.object({
9028
+ * shared: s.view(otherVal).render({ as: "inline" }),
9029
+ * title: s.string(),
9030
+ * });
9031
+ * export default c.define("/example.val.ts", schema, {
9032
+ * shared: { view: "/other.val.ts" },
9033
+ * title: "Hello",
9034
+ * });
9035
+ */
9036
+ }, {
9037
+ key: "render",
9038
+ value: function render(input) {
9039
+ return new ValViewSchema(this.moduleFilePath, this.valModule, this.isReadonly, this.isHidden, this.description, input);
9040
+ }
9041
+ }, {
9042
+ key: "executeSerialize",
9043
+ value: function executeSerialize() {
9044
+ var _this$renderInput;
9045
+ return {
9046
+ type: "view",
9047
+ render: (_this$renderInput = this.renderInput) !== null && _this$renderInput !== void 0 ? _this$renderInput : undefined,
9048
+ opt: false,
9049
+ moduleFilePath: this.moduleFilePath,
9050
+ readonly: this.isReadonly,
9051
+ hidden: this.isHidden,
9052
+ description: this.description
9053
+ };
9054
+ }
9055
+ }, {
9056
+ key: "executePreview",
9057
+ value: function executePreview() {
9058
+ return {};
9059
+ }
9060
+ }]);
9061
+ }(Schema);
9062
+
9063
+ /**
9064
+ * Point at another module, so the Val editor shows a way into it from here.
9065
+ *
9066
+ * A view stores a pointer and nothing else: the module it names keeps its own
9067
+ * source, patches, validation and address, and an editor reaches it by clicking
9068
+ * through rather than by hunting for it. It is there so content that has to live
9069
+ * in its own module — a `keyOf` target, shared settings, a route-keyed record —
9070
+ * can still be found from the page it belongs to.
9071
+ *
9072
+ * Reading it in consuming code gives you a pointer with no fields on it. Read
9073
+ * the module it names directly instead.
9074
+ *
9075
+ * @example
9076
+ * import otherVal from "./other.val"; // another module
9077
+ * const schema = s.object({ shared: s.view(otherVal), title: s.string() });
9078
+ * export default c.define("/example.val.ts", schema, {
9079
+ * shared: { view: "/other.val.ts" },
9080
+ * title: "Hello",
9081
+ * });
9082
+ */
9083
+ var view = function view(valModule) {
9084
+ var path = getValPath(valModule);
9085
+ if (!path) {
9086
+ throw new Error("s.view() must be given a Val module");
9087
+ }
9088
+ return new ValViewSchema(path, valModule);
9089
+ };
9090
+
9091
+ /**
9092
+ * The brand that says "this is a module", carrying the module's own id.
9093
+ *
9094
+ * `Id` is inferred from {@link define}'s first argument, so a module's PATH is
9095
+ * in its type. That is what lets `s.view(fooVal)` produce a schema whose source
9096
+ * type is the literal `{ view: "/foo.val.ts" }` — the path autocompletes at the
9097
+ * call site, and a source that names a different module than the schema does is
9098
+ * a type error rather than something validation has to catch later.
9099
+ *
9100
+ * Defaulted to `string` so every `ValModule<T>` written without it keeps
9101
+ * meaning what it did. The property is covariant, so a module with a known id
9102
+ * is still assignable everywhere `ValModule<T>` is expected.
9103
+ */
9104
+
9105
+ /** A module's own path, read back off its type. */
9106
+
8796
9107
  /**
8797
9108
  * What may be WRITTEN as an external record's source, on top of the marker.
8798
9109
  *
@@ -8812,6 +9123,17 @@ function localesSectionErrors(src) {
8812
9123
  function define(id,
8813
9124
  // TODO: `/${string}`
8814
9125
  schema, source) {
9126
+ // A module cannot BE a view of another module. `s.view()` stores nothing, so
9127
+ // the module's whole source would be `undefined` — which type-checks, because
9128
+ // `undefined` is a `SelectorSource`, and then fails everywhere downstream that
9129
+ // expects a module to have source. A view is a field of a module, never the
9130
+ // module. Thrown rather than typed: making the `schema` parameter conditional
9131
+ // on T would put it in a non-inferable position and break inference for every
9132
+ // other schema. `extractValModules` reports what this throws as a module
9133
+ // error, which is where a developer will look.
9134
+ if (schema instanceof ValViewSchema) {
9135
+ throw Error("Cannot define '".concat(id, "' as a view: s.view() shows another module and stores nothing, so it cannot be a module's own schema. Put it in an s.object({ ... }) field instead."));
9136
+ }
8815
9137
  return _defineProperty(_defineProperty(_defineProperty({}, GetSource, source), GetSchema, schema), Path, id);
8816
9138
  }
8817
9139
  function getSource(valModule) {
@@ -11501,6 +11823,7 @@ function initSchema() {
11501
11823
  image: image,
11502
11824
  literal: literal,
11503
11825
  keyOf: keyOf,
11826
+ view: view,
11504
11827
  record: record,
11505
11828
  file: file,
11506
11829
  fileset: fileset,
@@ -12008,6 +12331,185 @@ function resolveSettingsModule(schemas) {
12008
12331
  };
12009
12332
  }
12010
12333
 
12334
+ /**
12335
+ * Every module a schema points at with `s.view(...)`, at any depth.
12336
+ *
12337
+ * A GENERIC walk over the serialized schema rather than a switch over the
12338
+ * container types, and that is deliberate: a schema can hold another schema in
12339
+ * places an enumeration keeps forgetting — a richtext's `a` and `img` options
12340
+ * each carry one, and a discriminated union's variants are an array of them.
12341
+ * Missing a nesting site here means missing a cycle, and a missed cycle is the
12342
+ * failure this check exists to prevent.
12343
+ */
12344
+ function viewTargetsOf(schema) {
12345
+ var targets = [];
12346
+ var seen = new Set();
12347
+ function walk(node) {
12348
+ if (_typeof(node) !== "object" || node === null) {
12349
+ return;
12350
+ }
12351
+ // A serialized schema is a tree, but guard anyway: this runs over data the
12352
+ // walk does not own, and a cycle here would hang module extraction.
12353
+ if (seen.has(node)) {
12354
+ return;
12355
+ }
12356
+ seen.add(node);
12357
+ if (Array.isArray(node)) {
12358
+ var _iterator = result._createForOfIteratorHelper(node),
12359
+ _step;
12360
+ try {
12361
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
12362
+ var item = _step.value;
12363
+ walk(item);
12364
+ }
12365
+ } catch (err) {
12366
+ _iterator.e(err);
12367
+ } finally {
12368
+ _iterator.f();
12369
+ }
12370
+ return;
12371
+ }
12372
+ var record = node;
12373
+ if (record["type"] === "view") {
12374
+ var target = record["moduleFilePath"];
12375
+ if (typeof target === "string") {
12376
+ targets.push(target);
12377
+ }
12378
+ // A view schema holds nothing else worth walking.
12379
+ return;
12380
+ }
12381
+ for (var _i = 0, _Object$values = Object.values(record); _i < _Object$values.length; _i++) {
12382
+ var value = _Object$values[_i];
12383
+ walk(value);
12384
+ }
12385
+ }
12386
+ walk(schema);
12387
+ return targets;
12388
+ }
12389
+
12390
+ /**
12391
+ * The cycles in a project's views: `A → B → A`, and `A → A`.
12392
+ *
12393
+ * Each cycle is returned once, as the modules in it in order, rotated so it
12394
+ * starts at the lexicographically smallest — otherwise the same cycle would be
12395
+ * reported once per module it passes through, worded differently each time.
12396
+ */
12397
+ function findViewCycles(schemas) {
12398
+ var edges = new Map();
12399
+ for (var _i2 = 0, _Object$entries = Object.entries(schemas); _i2 < _Object$entries.length; _i2++) {
12400
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2),
12401
+ moduleFilePath = _Object$entries$_i[0],
12402
+ schema = _Object$entries$_i[1];
12403
+ edges.set(moduleFilePath, viewTargetsOf(schema));
12404
+ }
12405
+ var cycles = [];
12406
+ var found = new Set();
12407
+ var done = new Set();
12408
+ var stack = [];
12409
+ var onStack = new Set();
12410
+ function visit(moduleFilePath) {
12411
+ var _edges$get;
12412
+ if (done.has(moduleFilePath)) {
12413
+ return;
12414
+ }
12415
+ if (onStack.has(moduleFilePath)) {
12416
+ var cycle = stack.slice(stack.indexOf(moduleFilePath));
12417
+ var key = canonical(cycle);
12418
+ if (!found.has(key)) {
12419
+ found.add(key);
12420
+ cycles.push(rotate(cycle));
12421
+ }
12422
+ return;
12423
+ }
12424
+ stack.push(moduleFilePath);
12425
+ onStack.add(moduleFilePath);
12426
+ var _iterator2 = result._createForOfIteratorHelper((_edges$get = edges.get(moduleFilePath)) !== null && _edges$get !== void 0 ? _edges$get : []),
12427
+ _step2;
12428
+ try {
12429
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
12430
+ var target = _step2.value;
12431
+ // A target that is not a module of this project is a different error —
12432
+ // the view names something that does not exist — and is reported where
12433
+ // the field is, not here.
12434
+ if (edges.has(target)) {
12435
+ visit(target);
12436
+ }
12437
+ }
12438
+ } catch (err) {
12439
+ _iterator2.e(err);
12440
+ } finally {
12441
+ _iterator2.f();
12442
+ }
12443
+ stack.pop();
12444
+ onStack["delete"](moduleFilePath);
12445
+ done.add(moduleFilePath);
12446
+ }
12447
+ var _iterator3 = result._createForOfIteratorHelper(edges.keys()),
12448
+ _step3;
12449
+ try {
12450
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
12451
+ var _moduleFilePath = _step3.value;
12452
+ visit(_moduleFilePath);
12453
+ }
12454
+ } catch (err) {
12455
+ _iterator3.e(err);
12456
+ } finally {
12457
+ _iterator3.f();
12458
+ }
12459
+ return cycles;
12460
+ }
12461
+ function rotate(cycle) {
12462
+ var at = 0;
12463
+ for (var i = 1; i < cycle.length; i++) {
12464
+ if (cycle[i] < cycle[at]) {
12465
+ at = i;
12466
+ }
12467
+ }
12468
+ return [].concat(_toConsumableArray(cycle.slice(at)), _toConsumableArray(cycle.slice(0, at)));
12469
+ }
12470
+ function canonical(cycle) {
12471
+ return rotate(cycle).join(" -> ");
12472
+ }
12473
+
12474
+ /**
12475
+ * A cycle of views, as one error per module in it.
12476
+ *
12477
+ * Per module rather than once, because a developer opens ONE of them and the
12478
+ * error has to be there when they do. Every copy names the whole cycle, so it
12479
+ * reads the same wherever it is found.
12480
+ */
12481
+ function viewCycleErrors(schemas) {
12482
+ var errors = [];
12483
+ var _iterator4 = result._createForOfIteratorHelper(findViewCycles(schemas)),
12484
+ _step4;
12485
+ try {
12486
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
12487
+ var cycle = _step4.value;
12488
+ var described = [].concat(_toConsumableArray(cycle), [cycle[0]]).join(" -> ");
12489
+ var _iterator5 = result._createForOfIteratorHelper(cycle),
12490
+ _step5;
12491
+ try {
12492
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
12493
+ var moduleFilePath = _step5.value;
12494
+ errors.push({
12495
+ path: moduleFilePath,
12496
+ message: cycle.length === 1 ? "s.view() cycle: '".concat(moduleFilePath, "' views itself. A view must point at another module.") : "s.view() cycle: ".concat(described, ". Views may not form a cycle.")
12497
+ });
12498
+ }
12499
+ } catch (err) {
12500
+ _iterator5.e(err);
12501
+ } finally {
12502
+ _iterator5.f();
12503
+ }
12504
+ }
12505
+ } catch (err) {
12506
+ _iterator4.e(err);
12507
+ } finally {
12508
+ _iterator4.f();
12509
+ }
12510
+ return errors;
12511
+ }
12512
+
12011
12513
  // Lazily constructed: this module is also evaluated inside the `vm` sandbox
12012
12514
  // used by loadValModules (when user val modules import @valbuild/core), and
12013
12515
  // that sandbox has no `TextEncoder` global. `hash()` is never called from
@@ -12175,7 +12677,7 @@ function extractValModules(_x) {
12175
12677
  }
12176
12678
  function _extractValModules() {
12177
12679
  _extractValModules = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(valModules) {
12178
- var moduleErrors, addModuleError, sources, schemas, serializedSchemas, shaEntries, moduleIdx, module, at, value, path, schema, source, serializedSchema, pathM, _iterator2, _step2, error, _t, _t2;
12680
+ var moduleErrors, addModuleError, sources, schemas, serializedSchemas, shaEntries, moduleIdx, module, at, value, path, schema, source, serializedSchema, pathM, _iterator2, _step2, error, _iterator3, _step3, _error, _t, _t2;
12179
12681
  return _regenerator().w(function (_context) {
12180
12682
  while (1) switch (_context.p = _context.n) {
12181
12683
  case 0:
@@ -12328,11 +12830,26 @@ function _extractValModules() {
12328
12830
  error = _step2.value;
12329
12831
  moduleErrors.push(error);
12330
12832
  }
12833
+ // Same kind of rule, same reason it is here: a view cycle is a property of the
12834
+ // whole set of schemas, so no single module can see it. Nothing further down
12835
+ // catches it either — a view stores a pointer, not content, so there is no
12836
+ // data cycle for a source walk to trip over.
12331
12837
  } catch (err) {
12332
12838
  _iterator2.e(err);
12333
12839
  } finally {
12334
12840
  _iterator2.f();
12335
12841
  }
12842
+ _iterator3 = result._createForOfIteratorHelper(viewCycleErrors(serializedSchemas));
12843
+ try {
12844
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
12845
+ _error = _step3.value;
12846
+ moduleErrors.push(_error);
12847
+ }
12848
+ } catch (err) {
12849
+ _iterator3.e(err);
12850
+ } finally {
12851
+ _iterator3.f();
12852
+ }
12336
12853
  return _context.a(2, _objectSpread2(_objectSpread2({
12337
12854
  sources: sources,
12338
12855
  schemas: schemas,
@@ -12435,7 +12952,7 @@ function hasRemoteFileSchema(schema) {
12435
12952
  return false;
12436
12953
  } else if (schema.type === "boolean" ||
12437
12954
  // An enum's values are strings, so there is nothing to look inside.
12438
- schema.type === "enum" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route" || schema.type === "locale") {
12955
+ schema.type === "enum" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route" || schema.type === "locale" || schema.type === "view") {
12439
12956
  return false;
12440
12957
  } else {
12441
12958
  var exhaustiveCheck = schema;
@@ -12510,7 +13027,9 @@ function hasMediaSchema(schema) {
12510
13027
  schema.type === "enum" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route" || schema.type === "locale" ||
12511
13028
  // A settings module holds the assistant's configuration, not content, and
12512
13029
  // nothing in it is media.
12513
- schema.type === "settings") {
13030
+ schema.type === "settings" ||
13031
+ // A view shows another module; that module answers for its own media.
13032
+ schema.type === "view") {
12514
13033
  return false;
12515
13034
  } else {
12516
13035
  // Same contract as above: adding a schema type without teaching this
@@ -12826,6 +13345,160 @@ function discriminatedUnionBranchOf(schema, tag) {
12826
13345
  return undefined;
12827
13346
  }
12828
13347
 
13348
+ /**
13349
+ * Where a resolved view handle keeps the module it points at.
13350
+ *
13351
+ * A symbol, so it is invisible to `JSON.stringify`, `Object.keys`, the source
13352
+ * SHAs and every walk that treats source as data — the handle still IS
13353
+ * `{ view: "/foo.val.ts" }` everywhere that matters.
13354
+ */
13355
+ var ViewTargetModule = Symbol["for"]("val/ViewTargetModule");
13356
+
13357
+ /**
13358
+ * A view's source with the module it names attached.
13359
+ *
13360
+ * `useVal(page.header)` needs the target's SOURCE and its SCHEMA, and the
13361
+ * stored pointer has neither — it is a path. The app has no way to turn a path
13362
+ * back into a module either: `val.modules` holds lazy `import()` thunks, and
13363
+ * `<ValModulesClient>` is optional. So the module travels with the handle.
13364
+ *
13365
+ * Attached by the READ path (`stegaEncode`) rather than written into source,
13366
+ * and that is the whole reason it works in draft mode: with an edit pending,
13367
+ * the source comes from the overlay store as plain JSON that never went near a
13368
+ * module. The schema is the module's own either way, so the encoder always has
13369
+ * it to hand.
13370
+ */
13371
+
13372
+ /** Attach the module a view names to its pointer. */
13373
+ function createViewHandle(source, valModule) {
13374
+ return _objectSpread2(_objectSpread2({}, source), {}, _defineProperty({}, ViewTargetModule, valModule));
13375
+ }
13376
+
13377
+ /** Is this a view pointer that carries the module it names? */
13378
+ function isViewHandle(value) {
13379
+ return _typeof(value) === "object" && value !== null && ViewTargetModule in value && value[ViewTargetModule] !== undefined;
13380
+ }
13381
+
13382
+ /** The module a handle carries, or undefined if it carries none. */
13383
+ function viewHandleModule(value) {
13384
+ if (!isViewHandle(value)) {
13385
+ return undefined;
13386
+ }
13387
+ return value[ViewTargetModule];
13388
+ }
13389
+
13390
+ /**
13391
+ * What a reader that needs a MODULE accepts, rather than a value.
13392
+ *
13393
+ * `useValKey`, `useValRoute`, `useValRouteUrl` and their `fetch*` counterparts
13394
+ * all read a path, a schema and a source off what they are handed, so — unlike
13395
+ * `useVal` — they cannot take an arbitrary selector. They CAN take a view
13396
+ * pointing at one, which is the point of a page declaring which module it
13397
+ * shows. {@link resolveViewedModule} is the runtime half, and takes exactly
13398
+ * this, so the two cannot drift.
13399
+ */
13400
+
13401
+ /**
13402
+ * What a reader was actually handed: the module itself, or — when it is a view
13403
+ * handle — the module that view points at.
13404
+ *
13405
+ * Every reader that needs a module rather than a value goes through this:
13406
+ * `useValKey`, `useValRoute`, `useValRouteUrl` and their `fetch*` counterparts
13407
+ * all pull the path, the schema and the source off the thing they are given,
13408
+ * and a view handle has none of those. It is `{ view: "/foo.val.ts" }` with the
13409
+ * module on a symbol, so reading `Internal.getValPath` off it gives
13410
+ * `undefined`, and every one of those readers treats `undefined` as its own
13411
+ * "no route matched" answer. Without this the call is not an error — it is a
13412
+ * silently empty one.
13413
+ *
13414
+ * A POINTER with no module on it throws rather than being passed through, for
13415
+ * the same reason `stegaEncode` throws on one: symbols do not survive
13416
+ * serialization, so a handle that crossed the server/client boundary as a prop
13417
+ * arrives looking like content and is not.
13418
+ */
13419
+
13420
+ /*
13421
+ * A widened implementation signature rather than assertions in the body.
13422
+ *
13423
+ * The module rides on a symbol as `unknown` — the encoder that attaches it has
13424
+ * no type for it — and no narrowing tells the checker that what is left after
13425
+ * both guards is the module arm. Declaring the callable signature above and
13426
+ * implementing against `unknown` says that once, where the runtime fact lives,
13427
+ * instead of casting at each of the twelve readers.
13428
+ */
13429
+ function resolveViewedModule(selector) {
13430
+ var resolved = viewHandleModule(selector);
13431
+ if (resolved !== undefined) {
13432
+ return resolved;
13433
+ }
13434
+ if (isValViewSource(selector)) {
13435
+ throw Error("Cannot resolve the view of '".concat(selector.view, "': it carries no module. ") + "Either it crossed a server/client boundary, which drops the module because it rides on a symbol, " + "or it points at a different module than its schema declares \u2014 which `val validate --fix` repairs. " + "Resolve it in the same component that read the module containing it, or read '".concat(selector.view, "' directly."));
13436
+ }
13437
+ return selector;
13438
+ }
13439
+
13440
+ /**
13441
+ * Every module a schema's views point at, keyed by path.
13442
+ *
13443
+ * Walked GENERICALLY over the schema instance's own properties rather than by
13444
+ * switching on schema type, for the same reason `viewTargetsOf` is: a schema
13445
+ * holds another schema in places an enumeration keeps forgetting. Missing a
13446
+ * site here is a view that cannot be resolved at runtime, which is a worse
13447
+ * failure than the walk being slightly broad.
13448
+ *
13449
+ * Memoised per schema INSTANCE. A module's schema is built once at import, and
13450
+ * `useVal` runs on every render — so without this the walk would be a render
13451
+ * cost rather than a load cost.
13452
+ */
13453
+ var cache = new WeakMap();
13454
+ function viewModulesOf(schema) {
13455
+ if (schema === undefined) {
13456
+ return new Map();
13457
+ }
13458
+ var cached = cache.get(schema);
13459
+ if (cached) {
13460
+ return cached;
13461
+ }
13462
+ var modules = new Map();
13463
+ var seen = new Set();
13464
+ function walk(node) {
13465
+ if (_typeof(node) !== "object" || node === null || seen.has(node)) {
13466
+ return;
13467
+ }
13468
+ seen.add(node);
13469
+ if (node instanceof ValViewSchema) {
13470
+ var target = node["moduleFilePath"];
13471
+ var valModule = node["valModule"];
13472
+ if (typeof target === "string" && valModule !== undefined) {
13473
+ modules.set(target, valModule);
13474
+ }
13475
+ return;
13476
+ }
13477
+ if (Array.isArray(node)) {
13478
+ var _iterator = result._createForOfIteratorHelper(node),
13479
+ _step;
13480
+ try {
13481
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
13482
+ var item = _step.value;
13483
+ walk(item);
13484
+ }
13485
+ } catch (err) {
13486
+ _iterator.e(err);
13487
+ } finally {
13488
+ _iterator.f();
13489
+ }
13490
+ return;
13491
+ }
13492
+ for (var _i = 0, _Object$values = Object.values(node); _i < _Object$values.length; _i++) {
13493
+ var value = _Object$values[_i];
13494
+ walk(value);
13495
+ }
13496
+ }
13497
+ walk(schema);
13498
+ cache.set(schema, modules);
13499
+ return modules;
13500
+ }
13501
+
12829
13502
  function deserializeSchema(serialized) {
12830
13503
  var schema = deserializeSchemaImpl(serialized);
12831
13504
  if (serialized.readonly) {
@@ -12837,7 +13510,7 @@ function deserializeSchema(serialized) {
12837
13510
  return schema;
12838
13511
  }
12839
13512
  function deserializeSchemaImpl(serialized) {
12840
- var _serialized$options, _serialized$options2, _serialized$render, _serialized$multiline, _serialized$render2, _serialized$render3, _serialized$render4, _serialized$render5, _serialized$render6, _serialized$render7, _serialized$render8, _serialized$accept, _serialized$dir, _serialized$remote, _serialized$jsonValue, _serialized$render0, _serialized$external, _serialized$render1, _serialized$render12, _serialized$render13, _serialized$render14, _serialized$render15, _serialized$render16, _serialized$render17;
13513
+ var _serialized$options, _serialized$options2, _serialized$render, _serialized$multiline, _serialized$render2, _serialized$render3, _serialized$render4, _serialized$render5, _serialized$render6, _serialized$render7, _serialized$render8, _serialized$accept, _serialized$dir, _serialized$remote, _serialized$jsonValue, _serialized$render0, _serialized$external, _serialized$render1, _serialized$render12, _serialized$render13, _serialized$render14, _serialized$render15, _serialized$render16, _serialized$render17, _serialized$readonly, _serialized$hidden, _serialized$render18;
12841
13514
  switch (serialized.type) {
12842
13515
  case "string":
12843
13516
  return new StringSchema(_objectSpread2(_objectSpread2({}, serialized.options), {}, {
@@ -12934,6 +13607,11 @@ function deserializeSchemaImpl(serialized) {
12934
13607
  return new ColorSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render16 = serialized.render) !== null && _serialized$render16 !== void 0 ? _serialized$render16 : null);
12935
13608
  case "code":
12936
13609
  return new CodeSchema(serialized.options, serialized.opt, [], false, false, serialized.description, (_serialized$render17 = serialized.render) !== null && _serialized$render17 !== void 0 ? _serialized$render17 : null);
13610
+ case "view":
13611
+ return new ValViewSchema(serialized.moduleFilePath,
13612
+ // No module: this schema came off the wire. The Studio navigates to a
13613
+ // view rather than resolving one, so nothing here needs it.
13614
+ undefined, (_serialized$readonly = serialized.readonly) !== null && _serialized$readonly !== void 0 ? _serialized$readonly : false, (_serialized$hidden = serialized.hidden) !== null && _serialized$hidden !== void 0 ? _serialized$hidden : false, serialized.description, (_serialized$render18 = serialized.render) !== null && _serialized$render18 !== void 0 ? _serialized$render18 : null);
12937
13615
  default:
12938
13616
  {
12939
13617
  var exhaustiveCheck = serialized;
@@ -13489,7 +14167,7 @@ function isPageRouter(routerId) {
13489
14167
 
13490
14168
  var corePackageJson = {
13491
14169
  name: "@valbuild/core",
13492
- version: "0.130.0",
14170
+ version: "0.136.0",
13493
14171
  "private": false,
13494
14172
  description: "Val - supercharged hard-coded content",
13495
14173
  repository: {
@@ -13547,10 +14225,32 @@ var ModuleFilePathSep = "?p=";
13547
14225
  var FATAL_ERROR_TYPES = ["no-schema", "no-source", "invalid-id", "no-module", "invalid-patch"];
13548
14226
  var DEFAULT_CONTENT_HOST = "https://content.val.build";
13549
14227
  var DEFAULT_APP_HOST = "https://admin.val.build";
14228
+ /**
14229
+ * Where Val's own immutable build artifacts are served from.
14230
+ *
14231
+ * Only one kind of thing lives here so far: the WebAssembly binary of the
14232
+ * bundler the Studio builds a site with. It cannot travel with the Studio's
14233
+ * own bundle -- 10.9 MB of binary, and `/api/val/static` is a base64 record of
14234
+ * TEXT inside the server bundle, which both corrupts it and would carry it
14235
+ * into every project that installs `@valbuild/ui`.
14236
+ *
14237
+ * Everything under this host is addressed by the SHA-256 of its own bytes, so
14238
+ * an upload is idempotent, a URL is immutable, and the artifact a build asks
14239
+ * for cannot be a different one than the build was made against.
14240
+ */
14241
+ var DEFAULT_STATIC_HOST = "https://static.val.build";
13550
14242
  var Internal = {
13551
14243
  VERSION: {
13552
14244
  core: corePackageJson.version
13553
14245
  },
14246
+ // A view's read-path plumbing: `Internal` rather than the public surface,
14247
+ // because an app never builds or unwraps a handle — it passes one to
14248
+ // `useVal`.
14249
+ createViewHandle: createViewHandle,
14250
+ isViewHandle: isViewHandle,
14251
+ resolveViewedModule: resolveViewedModule,
14252
+ viewHandleModule: viewHandleModule,
14253
+ viewModulesOf: viewModulesOf,
13554
14254
  mediaUrl: mediaUrl,
13555
14255
  resolveMedia: resolveMedia,
13556
14256
  isRemoteMediaPath: isRemoteMediaPath,
@@ -13670,6 +14370,7 @@ exports.ColorSchema = ColorSchema;
13670
14370
  exports.DEFAULT_APP_HOST = DEFAULT_APP_HOST;
13671
14371
  exports.DEFAULT_COLOR_FORMAT = DEFAULT_COLOR_FORMAT;
13672
14372
  exports.DEFAULT_CONTENT_HOST = DEFAULT_CONTENT_HOST;
14373
+ exports.DEFAULT_STATIC_HOST = DEFAULT_STATIC_HOST;
13673
14374
  exports.DEFAULT_VAL_REMOTE_HOST = DEFAULT_VAL_REMOTE_HOST;
13674
14375
  exports.DateSchema = DateSchema;
13675
14376
  exports.DateTimeSchema = DateTimeSchema;
@@ -13698,6 +14399,7 @@ exports.THEME_LOGO_DIRECTORY = THEME_LOGO_DIRECTORY;
13698
14399
  exports.THEME_RADIUS_LENGTHS = THEME_RADIUS_LENGTHS;
13699
14400
  exports.THEME_RADIUS_STEPS = THEME_RADIUS_STEPS;
13700
14401
  exports.VAL_EXTENSION = VAL_EXTENSION;
14402
+ exports.ValViewSchema = ValViewSchema;
13701
14403
  exports._classCallCheck = _classCallCheck;
13702
14404
  exports._createClass = _createClass;
13703
14405
  exports._slicedToArray = _slicedToArray;
@@ -13717,6 +14419,7 @@ exports.initVal = initVal;
13717
14419
  exports.isInlineRender = isInlineRender;
13718
14420
  exports.isPageRouter = isPageRouter;
13719
14421
  exports.isRootModuleFilePath = isRootModuleFilePath;
14422
+ exports.isValViewSource = isValViewSource;
13720
14423
  exports.localeOfValue = localeOfValue;
13721
14424
  exports.localeTagError = localeTagError;
13722
14425
  exports.missingDeclaredKeys = missingDeclaredKeys;