@valbuild/shared 0.126.0 → 0.128.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 { Internal, CODE_LANGUAGES, DEFAULT_COLOR_FORMAT } from '@valbuild/core';
2
- import { _ as _createForOfIteratorHelper, n as _objectSpread2, o as _arrayLikeToArray, q as _unsupportedIterableToArray, r as _defineProperty, s as _typeof } from '../../dist/getSitemapTree-a6cb37cb.esm.js';
3
- export { 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, g as getNextAppRouterSourceFolder, l as getPageRouterSitemapTree, j as getPageRouterSourceFolder, m as getPatternFromModuleFilePath, h as getTanStackRouterSourceFolder, k as isTanStackRoutesFolder, f as isValCanvasFrame, i as isValCanvasPageMessage, e as isValCanvasStudioMessage, p as parseRoutePattern, w as withValCanvasParam } from '../../dist/getSitemapTree-a6cb37cb.esm.js';
1
+ import { Internal, CODE_LANGUAGES, resolveSettingsModule, missingDeclaredKeys, localeOfValue, discriminatedUnionBranchOf, DEFAULT_COLOR_FORMAT, declaredKeySetOf } from '@valbuild/core';
2
+ import { _ as _createForOfIteratorHelper, n as _objectSpread2, o as _arrayLikeToArray, q as _unsupportedIterableToArray, r as _typeof, s as _defineProperty } from '../../dist/getSitemapTree-6777bdfa.esm.js';
3
+ export { 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, g as getNextAppRouterSourceFolder, l as getPageRouterSitemapTree, j as getPageRouterSourceFolder, m as getPatternFromModuleFilePath, h as getTanStackRouterSourceFolder, k as isTanStackRoutesFolder, f as isValCanvasFrame, i as isValCanvasPageMessage, e as isValCanvasStudioMessage, p as parseRoutePattern, w as withValCanvasParam } from '../../dist/getSitemapTree-6777bdfa.esm.js';
4
4
  import { z } from 'zod';
5
5
  import { fromError } from 'zod-validation-error';
6
6
  import FlexSearch from 'flexsearch';
@@ -890,10 +890,34 @@ var PatchBlock = z.object({
890
890
  parentRef: ParentRef$1
891
891
  });
892
892
 
893
+ function _objectWithoutPropertiesLoose(r, e) {
894
+ if (null == r) return {};
895
+ var t = {};
896
+ for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
897
+ if (-1 !== e.indexOf(n)) continue;
898
+ t[n] = r[n];
899
+ }
900
+ return t;
901
+ }
902
+
903
+ function _objectWithoutProperties(e, t) {
904
+ if (null == e) return {};
905
+ var o,
906
+ r,
907
+ i = _objectWithoutPropertiesLoose(e, t);
908
+ if (Object.getOwnPropertySymbols) {
909
+ var n = Object.getOwnPropertySymbols(e);
910
+ for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
911
+ }
912
+ return i;
913
+ }
914
+
893
915
  var SourcePath = z.string().transform(function (path) {
894
916
  return path;
895
917
  });
896
918
 
919
+ var _excluded = ["directory"];
920
+
897
921
  // A render is static config, so unlike a `preview` it travels WHOLE — this
898
922
  // is the field the editor reads the layout from. See `core/src/render.ts`.
899
923
  // Every field can carry `{ as: "inline" }`, and that is all a render says.
@@ -1001,11 +1025,35 @@ var ImageEncodeOption = z.union([z.literal(false), z.object({
1001
1025
  maxWidth: z.number().optional(),
1002
1026
  maxHeight: z.number().optional()
1003
1027
  })]);
1004
- var ImageOptions = z.object({
1005
- directory: z.string().optional(),
1028
+ /**
1029
+ * Accept the pre-rename `directory` key and read it as `dir`.
1030
+ *
1031
+ * Serialized schemas are not only computed from live code: a commit record
1032
+ * stores the schema each module was under (`StoredModuleVersion`), and the
1033
+ * history view parses those back. Commits made before `directory` became `dir`
1034
+ * therefore still carry the old key, and dropping it would show a historical
1035
+ * gallery with no directory at all — silently, since the rest of the schema
1036
+ * parses fine.
1037
+ *
1038
+ * Only fills `dir` when it is absent, so a schema carrying both (which should
1039
+ * not happen) keeps the current key.
1040
+ */
1041
+ var readLegacyDirectoryAsDir = function readLegacyDirectoryAsDir(value) {
1042
+ if (!value || _typeof(value) !== "object" || Array.isArray(value) || !("directory" in value) || "dir" in value) {
1043
+ return value;
1044
+ }
1045
+ var _ref = value,
1046
+ directory = _ref.directory,
1047
+ rest = _objectWithoutProperties(_ref, _excluded);
1048
+ return _objectSpread2(_objectSpread2({}, rest), {}, {
1049
+ dir: directory
1050
+ });
1051
+ };
1052
+ var ImageOptions = z.preprocess(readLegacyDirectoryAsDir, z.object({
1053
+ dir: z.string().optional(),
1006
1054
  accept: z.string().optional(),
1007
1055
  encode: ImageEncodeOption.optional()
1008
- });
1056
+ }));
1009
1057
  var SerializedImageSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1010
1058
  type: z.literal("image"),
1011
1059
  options: ImageOptions.optional(),
@@ -1044,7 +1092,7 @@ var SerializedRichTextSchema = z.object(_objectSpread2(_objectSpread2({}, common
1044
1092
  opt: z["boolean"]()
1045
1093
  }));
1046
1094
  var SerializedRecordSchema = z.lazy(function () {
1047
- return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1095
+ return z.preprocess(readLegacyDirectoryAsDir, z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1048
1096
  type: z.literal("record"),
1049
1097
  item: SerializedSchema,
1050
1098
  opt: z["boolean"](),
@@ -1052,12 +1100,12 @@ var SerializedRecordSchema = z.lazy(function () {
1052
1100
  mediaType: z.union([z.literal("files"), z.literal("images")]).optional(),
1053
1101
  // Optional legacy gallery metadata
1054
1102
  accept: z.string().optional(),
1055
- directory: z.string().optional(),
1103
+ dir: z.string().optional(),
1056
1104
  remote: z["boolean"]().optional(),
1057
1105
  encode: ImageEncodeOption.optional(),
1058
1106
  alt: SerializedSchema.optional(),
1059
1107
  moduleMetadata: z.record(z.string(), z.record(z.string(), z.any())).optional()
1060
- })).passthrough();
1108
+ })).passthrough());
1061
1109
  });
1062
1110
  var SerializedKeyOfSchema = z.lazy(function () {
1063
1111
  return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
@@ -1118,6 +1166,16 @@ var SerializedCodeSchema = z.object(_objectSpread2(_objectSpread2({}, commonSche
1118
1166
  options: CodeOptions.optional(),
1119
1167
  opt: z["boolean"]()
1120
1168
  }));
1169
+ var SerializedLocaleSchema = z.object({
1170
+ type: z.literal("locale"),
1171
+ render: FieldRender,
1172
+ preview: z.literal(true).optional(),
1173
+ opt: z["boolean"](),
1174
+ customValidate: z["boolean"]().optional(),
1175
+ readonly: z["boolean"]().optional(),
1176
+ hidden: z["boolean"]().optional(),
1177
+ description: z.string().optional()
1178
+ });
1121
1179
  var SerializedRouteSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1122
1180
  type: z.literal("route"),
1123
1181
  options: z.object({
@@ -1143,10 +1201,15 @@ var SerializedSettingsSchema = z.lazy(function () {
1143
1201
  return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1144
1202
  type: z.literal("settings"),
1145
1203
  items: z.record(z.string(), SerializedSchema),
1146
- opt: z["boolean"]()
1204
+ opt: z["boolean"](),
1205
+ // Which section this is, where Val has rules about it beyond its keys.
1206
+ // Dropping it would leave the Studio's validation worker running every
1207
+ // field's own rules and none of the cross-field ones — see
1208
+ // `sectionValidators` in core/src/schema/settings.ts.
1209
+ section: z.string().optional()
1147
1210
  }));
1148
1211
  });
1149
- var SerializedSchema = z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
1212
+ var SerializedSchema = z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedLocaleSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
1150
1213
 
1151
1214
  var ValCommit = z.object({
1152
1215
  commitSha: z.string(),
@@ -1322,7 +1385,7 @@ var ValConfig = z.object({
1322
1385
  gitCommit: z.string().optional(),
1323
1386
  gitBranch: z.string().optional()
1324
1387
  });
1325
- 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("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("router:check-route"), 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("jsonValues:extract-entry")]);
1388
+ 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("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("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("jsonValues:extract-entry")]);
1326
1389
  var ValidationError = z.object({
1327
1390
  message: z.string(),
1328
1391
  value: z.unknown().optional(),
@@ -3229,7 +3292,7 @@ var compatible = {
3229
3292
  * `readonly Json[]` — so the check that reads naturally does not type. This
3230
3293
  * predicate is that check, said once.
3231
3294
  */
3232
- function isJsonObject(value) {
3295
+ function isJsonObject$1(value) {
3233
3296
  return _typeof(value) === "object" && value !== null && !Array.isArray(value);
3234
3297
  }
3235
3298
  function incompatible(reason) {
@@ -3286,7 +3349,7 @@ function variantOf(schema, value) {
3286
3349
  return undefined;
3287
3350
  }
3288
3351
  var discriminator = schema.key;
3289
- if (!isJsonObject(value)) {
3352
+ if (!isJsonObject$1(value)) {
3290
3353
  return undefined;
3291
3354
  }
3292
3355
  var tag = value[discriminator];
@@ -3414,7 +3477,7 @@ function checkCompatibility(from, to) {
3414
3477
  }
3415
3478
  case "object":
3416
3479
  {
3417
- if (from.schema.type !== "object" || !isJsonObject(from.value)) {
3480
+ if (from.schema.type !== "object" || !isJsonObject$1(from.value)) {
3418
3481
  return {
3419
3482
  status: "unknown",
3420
3483
  message: "The old value is not shaped like an object."
@@ -3517,7 +3580,7 @@ function checkCompatibility(from, to) {
3517
3580
  }
3518
3581
  case "record":
3519
3582
  {
3520
- if (from.schema.type !== "record" || !isJsonObject(from.value)) {
3583
+ if (from.schema.type !== "record" || !isJsonObject$1(from.value)) {
3521
3584
  return {
3522
3585
  status: "unknown",
3523
3586
  message: "The old value is not a record."
@@ -3953,6 +4016,59 @@ function checkRouteIsValid(route, includePattern, excludePattern, snapshot) {
3953
4016
  error: false
3954
4017
  };
3955
4018
  }
4019
+
4020
+ /**
4021
+ * The languages the project declares, from its settings module.
4022
+ *
4023
+ * Empty where there is no settings module, no `locales` section, or nothing in
4024
+ * it — all of which mean the same thing: this project has not said it is
4025
+ * translated.
4026
+ */
4027
+ function declaredLocales(snapshot) {
4028
+ var _resolveSettingsModul = resolveSettingsModule(snapshot.schemas),
4029
+ moduleFilePath = _resolveSettingsModul.moduleFilePath;
4030
+ if (moduleFilePath === null) {
4031
+ return [];
4032
+ }
4033
+ var source = snapshot.sources[moduleFilePath];
4034
+ if (_typeof(source) !== "object" || source === null || Array.isArray(source)) {
4035
+ return [];
4036
+ }
4037
+ var locales = source["locales"];
4038
+ if (_typeof(locales) !== "object" || locales === null || Array.isArray(locales)) {
4039
+ return [];
4040
+ }
4041
+ var available = locales["available"];
4042
+ if (!Array.isArray(available)) {
4043
+ return [];
4044
+ }
4045
+ return available.filter(function (tag) {
4046
+ return typeof tag === "string";
4047
+ });
4048
+ }
4049
+ /**
4050
+ * The fixes {@link resolveSchemaSourceFixForError} answers for.
4051
+ *
4052
+ * A marker error carrying one of these is NOT a message: it says "ask the whole
4053
+ * project about this" and carries the value to ask with, because the schema
4054
+ * that raised it can only see its own module. Left unresolved, what a user sees
4055
+ * is the marker's own placeholder text — "should typically be processed by Val
4056
+ * internally … version mismatch" — which is alarming and says nothing.
4057
+ *
4058
+ * A list rather than a check per call site: every caller that resolves some of
4059
+ * these has to resolve all of them, and the two that leaked did so by adding a
4060
+ * fix here and not to a condition somewhere else.
4061
+ */
4062
+ var SCHEMA_SOURCE_FIXES = ["keyof:check-keys", "router:check-route", "locale:check-locale", "record:fill-keys"];
4063
+
4064
+ /** Whether an error is a marker only the whole project can answer. */
4065
+ function isSchemaSourceFixError(error) {
4066
+ var _error$fixes;
4067
+ return ((_error$fixes = error.fixes) !== null && _error$fixes !== void 0 ? _error$fixes : []).some(function (fix) {
4068
+ return SCHEMA_SOURCE_FIXES.includes(fix);
4069
+ });
4070
+ }
4071
+
3956
4072
  /**
3957
4073
  * The record a `keyof:check-keys` error resolves against, or null for any other
3958
4074
  * error — including a malformed marker, which
@@ -3964,8 +4080,8 @@ function checkRouteIsValid(route, includePattern, excludePattern, snapshot) {
3964
4080
  * to the referenced record can invalidate the module holding the reference.
3965
4081
  */
3966
4082
  function keyOfRecordPath(error) {
3967
- var _error$fixes;
3968
- if (!((_error$fixes = error.fixes) !== null && _error$fixes !== void 0 ? _error$fixes : []).includes("keyof:check-keys")) {
4083
+ var _error$fixes2;
4084
+ if (!((_error$fixes2 = error.fixes) !== null && _error$fixes2 !== void 0 ? _error$fixes2 : []).includes("keyof:check-keys")) {
3969
4085
  return null;
3970
4086
  }
3971
4087
  var value = error.value;
@@ -3977,15 +4093,15 @@ function keyOfRecordPath(error) {
3977
4093
  }
3978
4094
 
3979
4095
  /**
3980
- * Resolves a single `keyof:check-keys` or `router:check-route` error against
3981
- * the given schema/source snapshot. Returns:
4096
+ * Resolves a single marker error (see {@link SCHEMA_SOURCE_FIXES}) against the
4097
+ * given schema/source snapshot. Returns:
3982
4098
  * - null if the error is not one of these fixes (caller should pass through)
3983
- * - { status: "resolved" } if the referenced key/route is valid (drop error)
4099
+ * - { status: "resolved" } if the reference is valid (drop error)
3984
4100
  * - { status: "remaining"; error } if invalid (rewritten error to surface)
3985
4101
  */
3986
4102
  function resolveSchemaSourceFixForError(error, snapshot) {
3987
- var _error$fixes2;
3988
- var fixes = (_error$fixes2 = error.fixes) !== null && _error$fixes2 !== void 0 ? _error$fixes2 : [];
4103
+ var _error$fixes3;
4104
+ var fixes = (_error$fixes3 = error.fixes) !== null && _error$fixes3 !== void 0 ? _error$fixes3 : [];
3989
4105
  if (fixes.includes("keyof:check-keys")) {
3990
4106
  if (!error.value) {
3991
4107
  return {
@@ -4044,6 +4160,120 @@ function resolveSchemaSourceFixForError(error, snapshot) {
4044
4160
  status: "resolved"
4045
4161
  };
4046
4162
  }
4163
+ if (fixes.includes("locale:check-locale")) {
4164
+ var value = error.value;
4165
+ if (_typeof(value) !== "object" || value === null) {
4166
+ return {
4167
+ status: "remaining",
4168
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
4169
+ message: "Expected locale validation error to have a 'value' object. Found: ".concat(_typeof(value), ". ").concat(TYPE_ERROR_MESSAGE),
4170
+ typeError: true,
4171
+ fixes: undefined
4172
+ })
4173
+ };
4174
+ }
4175
+ var _ref6 = value,
4176
+ locale = _ref6.locale;
4177
+ if (typeof locale !== "string") {
4178
+ return {
4179
+ status: "remaining",
4180
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
4181
+ message: "Expected locale validation error 'value' to have property 'locale' of type 'string'. Found: ".concat(_typeof(locale), ". ").concat(TYPE_ERROR_MESSAGE),
4182
+ typeError: true,
4183
+ fixes: undefined
4184
+ })
4185
+ };
4186
+ }
4187
+ var available = declaredLocales(snapshot);
4188
+ if (available.length === 0) {
4189
+ return {
4190
+ status: "remaining",
4191
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
4192
+ // The project has not said it is translated, so the value is not
4193
+ // wrong so much as premature — and the fix is in another file.
4194
+ message: "This project has no languages. Declare them under 'locales.available' in the settings module before using s.locale().",
4195
+ fixes: undefined
4196
+ })
4197
+ };
4198
+ }
4199
+ if (available.includes(locale)) {
4200
+ return {
4201
+ status: "resolved"
4202
+ };
4203
+ }
4204
+ return {
4205
+ status: "remaining",
4206
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
4207
+ message: "'".concat(locale, "' is not one of this project's languages: ").concat(available.map(function (each) {
4208
+ return "'".concat(each, "'");
4209
+ }).join(", ")),
4210
+ fixes: undefined
4211
+ })
4212
+ };
4213
+ }
4214
+ if (fixes.includes("record:fill-keys")) {
4215
+ var _value = error.value;
4216
+ if (_typeof(_value) !== "object" || _value === null) {
4217
+ return {
4218
+ status: "remaining",
4219
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
4220
+ message: "Expected record key validation error to have a 'value' object. Found: ".concat(_typeof(_value), ". ").concat(TYPE_ERROR_MESSAGE),
4221
+ typeError: true,
4222
+ fixes: undefined
4223
+ })
4224
+ };
4225
+ }
4226
+ var _ref7 = _value,
4227
+ present = _ref7.present,
4228
+ declared = _ref7.declared;
4229
+ if (!Array.isArray(present)) {
4230
+ return {
4231
+ status: "remaining",
4232
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
4233
+ message: "Expected record key validation error 'value' to have property 'present' of type 'array'. Found: ".concat(_typeof(present), ". ").concat(TYPE_ERROR_MESSAGE),
4234
+ typeError: true,
4235
+ fixes: undefined
4236
+ })
4237
+ };
4238
+ }
4239
+ var presentKeys = present.filter(function (key) {
4240
+ return typeof key === "string";
4241
+ });
4242
+ // A literal union brought its keys along; a locale record left them to be
4243
+ // read out of the settings module, which is the whole reason this check is
4244
+ // resolved here rather than in the schema.
4245
+ var declaredKeys = Array.isArray(declared) ? declared.filter(function (key) {
4246
+ return typeof key === "string";
4247
+ }) : declaredLocales(snapshot);
4248
+ if (declaredKeys.length === 0) {
4249
+ // A locale record on a project that has declared no languages. There is
4250
+ // nothing to require, and the locale field's own check already says the
4251
+ // project has not declared any — saying it twice on one record would not
4252
+ // add anything.
4253
+ return {
4254
+ status: "resolved"
4255
+ };
4256
+ }
4257
+ var missing = missingDeclaredKeys(declaredKeys, presentKeys);
4258
+ if (missing.length === 0) {
4259
+ return {
4260
+ status: "resolved"
4261
+ };
4262
+ }
4263
+ return {
4264
+ status: "remaining",
4265
+ error: _objectSpread2(_objectSpread2({}, error), {}, {
4266
+ message: "Missing ".concat(missing.length === 1 ? "key" : "keys", ": ").concat(missing.map(function (each) {
4267
+ return "'".concat(each, "'");
4268
+ }).join(", "), ". This record's keys are declared by its schema, so every one of them is an entry \u2014 an entry nobody has written yet is null, not absent."),
4269
+ value: {
4270
+ missing: missing,
4271
+ declared: declaredKeys
4272
+ },
4273
+ fixes: undefined
4274
+ })
4275
+ };
4276
+ }
4047
4277
  if (fixes.includes("router:check-route")) {
4048
4278
  if (!error.value) {
4049
4279
  return {
@@ -4105,10 +4335,10 @@ function asRegExpPattern(value) {
4105
4335
  /**
4106
4336
  * Apply schema/source fix resolution across a map of validation errors.
4107
4337
  *
4108
- * Errors with `keyof:check-keys` or `router:check-route` are resolved against
4109
- * the in-memory schema/source snapshot — valid references drop the error,
4110
- * invalid ones are rewritten with a "did you mean…" message and have their
4111
- * `fixes` cleared (so downstream code reports them as plain validation errors).
4338
+ * Errors carrying one of {@link SCHEMA_SOURCE_FIXES} are resolved against the
4339
+ * in-memory schema/source snapshot — valid references drop the error, invalid
4340
+ * ones are rewritten with a "did you mean…" message and have their `fixes`
4341
+ * cleared (so downstream code reports them as plain validation errors).
4112
4342
  * All other errors pass through untouched.
4113
4343
  *
4114
4344
  * Source paths with no remaining errors are removed from the result.
@@ -4142,6 +4372,189 @@ function resolveSchemaSourceFixes(validationErrors, snapshot) {
4142
4372
  return result;
4143
4373
  }
4144
4374
 
4375
+ /**
4376
+ * Which of the project's languages governs a path, or `null` for none.
4377
+ *
4378
+ * THE question the whole feature turns on. A locale scope is a subtree in one
4379
+ * language, and three things open one: a `locale` field on an object, a record
4380
+ * keyed by `s.locale()`, and a locale segment in a segmented key. Once this can
4381
+ * be answered from the schema and the source alone, everything downstream falls
4382
+ * out of it — the Studio's locale filter, deep links, `<html lang>`, and knowing
4383
+ * what to translate from and into.
4384
+ *
4385
+ * One implementation, so the Studio, the server and the validation worker cannot
4386
+ * disagree about what language a piece of content is in.
4387
+ *
4388
+ * `null` means no scope governs the path — which is the answer for most content
4389
+ * in most projects, and for every project that has not declared any languages.
4390
+ */
4391
+ function localeAt(path, snapshot) {
4392
+ var available = declaredLocales(snapshot);
4393
+ if (available.length === 0) {
4394
+ // Nothing can be one of no languages. Answering `null` rather than reading
4395
+ // the content is also what makes this cheap for the projects that are not
4396
+ // translated at all.
4397
+ return null;
4398
+ }
4399
+ var _Internal$splitModule = Internal.splitModuleFilePathAndModulePath(path),
4400
+ _Internal$splitModule2 = _slicedToArray(_Internal$splitModule, 2),
4401
+ moduleFilePath = _Internal$splitModule2[0],
4402
+ modulePath = _Internal$splitModule2[1];
4403
+ var schema = snapshot.schemas[moduleFilePath];
4404
+ var source = snapshot.sources[moduleFilePath];
4405
+ if (schema === undefined || source === undefined) {
4406
+ return null;
4407
+ }
4408
+ var segments = modulePath ? Internal.splitModulePath(modulePath) : [];
4409
+ return walk(schema, source, segments, available);
4410
+ }
4411
+
4412
+ /**
4413
+ * Walk from a node towards `segments`, answering with the innermost scope.
4414
+ *
4415
+ * The scope is read on ARRIVAL at each node — the node the path names included,
4416
+ * not only the ones passed through on the way to it. So `localeAt` of a
4417
+ * scope-opening object is that object's own locale rather than its parent's,
4418
+ * and a path that stops AT a block still answers with the block's language.
4419
+ *
4420
+ * A scope may not contain another (see `localeScopeErrors`), so at most one of
4421
+ * these can fire on a well-formed path; taking the innermost is what makes the
4422
+ * answer well-defined while a project is mid-fix.
4423
+ */
4424
+ function walk(schema, source, segments, available) {
4425
+ var entered = enter(schema, source, available);
4426
+ var locale = entered.locale;
4427
+ var currentSchema = entered.schema;
4428
+ var currentSource = source;
4429
+ var _iterator = _createForOfIteratorHelper(segments),
4430
+ _step;
4431
+ try {
4432
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
4433
+ var segment = _step.value;
4434
+ if (currentSchema === undefined) {
4435
+ return locale;
4436
+ }
4437
+ if (currentSchema.type === "record") {
4438
+ var _currentSchema$key;
4439
+ if (((_currentSchema$key = currentSchema.key) === null || _currentSchema$key === void 0 ? void 0 : _currentSchema$key.type) === "locale") {
4440
+ // In a locale-keyed record the KEY is the language, so the segment we
4441
+ // are about to take is the answer.
4442
+ var resolved = localeOfValue(segment, available);
4443
+ if (resolved !== null) {
4444
+ locale = resolved;
4445
+ }
4446
+ }
4447
+ currentSchema = currentSchema.item;
4448
+ } else if (currentSchema.type === "array") {
4449
+ currentSchema = currentSchema.item;
4450
+ } else if (currentSchema.type === "object" || currentSchema.type === "settings") {
4451
+ currentSchema = currentSchema.items[segment];
4452
+ } else {
4453
+ // A leaf, or richtext's internal structure. Nothing below opens a scope.
4454
+ return locale;
4455
+ }
4456
+ currentSource = childSource(currentSource, segment);
4457
+ entered = enter(currentSchema, currentSource, available);
4458
+ currentSchema = entered.schema;
4459
+ if (entered.locale !== null) {
4460
+ locale = entered.locale;
4461
+ }
4462
+ }
4463
+ } catch (err) {
4464
+ _iterator.e(err);
4465
+ } finally {
4466
+ _iterator.f();
4467
+ }
4468
+ return locale;
4469
+ }
4470
+
4471
+ /**
4472
+ * Arrive at a node: resolve what it really is, and read the scope it opens.
4473
+ *
4474
+ * A discriminated union is a fork rather than a level — the variant the value
4475
+ * takes IS the node — so it is resolved here, before anything asks what the
4476
+ * node holds. Doing that on arrival rather than on the way down is the
4477
+ * difference between a block's own path answering with its language and
4478
+ * answering with nothing.
4479
+ */
4480
+ function enter(schema, source, available) {
4481
+ var resolved = (schema === null || schema === void 0 ? void 0 : schema.type) === "discriminated-union" ? variantOfUnion(schema, source) : schema;
4482
+ return {
4483
+ schema: resolved,
4484
+ locale: resolved === undefined ? null : localeOfObjectField(resolved, source, available)
4485
+ };
4486
+ }
4487
+
4488
+ /**
4489
+ * The language an object's own `locale` field says it is in, if it has one.
4490
+ *
4491
+ * Reads the SOURCE, since unlike a record key the value is content rather than
4492
+ * part of the path. A field that has not been filled in, or holds something
4493
+ * that is not one of the project's languages, is not an answer — validation is
4494
+ * already reporting that, and guessing here would put a language in
4495
+ * `<html lang>` that nobody chose.
4496
+ */
4497
+ function localeOfObjectField(schema, source, available) {
4498
+ if (schema.type !== "object" || !isJsonObject(source)) {
4499
+ return null;
4500
+ }
4501
+ for (var _i = 0, _Object$entries = Object.entries(schema.items); _i < _Object$entries.length; _i++) {
4502
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
4503
+ key = _Object$entries$_i[0],
4504
+ item = _Object$entries$_i[1];
4505
+ if (item.type !== "locale") {
4506
+ continue;
4507
+ }
4508
+ var value = source[key];
4509
+ if (typeof value !== "string") {
4510
+ return null;
4511
+ }
4512
+ return localeOfValue(value, available);
4513
+ }
4514
+ return null;
4515
+ }
4516
+
4517
+ /**
4518
+ * The variant a value takes, or `undefined` if the tag matches none.
4519
+ *
4520
+ * The tag is read off the source here; picking the variant is
4521
+ * {@link discriminatedUnionBranchOf}, shared with the Studio's locale filter so
4522
+ * the two cannot disagree about which variant a row is. `s.enum()` never
4523
+ * reaches this — it is a leaf, with nothing under it to walk into.
4524
+ */
4525
+ function variantOfUnion(schema, source) {
4526
+ if (!isJsonObject(source)) {
4527
+ return undefined;
4528
+ }
4529
+ return discriminatedUnionBranchOf(schema, source[schema.key]);
4530
+ }
4531
+
4532
+ /** The child of a source value at a segment, or `null` where there is none. */
4533
+ function childSource(source, segment) {
4534
+ var _source$segment;
4535
+ if (Array.isArray(source)) {
4536
+ var _source$index;
4537
+ var index = Number(segment);
4538
+ return Number.isInteger(index) ? (_source$index = source[index]) !== null && _source$index !== void 0 ? _source$index : null : null;
4539
+ }
4540
+ if (!isJsonObject(source)) {
4541
+ return null;
4542
+ }
4543
+ return (_source$segment = source[segment]) !== null && _source$segment !== void 0 ? _source$segment : null;
4544
+ }
4545
+
4546
+ /**
4547
+ * Whether `source` is a JSON object rather than an array or a primitive.
4548
+ *
4549
+ * A type predicate rather than the inline checks, because `Array.isArray` does
4550
+ * NOT narrow `JsonArray` out of `Json` — it is `readonly Json[]`, and the
4551
+ * built-in guard only narrows mutable arrays. Without this, every read of a
4552
+ * property would need an assertion.
4553
+ */
4554
+ function isJsonObject(source) {
4555
+ return _typeof(source) === "object" && source !== null && !Array.isArray(source);
4556
+ }
4557
+
4145
4558
  /**
4146
4559
  * Local `yyyy-MM-dd`, which is the one thing this module used `date-fns` for.
4147
4560
  *
@@ -4178,15 +4591,27 @@ function clampDateTimeString(value, options) {
4178
4591
  }
4179
4592
  return value;
4180
4593
  }
4181
- function emptyOf(schema) {
4594
+
4595
+ /**
4596
+ * What `emptyOf` cannot read off a serialized schema.
4597
+ *
4598
+ * The project's languages, and which one is being worked in. Neither is in the
4599
+ * schema: a locale-keyed record has one entry per language and the languages
4600
+ * are declared in the settings module, and "which language am I writing" is a
4601
+ * fact about the editor, not about the content. Optional throughout: a caller
4602
+ * that has no context gets an empty record and an unset locale, which
4603
+ * validation then reports, rather than wrong ones.
4604
+ */
4605
+
4606
+ function emptyOf(schema, context) {
4182
4607
  if (schema.type === "object") {
4183
4608
  return Object.fromEntries(Object.keys(schema.items).map(function (key) {
4184
- return [key, emptyOf(schema.items[key])];
4609
+ return [key, emptyOf(schema.items[key], context)];
4185
4610
  }));
4186
4611
  } else if (schema.type === "array") {
4187
4612
  return [];
4188
4613
  } else if (schema.type === "record") {
4189
- return {};
4614
+ return emptyRecord(schema, context);
4190
4615
  } else if (schema.type === "settings") {
4191
4616
  // Not an object of empty sections: every settings key is optional, and
4192
4617
  // absent IS the empty value. Filling the sections in would write a shape
@@ -4210,6 +4635,16 @@ function emptyOf(schema) {
4210
4635
  }
4211
4636
  } else if (schema.type === "route") {
4212
4637
  return ""; // Empty string as default route value
4638
+ } else if (schema.type === "locale") {
4639
+ var _context$selectedLoca;
4640
+ // Which languages exist is in the settings module, which `emptyOf` has no
4641
+ // access to — it works from a serialized schema alone. So a caller that
4642
+ // has narrowed to one language says so and gets it; a caller that has not
4643
+ // gets the empty string, which is not a language and which validation
4644
+ // reports. Guessing `locales[0]` instead would file content under a
4645
+ // language nobody chose, which is the thing this whole feature exists to
4646
+ // make visible.
4647
+ return (_context$selectedLoca = context === null || context === void 0 ? void 0 : context.selectedLocale) !== null && _context$selectedLoca !== void 0 ? _context$selectedLoca : "";
4213
4648
  } else if (schema.type === "file" || schema.type === "image") {
4214
4649
  return null; // returning null is the only thing we can do, however, it means that the patches cannot be applied yet since that might fail
4215
4650
  } else if (schema.type === "literal") {
@@ -4222,7 +4657,7 @@ function emptyOf(schema) {
4222
4657
  if (schema.items.length === 0) {
4223
4658
  throw Error("Cannot create an empty value for a discriminated union with no variants");
4224
4659
  }
4225
- return emptyOf(schema.items[0]);
4660
+ return emptyOf(schema.items[0], context);
4226
4661
  } else if (schema.type === "enum") {
4227
4662
  if (schema.values.length === 0) {
4228
4663
  throw Error("Cannot create an empty value for an enum with no values");
@@ -4248,6 +4683,32 @@ function emptyOf(schema) {
4248
4683
  throw Error("Unexpected schema type: " + JSON.stringify(_exhaustiveCheck));
4249
4684
  }
4250
4685
 
4686
+ /**
4687
+ * An empty record — with every key its schema declares already in it.
4688
+ *
4689
+ * An open record (`s.record(s.string(), item)`) starts empty, because there is
4690
+ * no key anyone could mean. A record whose key schema enumerates its keys is
4691
+ * the opposite: the keys are part of the schema, so an empty one is already
4692
+ * missing them, and handing back `{}` would create content that fails
4693
+ * validation the moment it is written.
4694
+ *
4695
+ * The entries are `null` rather than `emptyOf(item)`. A null entry reads as "not
4696
+ * filled in yet", which is what a language nobody has translated into IS —
4697
+ * whereas an entry of empty strings claims someone wrote it and left it blank,
4698
+ * and would count as translated in every list and filter downstream.
4699
+ */
4700
+ function emptyRecord(schema, context) {
4701
+ var _context$locales;
4702
+ var declared = declaredKeySetOf(schema.key);
4703
+ if (declared === null) {
4704
+ return {};
4705
+ }
4706
+ var keys = declared.kind === "literals" ? declared.keys : (_context$locales = context === null || context === void 0 ? void 0 : context.locales) !== null && _context$locales !== void 0 ? _context$locales : [];
4707
+ return Object.fromEntries(keys.map(function (key) {
4708
+ return [key, null];
4709
+ }));
4710
+ }
4711
+
4251
4712
  var toolNames = ["get_all_schema", "get_source", "search_content", "validate_content", "create_patch", "add_session_image_to_gallery", "remove_image_gallery_entry", "navigate_to", "get_current_context", "get_patches", "get_source_path_from_route", "set_session_name", "show_compare_view", "ask_user_question", "duplicate_source", "empty_at_path", "count_entries", "get_record_keys"];
4252
4713
 
4253
4714
  function buildFileRefValue(filePath, metadata) {
@@ -4284,7 +4745,7 @@ function getSourceAt(source, path) {
4284
4745
  * Walks `schema` along `path`, returning a structured result:
4285
4746
  * - `leaf`: walked the full path; resolved schema at the end.
4286
4747
  * - `richtext`: hit a richtext mid-walk; richtext is the resolved schema.
4287
- * - `gallery-traversed`: hit an `s.images()` record and walked past it (i.e. the
4748
+ * - `gallery-traversed`: hit an `s.imageset()` record and walked past it (i.e. the
4288
4749
  * path tries to address an entry inside the gallery — a clue the AI should
4289
4750
  * have used the gallery tool instead).
4290
4751
  * - `unresolved`: the path could not be walked.
@@ -4415,7 +4876,7 @@ function buildImageGalleryPatch(args, moduleSchema) {
4415
4876
  op: "add",
4416
4877
  path: [args.filePath],
4417
4878
  value: _objectSpread2(_objectSpread2({}, args.metadata), {}, {
4418
- // `alt` is a REQUIRED field of a gallery entry (`s.images()` gives it a
4879
+ // `alt` is a REQUIRED field of a gallery entry (`s.imageset()` gives it a
4419
4880
  // nullable string schema), so an entry written without one is invalid
4420
4881
  // the moment it lands — and the caller that had no alt to give is the
4421
4882
  // common case, not the odd one. Null is what the Studio's own upload
@@ -4478,10 +4939,10 @@ function buildRemoveImageGalleryEntryPatch(args, moduleSchema, moduleSource) {
4478
4939
  var GALLERY_REDIRECT = {
4479
4940
  kind: "wrong-tool",
4480
4941
  suggestedTool: "add_session_image_to_gallery",
4481
- reason: "Destination is a media gallery (s.images() / s.files()). Gallery entries are keyed by file path and carry a file on disk, so they cannot be created with a plain patch. Use add_session_image_to_gallery."
4942
+ reason: "Destination is a media gallery (s.imageset() / s.fileset()). Gallery entries are keyed by file path and carry a file on disk, so they cannot be created with a plain patch. Use add_session_image_to_gallery."
4482
4943
  };
4483
4944
 
4484
- /** An `s.images()` / `s.files()` record: a gallery, not an ordinary record. */
4945
+ /** An `s.imageset()` / `s.fileset()` record: a gallery, not an ordinary record. */
4485
4946
  function isGallery(schema) {
4486
4947
  return schema.type === "record" && schema.mediaType !== undefined;
4487
4948
  }
@@ -4624,7 +5085,13 @@ function buildEmptyAtPathPatch(args, moduleSchema,
4624
5085
  // Required, not optional: the occupied-key check below is the only thing
4625
5086
  // standing between "scaffold an entry" and "delete the entry that was there",
4626
5087
  // and an optional argument is one a caller forgets.
4627
- moduleSource) {
5088
+ moduleSource,
5089
+ // The project's languages, for a locale-keyed record: its entries ARE the
5090
+ // languages, and they are declared in another module, so an empty one built
5091
+ // without them is `{}` — which the completeness rule then reports as every
5092
+ // language missing. Both callers can answer: the Studio from the context, the
5093
+ // MCP tools from the snapshot they already hold. See `EmptyOfContext`.
5094
+ context) {
4628
5095
  var destinationSchema;
4629
5096
  if (args.destinationPath.length === 0) {
4630
5097
  destinationSchema = moduleSchema;
@@ -4670,7 +5137,7 @@ moduleSource) {
4670
5137
  if (occupied) {
4671
5138
  return occupied;
4672
5139
  }
4673
- var value = emptyOf(destinationSchema);
5140
+ var value = emptyOf(destinationSchema, context);
4674
5141
  return safeParsePatch([{
4675
5142
  op: decision.op,
4676
5143
  path: args.destinationPath,
@@ -4861,6 +5328,8 @@ function isSkippableFixCode(fix) {
4861
5328
  return true;
4862
5329
  case "keyof:check-keys":
4863
5330
  case "router:check-route":
5331
+ case "locale:check-locale":
5332
+ case "record:fill-keys":
4864
5333
  case "jsonValues:extract-entry":
4865
5334
  // `jsonValues:extract-entry` is an entry inlined in the `.val.ts`. The
4866
5335
  // browser cannot fix it (that means writing a new `*.val.json` and
@@ -5268,8 +5737,8 @@ function traverseSchemaSource(source, schema, path, callback) {
5268
5737
  return;
5269
5738
  }
5270
5739
 
5271
- // Handle route
5272
- if (schema.type === "route") {
5740
+ // Handle route and locale: both are plain strings at a leaf
5741
+ if (schema.type === "route" || schema.type === "locale") {
5273
5742
  if (typeof source === "string") {
5274
5743
  callback({
5275
5744
  source: source,
@@ -5496,7 +5965,13 @@ function indexModule(searchIndex, moduleFilePath, source, schema) {
5496
5965
  // An enum's value is a string the editor chose; it is as searchable as
5497
5966
  // any other string leaf. The old string union was never indexed at all,
5498
5967
  // which is the gap this closes.
5499
- schema.type === "enum") {
5968
+ schema.type === "enum" ||
5969
+ // A locale tag, for the same reason: it is what the editor picked and
5970
+ // what they see in the field, so searching "nb-NO" has to find the
5971
+ // Norwegian rows. `traverseSchemaSource` visits these, and visiting a
5972
+ // leaf only to index it as the empty string is worse than not visiting
5973
+ // it — it looks like content that does not exist.
5974
+ schema.type === "locale") {
5500
5975
  var _source$toString, _source$toString2;
5501
5976
  searchText = (_source$toString = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString !== void 0 ? _source$toString : "";
5502
5977
  label = (_source$toString2 = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString2 !== void 0 ? _source$toString2 : "";
@@ -5637,4 +6112,4 @@ function performSearch(searchIndex, query) {
5637
6112
  };
5638
6113
  }
5639
6114
 
5640
- export { Api, 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, PatchGroup, PatchId, PatchJSON, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SharedValConfig, VAL_ENABLE_COOKIE_NAME, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, ValCommit, ValDeployment, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, checkCompatibility, chooseEncoded, concatModulePath, createRegExpFromPattern, createSearchIndex, createValClient, describeContainerAtPath, emptyOf, explainIncompatible, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, indexModule, isMimeTypeAccepted, isRemoteSchema, isSkippedSource, keyOfRecordPath, levenshtein, newestCommitSha, partitionValidationErrors, performSearch, remirrorToRichTextSource, removeModule, resolveEncodeSettings, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextToRemirror, safeParsePatch, sourcePathOfChild, toolNames, traverseSchemaSource, urlOf, validateRoutePatterns, withExtension };
6115
+ export { Api, 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, PatchGroup, PatchId, PatchJSON, 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, SharedValConfig, VAL_ENABLE_COOKIE_NAME, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, ValCommit, ValDeployment, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, checkCompatibility, chooseEncoded, concatModulePath, createRegExpFromPattern, createSearchIndex, createValClient, declaredLocales, describeContainerAtPath, emptyOf, explainIncompatible, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, indexModule, isMimeTypeAccepted, isRemoteSchema, isSchemaSourceFixError, isSkippedSource, keyOfRecordPath, levenshtein, localeAt, newestCommitSha, partitionValidationErrors, performSearch, remirrorToRichTextSource, removeModule, resolveEncodeSettings, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextToRemirror, safeParsePatch, sourcePathOfChild, toolNames, traverseSchemaSource, urlOf, validateRoutePatterns, withExtension };