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