@valbuild/core 0.136.8 → 0.138.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.
@@ -5630,6 +5630,21 @@ function missingDeclaredKeys(declared, present) {
5630
5630
  });
5631
5631
  }
5632
5632
 
5633
+ /**
5634
+ * A router's scheme restriction, as a spreadable fragment.
5635
+ *
5636
+ * Spread rather than assigned so an unrestricted router adds no key at all:
5637
+ * `routerSchemes: undefined` would serialize as an explicit `undefined` and
5638
+ * show up in every schema diff for no reason.
5639
+ */
5640
+ function routerSchemes(router) {
5641
+ var _router$getUrlSchemeP;
5642
+ var schemes = router === null || router === void 0 || (_router$getUrlSchemeP = router.getUrlSchemePolicy) === null || _router$getUrlSchemeP === void 0 ? void 0 : _router$getUrlSchemeP.call(router).schemes;
5643
+ return schemes === undefined ? {} : {
5644
+ routerSchemes: _toConsumableArray(schemes)
5645
+ };
5646
+ }
5647
+
5633
5648
  /**
5634
5649
  * The source type of a `.jsonValues()` record: every entry value is EITHER a
5635
5650
  * lazily loaded {@link JsonSource} whose resolved content is the (loosened, see
@@ -6446,21 +6461,22 @@ var RecordSchema = /*#__PURE__*/function (_Schema) {
6446
6461
  key: "executeSerialize",
6447
6462
  value: function executeSerialize() {
6448
6463
  var _this$renderInput, _this$keySchema, _this$currentRouter, _this$customValidateF, _this$externalLabel;
6449
- var result = {
6464
+ var result = _objectSpread2(_objectSpread2({
6450
6465
  type: "record",
6451
6466
  render: (_this$renderInput = this.renderInput) !== null && _this$renderInput !== void 0 ? _this$renderInput : undefined,
6452
6467
  item: this.item["executeSerialize"](),
6453
6468
  key: (_this$keySchema = this.keySchema) === null || _this$keySchema === void 0 ? void 0 : _this$keySchema["executeSerialize"](),
6454
6469
  opt: this.opt,
6455
6470
  preview: this.previewInput ? true : undefined,
6456
- router: (_this$currentRouter = this.currentRouter) === null || _this$currentRouter === void 0 ? void 0 : _this$currentRouter.getRouterId(),
6471
+ router: (_this$currentRouter = this.currentRouter) === null || _this$currentRouter === void 0 ? void 0 : _this$currentRouter.getRouterId()
6472
+ }, routerSchemes(this.currentRouter)), {}, {
6457
6473
  customValidate: this.customValidateFunctions && ((_this$customValidateF = this.customValidateFunctions) === null || _this$customValidateF === void 0 ? void 0 : _this$customValidateF.length) > 0,
6458
6474
  jsonValues: this.isJsonValues ? true : undefined,
6459
6475
  external: (_this$externalLabel = this.externalLabel) !== null && _this$externalLabel !== void 0 ? _this$externalLabel : undefined,
6460
6476
  readonly: this.isReadonly,
6461
6477
  hidden: this.isHidden,
6462
6478
  description: this.description
6463
- };
6479
+ });
6464
6480
  if (this.mediaOptions) {
6465
6481
  result.mediaType = this.mediaOptions.type;
6466
6482
  result.accept = this.mediaOptions.accept;
@@ -8740,7 +8756,8 @@ function settings() {
8740
8756
  }).nullable().describe("The project's own mark, shown where Val's is in the Studio. A square-ish mark rather than a wordmark: the slot is 32px wide.")
8741
8757
  }),
8742
8758
  studio: new SettingsSchema({
8743
- tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it.")
8759
+ tour: _boolean().nullable().describe("Whether editors are offered the guided tour of the Studio. Unset means yes. Off hides the offer for everyone on this project — the tour stays in Quick actions for anyone who wants it."),
8760
+ commitMessage: enumSchema("automatic", "required").nullable().describe("Whether publishing asks for a commit message. Unset is the same as 'automatic': Publish publishes straight away, with a message the AI writes (or one listing what changed). 'required' asks every time, and will not publish an empty one.")
8744
8761
  }),
8745
8762
  locales: new SettingsSchema({
8746
8763
  // Nullable because the source type is, and because the Studio writes
@@ -10912,7 +10929,7 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
10912
10929
  }, {
10913
10930
  key: "executeValidate",
10914
10931
  value: function executeValidate(path, src) {
10915
- var _this$options, _this$options2, _this$options3, _this$options4;
10932
+ var _this$options, _this$options2, _this$options3, _this$options4, _this$options5, _this$options6;
10916
10933
  var errors = this.executeCustomValidateFunctions(src, this.customValidateFunctions, {
10917
10934
  path: path
10918
10935
  });
@@ -10926,7 +10943,31 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
10926
10943
  });
10927
10944
  return _defineProperty({}, path, errors);
10928
10945
  }
10929
- if ((_this$options = this.options) !== null && _this$options !== void 0 && _this$options.from && (_this$options2 = this.options) !== null && _this$options2 !== void 0 && _this$options2.to) {
10946
+ // The bounds below are compared as strings, which only orders days when
10947
+ // every side is a real `YYYY-MM-DD`: "19f81-12-30" sorts between
10948
+ // "1900-01-01" and "2024-01-01" and would pass them.
10949
+ if (!isCalendarDay(src)) {
10950
+ errors.push({
10951
+ message: "Value '".concat(src, "' is not a valid date (expected YYYY-MM-DD)"),
10952
+ value: src
10953
+ });
10954
+ return _defineProperty({}, path, errors);
10955
+ }
10956
+ var bounds = [["From", (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.from], ["To", (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.to]];
10957
+ for (var _i = 0, _bounds = bounds; _i < _bounds.length; _i++) {
10958
+ var _bounds$_i = _slicedToArray(_bounds[_i], 2),
10959
+ name = _bounds$_i[0],
10960
+ bound = _bounds$_i[1];
10961
+ if (bound !== undefined && !isCalendarDay(bound)) {
10962
+ errors.push({
10963
+ message: "".concat(name, " date '").concat(bound, "' is not a valid date (expected YYYY-MM-DD)"),
10964
+ value: src,
10965
+ typeError: true
10966
+ });
10967
+ return _defineProperty({}, path, errors);
10968
+ }
10969
+ }
10970
+ if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.from && (_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.to) {
10930
10971
  if (this.options.from > this.options.to) {
10931
10972
  errors.push({
10932
10973
  message: "From date ".concat(this.options.from, " is after to date ").concat(this.options.to),
@@ -10939,14 +10980,14 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
10939
10980
  value: src
10940
10981
  });
10941
10982
  }
10942
- } else if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.from) {
10983
+ } else if ((_this$options5 = this.options) !== null && _this$options5 !== void 0 && _this$options5.from) {
10943
10984
  if (src < this.options.from) {
10944
10985
  errors.push({
10945
10986
  message: "Date is before the minimum date ".concat(this.options.from),
10946
10987
  value: src
10947
10988
  });
10948
10989
  }
10949
- } else if ((_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.to) {
10990
+ } else if ((_this$options6 = this.options) !== null && _this$options6 !== void 0 && _this$options6.to) {
10950
10991
  if (src > this.options.to) {
10951
10992
  errors.push({
10952
10993
  message: "Date is after the maximum date ".concat(this.options.to),
@@ -11116,6 +11157,27 @@ var DateSchema = /*#__PURE__*/function (_Schema) {
11116
11157
  }
11117
11158
  }]);
11118
11159
  }(Schema);
11160
+ var CALENDAR_DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
11161
+
11162
+ /**
11163
+ * Whether `value` is a `YYYY-MM-DD` day that exists on the calendar — so
11164
+ * "2023-02-29" and "2024-13-01" are not, even though they have the shape.
11165
+ */
11166
+ function isCalendarDay(value) {
11167
+ var match = CALENDAR_DAY.exec(value);
11168
+ if (!match) {
11169
+ return false;
11170
+ }
11171
+ var year = Number(match[1]);
11172
+ var month = Number(match[2]);
11173
+ var day = Number(match[3]);
11174
+ // Out-of-range fields roll over (Feb 30 -> Mar 2) instead of failing, so a
11175
+ // day is real only if it survives the round trip. setUTCFullYear, not
11176
+ // Date.UTC: the latter maps years 0-99 to 1900-1999.
11177
+ var date = new Date(0);
11178
+ date.setUTCFullYear(year, month - 1, day);
11179
+ return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
11180
+ }
11119
11181
  var date = function date(options) {
11120
11182
  return new DateSchema(options);
11121
11183
  };
@@ -13765,35 +13827,165 @@ function isInlineRender(schema) {
13765
13827
  return false;
13766
13828
  }
13767
13829
 
13768
- var externalPageRouter = {
13769
- getRouterId: function getRouterId() {
13770
- return "external-url-router";
13771
- },
13772
- validate: function validate(_moduleFilePath, urlPaths) {
13773
- var errors = [];
13774
- var _iterator = _createForOfIteratorHelper(urlPaths),
13775
- _step;
13776
- try {
13777
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
13778
- var urlPath = _step.value;
13779
- if (!(urlPath.startsWith("https://") || urlPath.startsWith("http://"))) {
13780
- errors.push({
13781
- error: {
13782
- message: "URL path \"".concat(urlPath, "\" does not start with \"https://\" or \"http://\""),
13783
- expectedPath: null,
13784
- urlPath: urlPath
13785
- }
13786
- });
13830
+ /**
13831
+ * Which URL schemes an external page key may use.
13832
+ *
13833
+ * External pages are the URLs a site links OUT to, and a link is not only a web
13834
+ * page: `mailto:` on a contact page and `tel:` on a phone number are the same
13835
+ * kind of thing to whoever maintains the list, and were refused outright.
13836
+ *
13837
+ * So the default is a deny list rather than an allow list — anything with a
13838
+ * scheme, except the handful that are not links at all. Listing every scheme
13839
+ * worth linking to is a list that is wrong the moment someone wants
13840
+ * `whatsapp:` or `msteams:`, and being wrong that way means an editor cannot
13841
+ * store a URL their site genuinely uses.
13842
+ *
13843
+ * A project that wants to be stricter says so:
13844
+ *
13845
+ * ```ts
13846
+ * s.router(externalPageRouter({ schemes: ["https", "mailto"] }), item)
13847
+ * ```
13848
+ */
13849
+
13850
+ /**
13851
+ * The schemes that are refused however wide the policy is.
13852
+ *
13853
+ * Not a style preference. An external page key ends up in an `href` in the
13854
+ * consuming site's own markup, so `javascript:` there is stored XSS on that
13855
+ * site, and `data:` is the same trick wearing a document. `vbscript:` is the
13856
+ * historical spelling of the first. `file:` and `blob:` address the visitor's
13857
+ * own machine rather than anywhere on the internet.
13858
+ *
13859
+ * These stay refused even when a project lists them in `schemes`: a config
13860
+ * option is not a good enough reason to let content inject script into the
13861
+ * page that renders it.
13862
+ */
13863
+ var UNSAFE_URL_SCHEMES = ["javascript", "data", "vbscript", "file", "blob"];
13864
+ /** The scheme of a URL, lowercased and without the colon, or null. */
13865
+ function schemeOf(url) {
13866
+ var match = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url.trim());
13867
+ return match === null ? null : match[1].toLowerCase();
13868
+ }
13869
+ /** `null` when the URL's scheme is acceptable under `policy`. */
13870
+ function rejectScheme(url) {
13871
+ var policy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13872
+ var scheme = schemeOf(url);
13873
+ if (scheme === null) {
13874
+ return {
13875
+ reason: "no-scheme"
13876
+ };
13877
+ }
13878
+ if (UNSAFE_URL_SCHEMES.includes(scheme)) {
13879
+ return {
13880
+ reason: "unsafe",
13881
+ scheme: scheme
13882
+ };
13883
+ }
13884
+ if (policy.schemes !== undefined) {
13885
+ var allowed = policy.schemes.map(function (s) {
13886
+ return s.toLowerCase();
13887
+ });
13888
+ if (!allowed.includes(scheme)) {
13889
+ return {
13890
+ reason: "not-allowed",
13891
+ scheme: scheme,
13892
+ allowed: policy.schemes
13893
+ };
13894
+ }
13895
+ }
13896
+ return null;
13897
+ }
13898
+
13899
+ /** What to tell whoever wrote the key. */
13900
+ function describeSchemeRejection(rejection) {
13901
+ switch (rejection.reason) {
13902
+ case "no-scheme":
13903
+ return "Must start with a scheme, like https:// or mailto:.";
13904
+ case "unsafe":
13905
+ return "\"".concat(rejection.scheme, ":\" cannot be used in a link: it runs code or reads the visitor's own machine rather than pointing at somewhere on the internet.");
13906
+ case "not-allowed":
13907
+ return "\"".concat(rejection.scheme, ":\" is not one of the schemes this router allows (").concat(rejection.allowed.join(", "), ").");
13908
+ }
13909
+ }
13910
+
13911
+ /**
13912
+ * Whether a link check can open this URL.
13913
+ *
13914
+ * Separate from whether it is ALLOWED, and the two are not the same question:
13915
+ * `mailto:` is a perfectly good external page and there is no request to make
13916
+ * of it. A URL that cannot be opened is reported as not checked, never as
13917
+ * broken.
13918
+ */
13919
+ function isCheckableScheme(url) {
13920
+ var scheme = schemeOf(url);
13921
+ return scheme === "http" || scheme === "https";
13922
+ }
13923
+
13924
+ /**
13925
+ * The router whose keys are URLs to somewhere else.
13926
+ *
13927
+ * Used directly for the wide default, or called to narrow it:
13928
+ *
13929
+ * ```ts
13930
+ * s.router(externalPageRouter, item) // anything safe
13931
+ * s.router(externalPageRouter({ schemes: ["https"] }), item) // https only
13932
+ * ```
13933
+ *
13934
+ * Its one rule is that a key has a scheme, and that the scheme is not one of
13935
+ * the handful that are not links at all - see `externalUrlSchemes.ts`. That
13936
+ * rule was WRITTEN here from the start and never enforced: the errors were
13937
+ * collected into a list and then `return []` threw them away, so every key was
13938
+ * accepted however it was spelled.
13939
+ *
13940
+ * `expectedPath` is null because there is nothing to suggest: a key that is
13941
+ * not a URL could have been meant as any URL, and guessing `https://` in front
13942
+ * of it would propose a different site as often as the right one.
13943
+ */
13944
+ var externalPageRouter = Object.assign(function (policy) {
13945
+ return createExternalPageRouter(policy);
13946
+ }, createExternalPageRouter());
13947
+
13948
+ /**
13949
+ * Callable AND a router, so `externalPageRouter` keeps working unchanged
13950
+ * where nobody needs to configure it - which is almost everywhere.
13951
+ */
13952
+
13953
+ function createExternalPageRouter() {
13954
+ var policy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
13955
+ return {
13956
+ getRouterId: function getRouterId() {
13957
+ return "external-url-router";
13958
+ },
13959
+ getUrlSchemePolicy: function getUrlSchemePolicy() {
13960
+ return policy;
13961
+ },
13962
+ validate: function validate(_moduleFilePath, urlPaths) {
13963
+ var errors = [];
13964
+ var _iterator = _createForOfIteratorHelper(urlPaths),
13965
+ _step;
13966
+ try {
13967
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
13968
+ var urlPath = _step.value;
13969
+ var rejection = rejectScheme(urlPath, policy);
13970
+ if (rejection !== null) {
13971
+ errors.push({
13972
+ error: {
13973
+ message: "URL \"".concat(urlPath, "\" cannot be used here. ").concat(describeSchemeRejection(rejection)),
13974
+ expectedPath: null,
13975
+ urlPath: urlPath
13976
+ }
13977
+ });
13978
+ }
13787
13979
  }
13980
+ } catch (err) {
13981
+ _iterator.e(err);
13982
+ } finally {
13983
+ _iterator.f();
13788
13984
  }
13789
- } catch (err) {
13790
- _iterator.e(err);
13791
- } finally {
13792
- _iterator.f();
13985
+ return errors;
13793
13986
  }
13794
- return [];
13795
- }
13796
- };
13987
+ };
13988
+ }
13797
13989
  // Helper function to validate a URL path against a route pattern
13798
13990
  function validateUrlAgainstPattern(urlPath, routePattern) {
13799
13991
  // Remove leading slash and split URL path
@@ -14193,7 +14385,7 @@ function isPageRouter(routerId) {
14193
14385
 
14194
14386
  var corePackageJson = {
14195
14387
  name: "@valbuild/core",
14196
- version: "0.136.8",
14388
+ version: "0.138.0",
14197
14389
  "private": false,
14198
14390
  description: "Val - supercharged hard-coded content",
14199
14391
  repository: {
@@ -14307,6 +14499,11 @@ var Internal = {
14307
14499
  nextAppRouter: nextAppRouter,
14308
14500
  tanstackRouter: tanstackRouter,
14309
14501
  externalPageRouter: externalPageRouter,
14502
+ describeSchemeRejection: describeSchemeRejection,
14503
+ isCheckableScheme: isCheckableScheme,
14504
+ rejectScheme: rejectScheme,
14505
+ schemeOf: schemeOf,
14506
+ UNSAFE_URL_SCHEMES: UNSAFE_URL_SCHEMES,
14310
14507
  color: {
14311
14508
  parseColor: parseColor,
14312
14509
  formatColor: formatColor,
@@ -14399,4 +14596,4 @@ function tryJsonParse(str) {
14399
14596
  }
14400
14597
  }
14401
14598
 
14402
- export { DEFAULT_COLOR_FORMAT as $, ArraySchema as A, BooleanSchema as B, isRootModuleFilePath as C, DEFAULT_CONTENT_HOST as D, resolveSettingsModule as E, FATAL_ERROR_TYPES as F, GenericSelector as G, localeTagError as H, Internal as I, localeOfValue as J, declaredKeySetOf as K, LocaleSchema as L, ModuleFilePathSep as M, NumberSchema as N, ObjectSchema as O, PatchError as P, missingDeclaredKeys as Q, RecordSchema as R, Schema as S, THEME_LOGO_DIRECTORY as T, discriminatedUnionBranchOf as U, VAL_EXTENSION as V, ColorSchema as W, CodeSchema as X, CODE_LANGUAGES as Y, COLOR_FORMATS as Z, _typeof as _, _slicedToArray as a, DateTimeSchema as a0, KeyOfSchema as a1, ValViewSchema as a2, isValViewSource as a3, RouteSchema as a4, RichTextSchema as a5, DiscriminatedUnionSchema as a6, EnumSchema as a7, LiteralSchema as a8, deserializeSchema as a9, previewScope as aa, isInlineRender as ab, parseNextJsRoutePattern as ac, parseTanStackRoutePattern as ad, tanStackSegmentsOfRoutePath as ae, validateUrlAgainstPattern as af, getSourcePathFromRoute as ag, isPageRouter as ah, _createClass as b, _classCallCheck as c, _toConsumableArray as d, DEFAULT_APP_HOST as e, DEFAULT_STATIC_HOST as f, extractValModules as g, computeValModuleShas as h, initVal as i, computeSourcesSha as j, hasRemoteFileSchema as k, hasMediaSchema as l, modules as m, DEFAULT_VAL_REMOTE_HOST as n, derefPatch as o, StringSchema as p, ImageSchema as q, FileSchema as r, splitModuleFilePathAndModulePath as s, DateSchema as t, SettingsSchema as u, ASSISTANT_SETTINGS_MAX_LENGTH as v, THEME_RADIUS_STEPS as w, THEME_RADIUS_LENGTHS as x, assistantAvailability as y, SETTINGS_MODULE_CONVENTION as z };
14599
+ export { DEFAULT_COLOR_FORMAT as $, ArraySchema as A, BooleanSchema as B, isRootModuleFilePath as C, DEFAULT_CONTENT_HOST as D, resolveSettingsModule as E, FATAL_ERROR_TYPES as F, GenericSelector as G, localeTagError as H, Internal as I, localeOfValue as J, declaredKeySetOf as K, LocaleSchema as L, ModuleFilePathSep as M, NumberSchema as N, ObjectSchema as O, PatchError as P, missingDeclaredKeys as Q, RecordSchema as R, Schema as S, THEME_LOGO_DIRECTORY as T, discriminatedUnionBranchOf as U, VAL_EXTENSION as V, ColorSchema as W, CodeSchema as X, CODE_LANGUAGES as Y, COLOR_FORMATS as Z, _typeof as _, _slicedToArray as a, DateTimeSchema as a0, KeyOfSchema as a1, ValViewSchema as a2, isValViewSource as a3, RouteSchema as a4, RichTextSchema as a5, DiscriminatedUnionSchema as a6, EnumSchema as a7, LiteralSchema as a8, deserializeSchema as a9, previewScope as aa, isInlineRender as ab, parseNextJsRoutePattern as ac, parseTanStackRoutePattern as ad, tanStackSegmentsOfRoutePath as ae, validateUrlAgainstPattern as af, getSourcePathFromRoute as ag, isPageRouter as ah, describeSchemeRejection as ai, isCheckableScheme as aj, rejectScheme as ak, schemeOf as al, UNSAFE_URL_SCHEMES as am, _createClass as b, _classCallCheck as c, _toConsumableArray as d, DEFAULT_APP_HOST as e, DEFAULT_STATIC_HOST as f, extractValModules as g, computeValModuleShas as h, initVal as i, computeSourcesSha as j, hasRemoteFileSchema as k, hasMediaSchema as l, modules as m, DEFAULT_VAL_REMOTE_HOST as n, derefPatch as o, StringSchema as p, ImageSchema as q, FileSchema as r, splitModuleFilePathAndModulePath as s, DateSchema as t, SettingsSchema as u, ASSISTANT_SETTINGS_MAX_LENGTH as v, THEME_RADIUS_STEPS as w, THEME_RADIUS_LENGTHS as x, assistantAvailability as y, SETTINGS_MODULE_CONVENTION as z };