@valbuild/shared 0.146.4 → 0.148.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.
@@ -1,6 +1,6 @@
1
- import { _ as _createForOfIteratorHelper, o as _objectSpread2, q as _arrayLikeToArray, r as _unsupportedIterableToArray, s as _typeof, t as _defineProperty, u as _toPropertyKey } from '../../dist/getSitemapTree-e65acefe.esm.js';
2
- export { D as DEFAULT_CANVAS_SELECTION, e as DEFAULT_CANVAS_SELECTION_SOFT, b as VAL_AI_SESSION_STORAGE_KEY, d as VAL_CANVAS_MESSAGE, c as VAL_CANVAS_PARAM, V as VAL_CONFIG_SESSION_STORAGE_KEY, a as VAL_THEME_SESSION_STORAGE_KEY, h as getNextAppRouterSourceFolder, m as getPageRouterSitemapTree, k as getPageRouterSourceFolder, n as getPatternFromModuleFilePath, j as getTanStackRouterSourceFolder, l as isTanStackRoutesFolder, g as isValCanvasFrame, i as isValCanvasPageMessage, f as isValCanvasStudioMessage, p as parseRoutePattern, w as withValCanvasParam } from '../../dist/getSitemapTree-e65acefe.esm.js';
3
- import { Internal, CODE_LANGUAGES, resolveSettingsModule, missingDeclaredKeys, localeOfValue, discriminatedUnionBranchOf, DEFAULT_COLOR_FORMAT, declaredKeySetOf } from '@valbuild/core';
1
+ import { _ as _createForOfIteratorHelper, q as _objectSpread2, r as _arrayLikeToArray, s as _unsupportedIterableToArray, t as _typeof, u as _defineProperty, v as _toPropertyKey } from '../../dist/getSitemapTree-c70ea145.esm.js';
2
+ export { D as DEFAULT_CANVAS_SELECTION, e as DEFAULT_CANVAS_SELECTION_SOFT, b as VAL_AI_SESSION_STORAGE_KEY, d as VAL_CANVAS_MESSAGE, c as VAL_CANVAS_PARAM, V as VAL_CONFIG_SESSION_STORAGE_KEY, a as VAL_THEME_SESSION_STORAGE_KEY, h as formatRoutePattern, j as getNextAppRouterSourceFolder, n as getPageRouterSitemapTree, l as getPageRouterSourceFolder, o as getPatternFromModuleFilePath, k as getTanStackRouterSourceFolder, m as isTanStackRoutesFolder, g as isValCanvasFrame, i as isValCanvasPageMessage, f as isValCanvasStudioMessage, p as parseRoutePattern, w as withValCanvasParam } from '../../dist/getSitemapTree-c70ea145.esm.js';
3
+ import { Internal, CODE_LANGUAGES, resolveSettingsModule, missingDeclaredKeys, localeOfValue, discriminatedUnionBranchOf, routePatternOf, matchRoutePattern, DEFAULT_COLOR_FORMAT, declaredKeySetOf } from '@valbuild/core';
4
4
  import { z } from 'zod';
5
5
  import { fromError } from 'zod-validation-error';
6
6
  import FlexSearch from 'flexsearch';
@@ -266,7 +266,7 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
266
266
  } else {
267
267
  console.error("Patched image URL does not start with /api/val/files: " + url);
268
268
  }
269
- } else if (remoteOptions && noParamsUrl.startsWith(remoteOptions.remoteHost)) ; else if (!noParamsUrl.startsWith("/public")) {
269
+ } else if (Internal.remote.splitRemoteRef(noParamsUrl).status === "success") ; else if (!noParamsUrl.startsWith("/public")) {
270
270
  noParamsUrl = "/public".concat(noParamsUrl);
271
271
  } else {
272
272
  console.error("Unpatched image URL starts with /public: " + url);
@@ -1047,6 +1047,10 @@ var PatchBlock = z.object({
1047
1047
  parentRef: ParentRef$1
1048
1048
  });
1049
1049
 
1050
+ var SourcePath = z.string().transform(function (path) {
1051
+ return path;
1052
+ });
1053
+
1050
1054
  function _objectWithoutPropertiesLoose(r, e) {
1051
1055
  if (null == r) return {};
1052
1056
  var t = {};
@@ -1069,10 +1073,6 @@ function _objectWithoutProperties(e, t) {
1069
1073
  return i;
1070
1074
  }
1071
1075
 
1072
- var SourcePath = z.string().transform(function (path) {
1073
- return path;
1074
- });
1075
-
1076
1076
  var ModuleFilePath$1 = z.string().transform(function (path) {
1077
1077
  return path;
1078
1078
  });
@@ -1189,6 +1189,8 @@ var SerializedDiscriminatedUnionSchema = z.lazy(function () {
1189
1189
  var SerializedEnumSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1190
1190
  type: z.literal("enum"),
1191
1191
  values: z.array(z.string()),
1192
+ locales: z.record(z.string(), z.string()).optional(),
1193
+ nullLocale: z.string().optional(),
1192
1194
  opt: z["boolean"]()
1193
1195
  }));
1194
1196
  var ImageEncodeOption = z.union([z.literal(false), z.object({
@@ -1268,6 +1270,7 @@ var SerializedRecordSchema = z.lazy(function () {
1268
1270
  render: FieldRender,
1269
1271
  type: z.literal("record"),
1270
1272
  item: SerializedSchema,
1273
+ params: z.record(z.string(), SerializedSchema).optional(),
1271
1274
  opt: z["boolean"](),
1272
1275
  // Optional gallery marker for files/images/videos
1273
1276
  mediaType: z.union([z.literal("files"), z.literal("images"), z.literal("videos")]).optional(),
@@ -1611,7 +1614,7 @@ var ValConfig = z.object({
1611
1614
  gitCommit: z.string().optional(),
1612
1615
  gitBranch: z.string().optional()
1613
1616
  });
1614
- var ValidationFixZ = z.union([z.literal("image:add-metadata"), z.literal("image:check-metadata"), z.literal("image:check-remote"), z.literal("image:upload-remote"), z.literal("image:download-remote"), z.literal("images:check-remote"), z.literal("images:upload-remote"), z.literal("file:add-metadata"), z.literal("video:add-metadata"), z.literal("video:upload-remote"), z.literal("video:download-remote"), z.literal("videos:add-metadata"), z.literal("videos:check-remote"), z.literal("videos:upload-remote"), z.literal("file:check-metadata"), z.literal("file:check-remote"), z.literal("file:upload-remote"), z.literal("file:download-remote"), z.literal("files:check-remote"), z.literal("files:upload-remote"), z.literal("keyof:check-keys"), z.literal("image:check-gallery"), z.literal("file:check-gallery"), z.literal("video:check-gallery"), z.literal("router:check-route"), z.literal("locale:check-locale"), z.literal("record:fill-keys"), z.literal("images:check-unique-folder"), z.literal("files:check-unique-folder"), z.literal("images:check-all-files"), z.literal("files:check-all-files"), z.literal("videos:check-unique-folder"), z.literal("videos:check-all-files"), z.literal("jsonValues:extract-entry"), z.literal("view:check-module")]);
1617
+ var ValidationFixZ = z.union([z.literal("image:add-metadata"), z.literal("image:check-metadata"), z.literal("image:check-remote"), z.literal("image:upload-remote"), z.literal("image:download-remote"), z.literal("images:check-remote"), z.literal("images:upload-remote"), z.literal("file:add-metadata"), z.literal("video:add-metadata"), z.literal("video:upload-remote"), z.literal("video:download-remote"), z.literal("videos:add-metadata"), z.literal("videos:check-remote"), z.literal("videos:upload-remote"), z.literal("file:check-metadata"), z.literal("file:check-remote"), z.literal("file:upload-remote"), z.literal("file:download-remote"), z.literal("files:check-remote"), z.literal("files:upload-remote"), z.literal("keyof:check-keys"), z.literal("image:check-gallery"), z.literal("file:check-gallery"), z.literal("video:check-gallery"), z.literal("router:check-route"), z.literal("locale:check-locale"), z.literal("router:check-locale-param"), z.literal("record:fill-keys"), z.literal("images:check-unique-folder"), z.literal("files:check-unique-folder"), z.literal("images:check-all-files"), z.literal("files:check-all-files"), z.literal("videos:check-unique-folder"), z.literal("videos:check-all-files"), z.literal("jsonValues:extract-entry"), z.literal("view:check-module"), z.literal("external:upload"), z.literal("remote:rewrite-host")]);
1615
1618
  var ValidationError = z.object({
1616
1619
  message: z.string(),
1617
1620
  value: z.unknown().optional(),
@@ -2013,7 +2016,13 @@ var Api = {
2013
2016
  coreVersion: z.string(),
2014
2017
  remoteFileBuckets: z.array(z.object({
2015
2018
  bucket: z.string()
2016
- }))
2019
+ })),
2020
+ /**
2021
+ * The host new refs name, from the project's settings: its own
2022
+ * address for a Hosted project whose files are in R2. Optional,
2023
+ * for a content service that predates it.
2024
+ */
2025
+ remoteHost: z.string().optional()
2017
2026
  })
2018
2027
  }), z.object({
2019
2028
  status: z.literal(400),
@@ -2759,6 +2768,105 @@ var Api = {
2759
2768
  })])
2760
2769
  }
2761
2770
  },
2771
+ /**
2772
+ * The Studio's full check, one module per request: its sources against its
2773
+ * schema and its local files against their bytes, on the content the caller
2774
+ * is looking at. See `docs/plans/studio-validate.md`.
2775
+ */
2776
+ "/validate": {
2777
+ POST: {
2778
+ req: {
2779
+ body: z.object({
2780
+ moduleFilePath: ModuleFilePath,
2781
+ /**
2782
+ * The patches the caller is showing, applied over base. Omitted
2783
+ * means every pending patch, as `/sources/~`; empty means base.
2784
+ */
2785
+ patchIds: z.array(PatchId).optional()
2786
+ }),
2787
+ cookies: {
2788
+ val_session: z.string().optional()
2789
+ }
2790
+ },
2791
+ res: z.union([unauthorizedResponse, notFoundResponse, z.object({
2792
+ status: z.literal(500),
2793
+ json: z.object({
2794
+ message: z.string()
2795
+ })
2796
+ }), /** No full check on this server (one running in a browser tab). */
2797
+ z.object({
2798
+ status: z.literal(501),
2799
+ json: z.object({
2800
+ message: z.string()
2801
+ })
2802
+ }), z.object({
2803
+ status: z.literal(200),
2804
+ json: z.object({
2805
+ moduleFilePath: ModuleFilePath,
2806
+ errors: z.record(z.string(), z.array(ValidationError)),
2807
+ /** Set when the module's source could not be read at all. */
2808
+ invalidSource: z.string().optional(),
2809
+ /**
2810
+ * Hosts that did not serve this module's remote files: one entry
2811
+ * per host, however many files it holds. Optional so a server
2812
+ * from before remote checks still parses.
2813
+ */
2814
+ remoteHosts: z.array(z.object({
2815
+ host: z.string(),
2816
+ status: z.union([z.literal("missing"), z.literal("unreachable")]),
2817
+ details: z.string(),
2818
+ sourcePaths: z.array(SourcePath)
2819
+ })).optional()
2820
+ })
2821
+ })])
2822
+ }
2823
+ },
2824
+ /**
2825
+ * The patch that fixes one error the full check reported. The server finds
2826
+ * the error again rather than trusting the caller's copy, and builds only the
2827
+ * fixes `STUDIO_FIXES` lists. The patch is returned, not applied: the Studio
2828
+ * adds it as a pending change like any edit.
2829
+ */
2830
+ "/validate/fix": {
2831
+ POST: {
2832
+ req: {
2833
+ body: z.object({
2834
+ moduleFilePath: ModuleFilePath,
2835
+ sourcePath: SourcePath,
2836
+ fix: ValidationFixZ,
2837
+ patchIds: z.array(PatchId).optional()
2838
+ }),
2839
+ cookies: {
2840
+ val_session: z.string().optional()
2841
+ }
2842
+ },
2843
+ res: z.union([unauthorizedResponse, notFoundResponse, z.object({
2844
+ status: z.literal(400),
2845
+ json: z.object({
2846
+ message: z.string()
2847
+ })
2848
+ }), z.object({
2849
+ status: z.literal(500),
2850
+ json: z.object({
2851
+ message: z.string()
2852
+ })
2853
+ }), /** No full check on this server (one running in a browser tab). */
2854
+ z.object({
2855
+ status: z.literal(501),
2856
+ json: z.object({
2857
+ message: z.string()
2858
+ })
2859
+ }), z.object({
2860
+ status: z.literal(200),
2861
+ json: z.object({
2862
+ patch: Patch,
2863
+ remainingErrors: z.array(ValidationError.extend({
2864
+ sourcePath: z.string().optional()
2865
+ }))
2866
+ })
2867
+ })])
2868
+ }
2869
+ },
2762
2870
  "/publish-job-prepare": {
2763
2871
  POST: {
2764
2872
  req: {
@@ -3738,8 +3846,11 @@ function detailsOf(json) {
3738
3846
  }
3739
3847
  var DEFAULT_ERROR_MESSAGE = "Unknown error with unknown message from server";
3740
3848
 
3741
- var createValClient = function createValClient(host, config // We want to use this in the future
3742
- ) {
3849
+ var createValClient = function createValClient(host, config, options) {
3850
+ var _options$fetch;
3851
+ var send = (_options$fetch = options === null || options === void 0 ? void 0 : options.fetch) !== null && _options$fetch !== void 0 ? _options$fetch : function (input, init) {
3852
+ return fetch(input, init);
3853
+ };
3743
3854
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3744
3855
  var anyApi = Api;
3745
3856
  return /*#__PURE__*/function () {
@@ -3830,7 +3941,7 @@ var createValClient = function createValClient(host, config // We want to use th
3830
3941
  case 2:
3831
3942
  _context2.p = 2;
3832
3943
  _context2.n = 3;
3833
- return fetch("".concat(host).concat(fullPath), {
3944
+ return send("".concat(host).concat(fullPath), {
3834
3945
  method: method,
3835
3946
  headers: {
3836
3947
  "Content-Type": "application/json"
@@ -3964,6 +4075,29 @@ function isRetryable(error) {
3964
4075
  return false;
3965
4076
  }
3966
4077
 
4078
+ /**
4079
+ * A Val demo, as the page hands it to the Studio.
4080
+ *
4081
+ * In a demo the Studio talks to Val's own server running in the same tab
4082
+ * (`@valbuild/server/browser`) instead of to an API route, and the page that
4083
+ * starts that server is the app's (`<ValApp demo>`) while the Studio is a
4084
+ * separate bundle loaded by a script tag. A window property is how the two
4085
+ * meet, the same way `window.__VAL_MODULES__` hands the Studio the project's
4086
+ * modules. See `docs/plans/demo-mode.md`.
4087
+ */
4088
+
4089
+ /*
4090
+ * A global `var` rather than a `Window` member: this package is compiled
4091
+ * without the DOM's types, and a `var` in global scope is also a property of
4092
+ * `window` where they exist.
4093
+ */
4094
+
4095
+ /** The demo this page was started with, or null outside a demo. */
4096
+ function getValDemo() {
4097
+ var _globalThis$__VAL_DEM;
4098
+ return (_globalThis$__VAL_DEM = globalThis.__VAL_DEMO__) !== null && _globalThis$__VAL_DEM !== void 0 ? _globalThis$__VAL_DEM : null;
4099
+ }
4100
+
3967
4101
  function _arrayWithHoles(r) {
3968
4102
  if (Array.isArray(r)) return r;
3969
4103
  }
@@ -4843,7 +4977,7 @@ function declaredLocales(snapshot) {
4843
4977
  * these has to resolve all of them, and the two that leaked did so by adding a
4844
4978
  * fix here and not to a condition somewhere else.
4845
4979
  */
4846
- var SCHEMA_SOURCE_FIXES = ["keyof:check-keys", "image:check-gallery", "file:check-gallery", "video:check-gallery", "router:check-route", "locale:check-locale", "record:fill-keys"];
4980
+ var SCHEMA_SOURCE_FIXES = ["keyof:check-keys", "image:check-gallery", "file:check-gallery", "video:check-gallery", "router:check-route", "locale:check-locale", "router:check-locale-param", "record:fill-keys"];
4847
4981
 
4848
4982
  /** Whether an error is a marker only the whole project can answer. */
4849
4983
  function isSchemaSourceFixError(error) {
@@ -5033,21 +5167,51 @@ function resolveSchemaSourceFixForError(error, snapshot) {
5033
5167
  })
5034
5168
  };
5035
5169
  }
5036
- if (fixes.includes("record:fill-keys")) {
5170
+ if (fixes.includes("router:check-locale-param")) {
5037
5171
  var _value = error.value;
5038
- if (_typeof(_value) !== "object" || _value === null) {
5172
+ var _ref7 = _typeof(_value) === "object" && _value !== null ? _value : {},
5173
+ name = _ref7.name,
5174
+ segment = _ref7.segment;
5175
+ if (typeof name !== "string" || typeof segment !== "string") {
5176
+ return {
5177
+ status: "remaining",
5178
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
5179
+ message: "Expected route locale parameter validation error to have a 'value' with 'name' and 'segment'. ".concat(TYPE_ERROR_MESSAGE),
5180
+ typeError: true,
5181
+ fixes: undefined
5182
+ })
5183
+ };
5184
+ }
5185
+ // A project that declares no languages is not translated, and a
5186
+ // `[locale]` segment there is the app's own business.
5187
+ if (declaredLocales(snapshot).length === 0) {
5188
+ return {
5189
+ status: "resolved"
5190
+ };
5191
+ }
5192
+ return {
5193
+ status: "remaining",
5194
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
5195
+ message: "This route has a '".concat(name, "' segment (").concat(segment, "), but nothing says which of the project's languages it can be, so its pages are not in any language and '/xx/\u2026' validates. Give the parameter a locale schema: s.router(router, { ").concat(name, ": urlLocale, \u2026 }, item), where urlLocale is an s.enum(\u2026).locales({ \u2026 }) or s.locale(). If '").concat(name, "' is not a language here, give it a schema of its own instead, e.g. { ").concat(name, ": s.enum(\u2026) }."),
5196
+ fixes: undefined
5197
+ })
5198
+ };
5199
+ }
5200
+ if (fixes.includes("record:fill-keys")) {
5201
+ var _value2 = error.value;
5202
+ if (_typeof(_value2) !== "object" || _value2 === null) {
5039
5203
  return {
5040
5204
  status: "remaining",
5041
5205
  error: _objectSpread2(_objectSpread2({}, error), {}, {
5042
- message: "Expected record key validation error to have a 'value' object. Found: ".concat(_typeof(_value), ". ").concat(TYPE_ERROR_MESSAGE),
5206
+ message: "Expected record key validation error to have a 'value' object. Found: ".concat(_typeof(_value2), ". ").concat(TYPE_ERROR_MESSAGE),
5043
5207
  typeError: true,
5044
5208
  fixes: undefined
5045
5209
  })
5046
5210
  };
5047
5211
  }
5048
- var _ref7 = _value,
5049
- present = _ref7.present,
5050
- declared = _ref7.declared;
5212
+ var _ref8 = _value2,
5213
+ present = _ref8.present,
5214
+ declared = _ref8.declared;
5051
5215
  if (!Array.isArray(present)) {
5052
5216
  return {
5053
5217
  status: "remaining",
@@ -5199,7 +5363,8 @@ function resolveSchemaSourceFixes(validationErrors, snapshot) {
5199
5363
  *
5200
5364
  * THE question the whole feature turns on. A locale scope is a subtree in one
5201
5365
  * language, and three things open one: a `locale` field on an object, a record
5202
- * keyed by `s.locale()`, and a locale segment in a segmented key. Once this can
5366
+ * keyed by `s.locale()`, and a locale parameter in a router's key
5367
+ * (`s.router(router, { locale: … }, item)`, where the URL names the language). Once this can
5203
5368
  * be answered from the schema and the source alone, everything downstream falls
5204
5369
  * out of it — the Studio's locale filter, deep links, `<html lang>`, and knowing
5205
5370
  * what to translate from and into.
@@ -5228,7 +5393,79 @@ function localeAt(path, snapshot) {
5228
5393
  return null;
5229
5394
  }
5230
5395
  var segments = modulePath ? Internal.splitModulePath(modulePath) : [];
5231
- return walk(schema, source, segments, available);
5396
+ return walk(schema, source, segments, available, moduleFilePath);
5397
+ }
5398
+
5399
+ /**
5400
+ * The language a locale schema's value MEANS, or `null` if it means none of
5401
+ * the project's.
5402
+ *
5403
+ * `s.locale()` stores the tag itself. An enum with `.locales(...)` stores a
5404
+ * value that stands for one — `nb` for `nb-NO` — and, on a router parameter,
5405
+ * `null` for a URL that left the segment out, which `{ null: tag }` names.
5406
+ * Anything else is not a locale and means nothing here.
5407
+ *
5408
+ * Callers reading a FIELD pass only strings: a field that holds `null` has no
5409
+ * language, whatever its schema says about URLs.
5410
+ */
5411
+ function localeMeantBy(schema, value, available) {
5412
+ if (schema.type === "locale") {
5413
+ return value === null ? null : localeOfValue(value, available);
5414
+ }
5415
+ if (schema.type === "enum" && schema.locales !== undefined) {
5416
+ var tag = enumLocaleTag(schema, value);
5417
+ return tag === undefined ? null : localeOfValue(tag, available);
5418
+ }
5419
+ return null;
5420
+ }
5421
+
5422
+ /**
5423
+ * The tag an enum's `.locales(...)` maps a value to, as written — `null` is
5424
+ * the `{ null }` mapping — or `undefined` where it maps none.
5425
+ *
5426
+ * An own property: a plain object inherits `toString` and the rest, and an
5427
+ * enum value is allowed to be spelled like one.
5428
+ */
5429
+ function enumLocaleTag(schema, value) {
5430
+ if (value === null) {
5431
+ return schema.nullLocale;
5432
+ }
5433
+ return schema.locales !== undefined && Object.prototype.hasOwnProperty.call(schema.locales, value) ? schema.locales[value] : undefined;
5434
+ }
5435
+
5436
+ /**
5437
+ * Whether a schema is a locale: `s.locale()`, or an enum `.locales(...)` made
5438
+ * one.
5439
+ */
5440
+ function isLocaleSchema(schema) {
5441
+ return schema.type === "locale" || schema.type === "enum" && schema.locales !== undefined;
5442
+ }
5443
+
5444
+ /**
5445
+ * The language a router's key is in, where one of its route parameters is a
5446
+ * locale — `/nb/blog/hei` is `nb-NO` when `locale` maps `nb` to it.
5447
+ */
5448
+ function localeOfRouteKey(schema, moduleFilePath, key, available) {
5449
+ if (schema.params === undefined || schema.router === undefined) {
5450
+ return null;
5451
+ }
5452
+ var pattern = routePatternOf(schema.router, moduleFilePath);
5453
+ if (pattern === null) {
5454
+ return null;
5455
+ }
5456
+ var values = matchRoutePattern(key, pattern);
5457
+ if (values === null) {
5458
+ return null;
5459
+ }
5460
+ for (var _i = 0, _Object$entries = Object.entries(schema.params); _i < _Object$entries.length; _i++) {
5461
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
5462
+ name = _Object$entries$_i[0],
5463
+ param = _Object$entries$_i[1];
5464
+ if (isLocaleSchema(param) && Object.prototype.hasOwnProperty.call(values, name)) {
5465
+ return localeMeantBy(param, values[name], available);
5466
+ }
5467
+ }
5468
+ return null;
5232
5469
  }
5233
5470
 
5234
5471
  /**
@@ -5243,7 +5480,7 @@ function localeAt(path, snapshot) {
5243
5480
  * these can fire on a well-formed path; taking the innermost is what makes the
5244
5481
  * answer well-defined while a project is mid-fix.
5245
5482
  */
5246
- function walk(schema, source, segments, available) {
5483
+ function walk(schema, source, segments, available, moduleFilePath) {
5247
5484
  var entered = enter(schema, source, available);
5248
5485
  var locale = entered.locale;
5249
5486
  var currentSchema = entered.schema;
@@ -5257,14 +5494,21 @@ function walk(schema, source, segments, available) {
5257
5494
  return locale;
5258
5495
  }
5259
5496
  if (currentSchema.type === "record") {
5260
- var _currentSchema$key;
5261
- if (((_currentSchema$key = currentSchema.key) === null || _currentSchema$key === void 0 ? void 0 : _currentSchema$key.type) === "locale") {
5497
+ if (currentSchema.key !== undefined && isLocaleSchema(currentSchema.key)) {
5262
5498
  // In a locale-keyed record the KEY is the language, so the segment we
5263
- // are about to take is the answer.
5264
- var resolved = localeOfValue(segment, available);
5499
+ // are about to take is the answer — the tag itself for `s.locale()`,
5500
+ // the tag it stands for when the key is an enum with `.locales()`.
5501
+ var resolved = localeMeantBy(currentSchema.key, segment, available);
5265
5502
  if (resolved !== null) {
5266
5503
  locale = resolved;
5267
5504
  }
5505
+ } else if (currentSchema.params !== undefined) {
5506
+ // A router whose URL names the language: the key is a URL, and one of
5507
+ // its parameters is the answer.
5508
+ var _resolved = localeOfRouteKey(currentSchema, moduleFilePath, segment, available);
5509
+ if (_resolved !== null) {
5510
+ locale = _resolved;
5511
+ }
5268
5512
  }
5269
5513
  currentSchema = currentSchema.item;
5270
5514
  } else if (currentSchema.type === "array") {
@@ -5320,18 +5564,18 @@ function localeOfObjectField(schema, source, available) {
5320
5564
  if (schema.type !== "object" || !isJsonObject(source)) {
5321
5565
  return null;
5322
5566
  }
5323
- for (var _i = 0, _Object$entries = Object.entries(schema.items); _i < _Object$entries.length; _i++) {
5324
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
5325
- key = _Object$entries$_i[0],
5326
- item = _Object$entries$_i[1];
5327
- if (item.type !== "locale") {
5567
+ for (var _i2 = 0, _Object$entries2 = Object.entries(schema.items); _i2 < _Object$entries2.length; _i2++) {
5568
+ var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
5569
+ key = _Object$entries2$_i[0],
5570
+ item = _Object$entries2$_i[1];
5571
+ if (!isLocaleSchema(item)) {
5328
5572
  continue;
5329
5573
  }
5330
5574
  var value = source[key];
5331
5575
  if (typeof value !== "string") {
5332
5576
  return null;
5333
5577
  }
5334
- return localeOfValue(value, available);
5578
+ return localeMeantBy(item, value, available);
5335
5579
  }
5336
5580
  return null;
5337
5581
  }
@@ -6166,6 +6410,16 @@ function partitionValidationErrors(validationErrors) {
6166
6410
  skipped: skipped
6167
6411
  };
6168
6412
  }
6413
+
6414
+ /**
6415
+ * Whether the Studio leaves this error to the server, which repairs it on
6416
+ * save, rather than showing it and holding a publish back for it. One rule
6417
+ * for every gate: `val publish` asks the same question, so the CLI does not
6418
+ * refuse what the Studio would publish.
6419
+ */
6420
+ function isRepairedOnSave(error) {
6421
+ return isSkippedFix(error);
6422
+ }
6169
6423
  function isSkippedFix(error) {
6170
6424
  var _error$fixes;
6171
6425
  var fixes = (_error$fixes = error.fixes) !== null && _error$fixes !== void 0 ? _error$fixes : [];
@@ -6220,6 +6474,7 @@ function isSkippableFixCode(fix) {
6220
6474
  case "video:check-gallery":
6221
6475
  case "router:check-route":
6222
6476
  case "locale:check-locale":
6477
+ case "router:check-locale-param":
6223
6478
  case "record:fill-keys":
6224
6479
  case "jsonValues:extract-entry":
6225
6480
  // `jsonValues:extract-entry` is an entry inlined in the `.val.ts`. The
@@ -6234,6 +6489,12 @@ function isSkippableFixCode(fix) {
6234
6489
  // under `val external upload` and never under a blanket `--fix`. Surfacing
6235
6490
  // it is the whole point: someone has to decide.
6236
6491
  return false;
6492
+ case "remote:rewrite-host":
6493
+ // Reported only by the server's full check (the Studio's Validate page,
6494
+ // `val validate`), which asks the ref's host -- never by a schema, so
6495
+ // the Studio's own validator never holds one. Were it to, a person has
6496
+ // to see it: the host is gone, and nothing on save repoints the ref.
6497
+ return false;
6237
6498
  default:
6238
6499
  {
6239
6500
  return false;
@@ -6273,6 +6534,33 @@ function filterBlockingValidationErrors(validationErrors, schemas, sources) {
6273
6534
  return partitionValidationErrors(resolved).surfaced;
6274
6535
  }
6275
6536
 
6537
+ /**
6538
+ * The fixes the Studio's full check offers a **Fix** button for.
6539
+ *
6540
+ * Each is one `createFixPatch` can build without a disk: it reads a file's
6541
+ * bytes through `FixFiles`, or needs no bytes at all. The server refuses any
6542
+ * other code on `/validate/fix`, and the Studio shows those errors with the
6543
+ * command to run instead of a button — so a code is added here when its fix
6544
+ * can run in http mode and on the platform, not before. See
6545
+ * `docs/plans/studio-validate.md`, "Order of work".
6546
+ */
6547
+ var STUDIO_FIXES = ["image:add-metadata", "image:check-metadata", "file:add-metadata", "file:check-metadata", "video:add-metadata", "videos:add-metadata", "view:check-module",
6548
+ // A stale remote ref: re-read through `FixFiles.readRemoteFile`, rewritten
6549
+ // on the host it was already on.
6550
+ "image:check-remote", "file:check-remote",
6551
+ // One entry of an `s.imagedir()` / `s.filedir()`: removed when its file is
6552
+ // gone, rewritten from its bytes when they disagree. Built for that entry
6553
+ // alone, never the whole directory (`fixForStudio`).
6554
+ "images:check-all-files", "files:check-all-files",
6555
+ // A platform ref on a host that no longer serves its project, pointed at
6556
+ // the project's own `valstart.dev` address. Derived from the ref alone.
6557
+ "remote:rewrite-host"];
6558
+ function isStudioFix(fix) {
6559
+ return STUDIO_FIXES.some(function (studioFix) {
6560
+ return studioFix === fix;
6561
+ });
6562
+ }
6563
+
6276
6564
  /**
6277
6565
  * Every decision `encode` makes, with none of the pixels.
6278
6566
  *
@@ -6381,6 +6669,63 @@ function isSkippedSource(sourceMimeType, targetMimeType, needsDownscale) {
6381
6669
  return sourceMimeType === targetMimeType && !needsDownscale;
6382
6670
  }
6383
6671
 
6672
+ /**
6673
+ * The types the Studio can write with WebAssembly when the canvas cannot.
6674
+ *
6675
+ * libwebp, compiled to WebAssembly (`@jsquash/webp`, loaded by
6676
+ * `packages/ui/spa/utils/webpWasm.ts`). One entry because `encode` has one
6677
+ * `type`; a second type is a second encoder to host, not a second string here.
6678
+ */
6679
+ var WASM_ENCODED_MIME_TYPES = ["image/webp"];
6680
+
6681
+ /**
6682
+ * Whether to encode the canvas's pixels with WebAssembly, given what the
6683
+ * canvas's own encoder handed back.
6684
+ *
6685
+ * `canvas.toBlob` answers a type it cannot write with a PNG instead of an error,
6686
+ * and Safari -- the browser people take their photos in -- cannot write WebP.
6687
+ * The pixels on that canvas are already decoded, oriented and scaled, so the
6688
+ * only thing missing is the encoder, and libwebp in WebAssembly is that.
6689
+ *
6690
+ * Only then, and that is what keeps the WebAssembly lazy: a browser whose
6691
+ * canvas wrote the type is never asked to download an encoder, even when its
6692
+ * WebP then loses to the original on size -- a second encoder of the same
6693
+ * format would lose for the same reason.
6694
+ *
6695
+ * `null` -- no blob at all -- is not "cannot write this type" either. It is a
6696
+ * canvas that failed to serialize anything, under memory pressure for instance,
6697
+ * and says nothing about WebP: a Chromium tab that hits it is still a browser
6698
+ * that writes WebP, and must not start downloading an encoder because of it.
6699
+ */
6700
+ function shouldEncodeWithWasm(input) {
6701
+ return WASM_ENCODED_MIME_TYPES.includes(input.targetMimeType) && input.canvasOutputType !== null && input.canvasOutputType !== input.targetMimeType;
6702
+ }
6703
+
6704
+ /**
6705
+ * What to scale an image down to when nothing could write the type asked for:
6706
+ * the source's own format, when it is one every canvas can write.
6707
+ *
6708
+ * The second fallback, after {@link shouldEncodeWithWasm}: it is what a browser
6709
+ * whose canvas cannot write WebP gets when the WebAssembly encoder could not be
6710
+ * loaded either (offline, a blocked host, a failed instantiation). Without it,
6711
+ * `chooseEncoded` would rightly refuse the canvas's PNG and upload the original
6712
+ * -- every photo at the camera's full size, the one case `maxWidth` exists
6713
+ * for. Scaling it down as a JPEG keeps the half of `encode` this browser can do.
6714
+ *
6715
+ * Only when a downscale is needed: re-encoding a JPEG that already fits, as a
6716
+ * JPEG, is a second generation of loss for a few percent. And never into a
6717
+ * type `accept` refuses, for the reason `isMimeTypeAccepted` gives.
6718
+ */
6719
+ function fallbackMimeType(sourceMimeType, needsDownscale, accept) {
6720
+ if (!needsDownscale) {
6721
+ return null;
6722
+ }
6723
+ if (sourceMimeType !== "image/jpeg" && sourceMimeType !== "image/png") {
6724
+ return null;
6725
+ }
6726
+ return isMimeTypeAccepted(sourceMimeType, accept) ? sourceMimeType : null;
6727
+ }
6728
+
6384
6729
  /**
6385
6730
  * Whether to upload what the encoder produced, or the file that was picked.
6386
6731
  *
@@ -8570,4 +8915,4 @@ function schemaErrorsOf(schema, moduleFilePath, source) {
8570
8915
  return errors;
8571
8916
  }
8572
8917
 
8573
- export { AA_LARGE, AA_TEXT, Api, BRAND_CONTRAST_PAIRS, BinaryFileRef, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, ENCODE_DEFAULTS, ENCODE_MIME_TYPE_OF, FileOptions, HistoricalCommit, HistoricalModule, HistoricalPatchSet, HistoryError, ImageEncodeOption, ImageOptions, JSONValue, JSON_ENTRIES_BATCH_MAX, ModuleComparison, NEVER_ENCODED, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchId, PatchJSON, PatchSets, PlanRefusalError, PublishProtocolError, RAMP_STEPS, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SCHEMA_SOURCE_FIXES, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedLocaleSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SerializedValViewSchema, SerializedVideoSchema, SharedValConfig, VAL_ENABLE_COOKIE_NAME, VAL_GREEN_RAMP, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_STUDIO_HEADER, ValCommit, ValDeployment, VideoOptions, VideoStreamOption, accentRamp, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, chainHeadOf, checkCompatibility, chooseEncoded, concatModulePath, contrastRatio, createRegExpFromPattern, createSearchIndex, createValClient, declaredLocales, describeContainerAtPath, emptyOf, explainIncompatible, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, forEachRichTextImage, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, indexModule, isInsidePatchSetPath, isMimeTypeAccepted, isRemoteSchema, isSchemaSourceFixError, isSkippedSource, joinSharedChanges, keyOfRecordPath, levenshtein, localeAt, memoizePerRequest, newestCommitSha, parseArtifacts, parseBuildTarget, parseCancel, parseDeclare, parseDiscard, parseJob, parseNewestCiRun, parsePress, parseProblems, parseProjectSource, parsePromote, parsePublishToken, parseRenew, parseRequestStatus, parseStatus, parseUpdateTarget, parseVerify, partitionValidationErrors, performSearch, relativeLuminance, remirrorToRichTextSource, removeModule, resolveEncodeSettings, resolvePairGround, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextImageSchema, richTextToRemirror, safeParsePatch, schemaErrorsOf, schemaTypesOfPath, selectionClosure, sideFiles, sideSource, sourcePathOfChild, themeCustomProperties, toolNames, traverseSchemaSource, urlOf, validateRoutePatterns, withExtension };
8918
+ export { AA_LARGE, AA_TEXT, Api, BRAND_CONTRAST_PAIRS, BinaryFileRef, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, ENCODE_DEFAULTS, ENCODE_MIME_TYPE_OF, FileOptions, HistoricalCommit, HistoricalModule, HistoricalPatchSet, HistoryError, ImageEncodeOption, ImageOptions, JSONValue, JSON_ENTRIES_BATCH_MAX, ModuleComparison, NEVER_ENCODED, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchId, PatchJSON, PatchSets, PlanRefusalError, PublishProtocolError, RAMP_STEPS, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SCHEMA_SOURCE_FIXES, STUDIO_FIXES, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedLocaleSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SerializedValViewSchema, SerializedVideoSchema, SharedValConfig, VAL_ENABLE_COOKIE_NAME, VAL_GREEN_RAMP, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_STUDIO_HEADER, ValCommit, ValDeployment, VideoOptions, VideoStreamOption, WASM_ENCODED_MIME_TYPES, accentRamp, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, chainHeadOf, checkCompatibility, chooseEncoded, concatModulePath, contrastRatio, createRegExpFromPattern, createSearchIndex, createValClient, declaredLocales, describeContainerAtPath, emptyOf, enumLocaleTag, explainIncompatible, fallbackMimeType, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, forEachRichTextImage, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, getValDemo, indexModule, isInsidePatchSetPath, isLocaleSchema, isMimeTypeAccepted, isRemoteSchema, isRepairedOnSave, isSchemaSourceFixError, isSkippedSource, isStudioFix, joinSharedChanges, keyOfRecordPath, levenshtein, localeAt, localeMeantBy, memoizePerRequest, newestCommitSha, parseArtifacts, parseBuildTarget, parseCancel, parseDeclare, parseDiscard, parseJob, parseNewestCiRun, parsePress, parseProblems, parseProjectSource, parsePromote, parsePublishToken, parseRenew, parseRequestStatus, parseStatus, parseUpdateTarget, parseVerify, partitionValidationErrors, performSearch, relativeLuminance, remirrorToRichTextSource, removeModule, resolveEncodeSettings, resolvePairGround, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextImageSchema, richTextToRemirror, safeParsePatch, schemaErrorsOf, schemaTypesOfPath, selectionClosure, shouldEncodeWithWasm, sideFiles, sideSource, sourcePathOfChild, themeCustomProperties, toolNames, traverseSchemaSource, urlOf, validateRoutePatterns, withExtension };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valbuild/shared",
3
- "version": "0.146.4",
3
+ "version": "0.148.0",
4
4
  "private": false,
5
5
  "description": "Val shared types and utilities",
6
6
  "repository": {
@@ -37,7 +37,7 @@
37
37
  "flexsearch": "^0.8.212",
38
38
  "zod": "^4.4.3",
39
39
  "zod-validation-error": "^5.0.0",
40
- "@valbuild/core": "0.146.2"
40
+ "@valbuild/core": "0.148.0"
41
41
  },
42
42
  "files": [
43
43
  "CHANGELOG.md",