@valbuild/shared 0.123.2 → 0.123.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.123.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#633](https://github.com/valbuild/val/pull/633) [`accf4f8`](https://github.com/valbuild/val/commit/accf4f852fe3400d762cc14e80316da741a69a9a) Thanks [@freekh](https://github.com/freekh)! - Stop `@valbuild/shared` replacing a consumer's own flexsearch types
8
+
9
+ `@valbuild/shared` gained a flexsearch dependency in 0.123.0, for
10
+ `search_content`, and typed its exported `SearchIndex.index` as flexsearch's
11
+ own `Index`. That put `import { Index } from "flexsearch"` into the published
12
+ `searchIndex.d.ts` — and flexsearch's type entry opens with
13
+ `declare module "flexsearch"`, which is an AMBIENT module declaration and so
14
+ global to a whole TypeScript program.
15
+
16
+ The effect on a project that uses flexsearch itself, at a different version:
17
+ its own calls are checked against the copy Val dragged in. valbuild/web pins
18
+ flexsearch 0.7 and builds an index with
19
+ `new flexsearch.Document({ language: "en", … })`; on `@valbuild/shared@0.123.2`
20
+ that stopped compiling, because 0.8's `DocumentOptions` has no `language`. Its
21
+ resolved copy was still 0.7 — only the types had been swapped underneath it.
22
+
23
+ `SearchIndex.index` is now a structural type covering the three methods this
24
+ package calls, so nothing in the published declarations names flexsearch. The
25
+ library is still used to build the index; that is a value import, which never
26
+ reaches a `.d.ts`.
27
+
28
+ No API change: `SearchIndex` is still assignable from a real flexsearch
29
+ `Index`, and `noFlexsearchInPublishedTypes.test.ts` asserts both halves of that
30
+ — that no source file names flexsearch in a type position, and that a real
31
+ `Index` still satisfies the stand-in, so it cannot drift from the library
32
+ unnoticed.
33
+
34
+ - [#628](https://github.com/valbuild/val/pull/628) [`aef45ce`](https://github.com/valbuild/val/commit/aef45ce3ef269f1b6221de44a56df0f6a0d9dbbd) Thanks [@freekh](https://github.com/freekh)! - Stop the serialized-schema parser dropping schema metadata
35
+
36
+ `SerializedSchema` in `@valbuild/shared` is a zod mirror of the serialized
37
+ schema type, and its `z.object`s strip keys they do not declare. Five fields
38
+ were declared on some schemas and forgotten on others:
39
+
40
+ - `customValidate` — missing on twelve of the eighteen schemas. This is the flag
41
+ that says a schema declares a `.validate()`; the function itself cannot
42
+ serialize, so the flag is the only thing that tells the Studio to run the
43
+ custom validators against the real instance.
44
+ - `description` — missing on thirteen, so a `.describe()` went unseen.
45
+ - `remote` and `referencedModule` — missing on `s.image()` and `s.file()`, so a
46
+ remote field read as local and a gallery-backed field lost the gallery it
47
+ reads its dimensions and mime type from.
48
+ - the `message` on a `.regexp(pattern, message)` — so a field with a custom
49
+ pattern message fell back to the generic "Expected string to match reg
50
+ exp: …".
51
+
52
+ Because it strips rather than rejects, nothing failed and nothing said so. The
53
+ live `/schema` route was unaffected — `ValClient` validates the response and
54
+ then returns the raw JSON — but the history path consumes the parsed output, so
55
+ in a commit or historical patch-set view (`getModuleAtCommit`,
56
+ `getHistoricalPatchSet`) a schema's custom validators, description, remoteness
57
+ and backing gallery all quietly disappeared.
58
+
59
+ The fields every serialized schema shares are now spread from one
60
+ `commonSchemaFields` object instead of being retyped eighteen times, which is
61
+ how they drifted apart in the first place. A round-trip test walks each schema's
62
+ serialized output against the parsed result and fails with the path of any key
63
+ that did not survive, so a field added to a serialized schema and forgotten in
64
+ the parser is caught without anyone having to remember to assert it.
65
+
3
66
  ## 0.123.2
4
67
 
5
68
  ### Patch Changes
@@ -1,5 +1,4 @@
1
1
  import { Json, ModuleFilePath, SerializedSchema, SourcePath } from "@valbuild/core";
2
- import { Index } from "flexsearch";
3
2
  /**
4
3
  * The search index and the labels to render for its hits.
5
4
  *
@@ -15,8 +14,37 @@ import { Index } from "flexsearch";
15
14
  * them — an agent and an editor searching the same project should find the same
16
15
  * things.
17
16
  */
17
+ /**
18
+ * The parts of FlexSearch's `Index` this module uses.
19
+ *
20
+ * Structural, rather than importing flexsearch's own `Index`, because
21
+ * {@link SearchIndex} is EXPORTED — and a `.d.ts` that names `flexsearch` pulls
22
+ * that package's `declare module "flexsearch"` into every consumer's TypeScript
23
+ * program. Ambient module declarations are global to a program, so a consumer
24
+ * with its own flexsearch at another version then checks its OWN calls against
25
+ * the copy we dragged in, whichever of the two wins.
26
+ *
27
+ * That is not hypothetical: `@valbuild/shared` gained a flexsearch dependency
28
+ * in 0.123.0 for `search_content`, and valbuild/web — which pins flexsearch
29
+ * 0.7 and calls `new flexsearch.Document({ language: "en", … })` — stopped
30
+ * building on 0.123.2, because 0.8's `DocumentOptions` has no `language`. Its
31
+ * own resolved copy was still 0.7; only the types had been replaced.
32
+ *
33
+ * Three methods is all this module calls, and none of their signatures here is
34
+ * a guess: `add` and `remove` are used in `indexModule`/`removeModule`, and
35
+ * `search`'s result is only ever `.length`-ed, `.slice`-d and mapped over as
36
+ * ids. `FlexSearch.Index` is still constructed with the real library in
37
+ * {@link createSearchIndex} — a value import, which does not reach the `.d.ts`.
38
+ */
39
+ type SearchableIndex = {
40
+ add(id: string, content: string): void;
41
+ remove(id: string): void;
42
+ search(query: string, options: {
43
+ limit: number;
44
+ }): (string | number)[];
45
+ };
18
46
  export type SearchIndex = {
19
- index: Index;
47
+ index: SearchableIndex;
20
48
  pathToLabel: Map<string, string>;
21
49
  /**
22
50
  * Which document ids belong to which module, so one module can be re-indexed
@@ -74,3 +102,4 @@ export type SearchResults = {
74
102
  totalIsLowerBound: boolean;
75
103
  };
76
104
  export declare function performSearch(searchIndex: SearchIndex | null, query: string, limit?: number, offset?: number): SearchResults;
105
+ export {};
@@ -911,97 +911,96 @@ var InlineRender = zod.z.object({
911
911
  as: zod.z.literal("inline")
912
912
  });
913
913
  var FieldRender = InlineRender.optional();
914
- var SerializedStringSchema = zod.z.object({
915
- type: zod.z.literal("string"),
914
+
915
+ /**
916
+ * The fields EVERY serialized schema carries, in one place.
917
+ *
918
+ * Spread into each schema below rather than retyped, because retyping them is
919
+ * exactly how they went missing: `customValidate` and `description` were
920
+ * declared on six of the eighteen schemas and silently dropped by the other
921
+ * twelve. A field added to the base of `SerializedSchema` needs adding here and
922
+ * nowhere else.
923
+ *
924
+ * `type`, `opt` and whatever is particular to the schema stay at the call site
925
+ * - those differ per schema, so there is nothing to share.
926
+ */
927
+ var commonSchemaFields = {
916
928
  render: FieldRender,
929
+ preview: zod.z.literal(true).optional(),
930
+ // Whether the schema declares a `.validate()`. The function cannot serialize,
931
+ // so this flag is what tells the Studio to run the custom validators on the
932
+ // main thread against the real instance - dropped, and they never run at all.
933
+ // See `hasCustomValidate` in `ui/spa/validation/customValidate.ts`.
934
+ customValidate: zod.z["boolean"]().optional(),
935
+ readonly: zod.z["boolean"]().optional(),
936
+ hidden: zod.z["boolean"]().optional(),
937
+ description: zod.z.string().optional()
938
+ };
939
+ var SerializedStringSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
940
+ type: zod.z.literal("string"),
917
941
  // `.multiline()`. Stripped like any undeclared key if it goes missing here,
918
942
  // which is a single-line input where the author asked for a text box.
919
943
  multiline: zod.z["boolean"]().optional(),
920
- preview: zod.z.literal(true).optional(),
921
944
  options: zod.z.object({
922
945
  maxLength: zod.z.number().optional(),
923
946
  minLength: zod.z.number().optional(),
924
947
  regexp: zod.z.object({
948
+ // `.regexp(re, message)`'s message. Written by
949
+ // `StringSchema.executeSerialize`, so omitting it here strips the
950
+ // author's own wording and leaves the generic "Expected string to
951
+ // match reg exp: …" in its place.
952
+ message: zod.z.string().optional(),
925
953
  source: zod.z.string(),
926
954
  flags: zod.z.string()
927
- }).optional()
955
+ }).optional(),
956
+ customValidate: zod.z["boolean"]().optional()
928
957
  }).optional(),
929
958
  opt: zod.z["boolean"](),
930
- raw: zod.z["boolean"](),
931
- readonly: zod.z["boolean"]().optional(),
932
- hidden: zod.z["boolean"]().optional()
933
- });
934
- var SerializedLiteralSchema = zod.z.object({
959
+ raw: zod.z["boolean"]()
960
+ }));
961
+ var SerializedLiteralSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
935
962
  type: zod.z.literal("literal"),
936
- render: FieldRender,
937
- preview: zod.z.literal(true).optional(),
938
963
  value: zod.z.string(),
939
- opt: zod.z["boolean"](),
940
- readonly: zod.z["boolean"]().optional(),
941
- hidden: zod.z["boolean"]().optional()
942
- });
943
- var SerializedBooleanSchema = zod.z.object({
964
+ opt: zod.z["boolean"]()
965
+ }));
966
+ var SerializedBooleanSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
944
967
  type: zod.z.literal("boolean"),
945
- render: FieldRender,
946
- preview: zod.z.literal(true).optional(),
947
- opt: zod.z["boolean"](),
948
- readonly: zod.z["boolean"]().optional(),
949
- hidden: zod.z["boolean"]().optional()
950
- });
951
- var SerializedNumberSchema = zod.z.object({
968
+ opt: zod.z["boolean"]()
969
+ }));
970
+ var SerializedNumberSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
952
971
  type: zod.z.literal("number"),
953
- render: FieldRender,
954
- preview: zod.z.literal(true).optional(),
955
972
  options: zod.z.object({
956
973
  max: zod.z.number().optional(),
957
974
  min: zod.z.number().optional()
958
975
  }).optional(),
959
- opt: zod.z["boolean"](),
960
- readonly: zod.z["boolean"]().optional(),
961
- hidden: zod.z["boolean"]().optional()
962
- });
976
+ opt: zod.z["boolean"]()
977
+ }));
963
978
  var SerializedObjectSchema = zod.z.lazy(function () {
964
- return zod.z.object({
979
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
965
980
  type: zod.z.literal("object"),
966
- render: FieldRender,
967
- preview: zod.z.literal(true).optional(),
968
981
  items: zod.z.record(zod.z.string(), SerializedSchema),
969
- opt: zod.z["boolean"](),
970
- readonly: zod.z["boolean"]().optional(),
971
- hidden: zod.z["boolean"]().optional()
972
- });
982
+ opt: zod.z["boolean"]()
983
+ }));
973
984
  });
974
985
  var SerializedArraySchema = zod.z.lazy(function () {
975
- return zod.z.object({
986
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
976
987
  type: zod.z.literal("array"),
977
- render: FieldRender,
978
- preview: zod.z.literal(true).optional(),
979
988
  item: SerializedSchema,
980
- opt: zod.z["boolean"](),
981
- readonly: zod.z["boolean"]().optional(),
982
- hidden: zod.z["boolean"]().optional()
983
- });
989
+ opt: zod.z["boolean"]()
990
+ }));
984
991
  });
985
992
  var SerializedUnionSchema = zod.z.lazy(function () {
986
- return zod.z.union([zod.z.object({
993
+ return zod.z.union([zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
987
994
  type: zod.z.literal("union"),
988
- render: FieldRender,
989
- preview: zod.z.literal(true).optional(),
990
995
  key: SerializedLiteralSchema,
991
996
  items: zod.z.array(SerializedLiteralSchema),
992
- opt: zod.z["boolean"](),
993
- readonly: zod.z["boolean"]().optional(),
994
- hidden: zod.z["boolean"]().optional()
995
- }), zod.z.object({
997
+ opt: zod.z["boolean"]()
998
+ })), zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
996
999
  type: zod.z.literal("union"),
997
- render: FieldRender,
998
- preview: zod.z.literal(true).optional(),
999
1000
  key: zod.z.string(),
1000
1001
  items: zod.z.array(SerializedObjectSchema),
1001
- opt: zod.z["boolean"](),
1002
- readonly: zod.z["boolean"]().optional(),
1003
- hidden: zod.z["boolean"]().optional()
1004
- })]);
1002
+ opt: zod.z["boolean"]()
1003
+ }))]);
1005
1004
  });
1006
1005
  var ImageEncodeOption = zod.z.union([zod.z.literal(false), zod.z.object({
1007
1006
  type: zod.z.literal("webp"),
@@ -1014,15 +1013,16 @@ var ImageOptions = zod.z.object({
1014
1013
  accept: zod.z.string().optional(),
1015
1014
  encode: ImageEncodeOption.optional()
1016
1015
  });
1017
- var SerializedImageSchema = zod.z.object({
1016
+ var SerializedImageSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1018
1017
  type: zod.z.literal("image"),
1019
- render: FieldRender,
1020
- preview: zod.z.literal(true).optional(),
1021
1018
  options: ImageOptions.optional(),
1022
1019
  opt: zod.z["boolean"](),
1023
- readonly: zod.z["boolean"]().optional(),
1024
- hidden: zod.z["boolean"]().optional()
1025
- });
1020
+ // `.remote()`, and the gallery a gallery-backed field reads its metadata
1021
+ // from. Neither is cosmetic: dropped, a remote field looks local and a
1022
+ // gallery-backed one looks like it holds its own dimensions.
1023
+ remote: zod.z["boolean"]().optional(),
1024
+ referencedModule: zod.z.string().optional()
1025
+ }));
1026
1026
  var RichTextOptions = zod.z.lazy(function () {
1027
1027
  return zod.z.object({
1028
1028
  // Set by `.maxLength()` / `.minLength()`, not by the options argument.
@@ -1045,20 +1045,14 @@ var RichTextOptions = zod.z.lazy(function () {
1045
1045
  img: zod.z.union([zod.z["boolean"](), SerializedImageSchema]).optional()
1046
1046
  });
1047
1047
  });
1048
- var SerializedRichTextSchema = zod.z.object({
1048
+ var SerializedRichTextSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1049
1049
  type: zod.z.literal("richtext"),
1050
- render: FieldRender,
1051
- preview: zod.z.literal(true).optional(),
1052
1050
  options: RichTextOptions.optional(),
1053
- opt: zod.z["boolean"](),
1054
- readonly: zod.z["boolean"]().optional(),
1055
- hidden: zod.z["boolean"]().optional()
1056
- });
1051
+ opt: zod.z["boolean"]()
1052
+ }));
1057
1053
  var SerializedRecordSchema = zod.z.lazy(function () {
1058
- return zod.z.object({
1054
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1059
1055
  type: zod.z.literal("record"),
1060
- render: FieldRender,
1061
- preview: zod.z.literal(true).optional(),
1062
1056
  item: SerializedSchema,
1063
1057
  opt: zod.z["boolean"](),
1064
1058
  // Optional gallery marker for files/images
@@ -1069,16 +1063,12 @@ var SerializedRecordSchema = zod.z.lazy(function () {
1069
1063
  remote: zod.z["boolean"]().optional(),
1070
1064
  encode: ImageEncodeOption.optional(),
1071
1065
  alt: SerializedSchema.optional(),
1072
- moduleMetadata: zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.any())).optional(),
1073
- readonly: zod.z["boolean"]().optional(),
1074
- hidden: zod.z["boolean"]().optional()
1075
- }).passthrough();
1066
+ moduleMetadata: zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.any())).optional()
1067
+ })).passthrough();
1076
1068
  });
1077
1069
  var SerializedKeyOfSchema = zod.z.lazy(function () {
1078
- return zod.z.object({
1070
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1079
1071
  type: zod.z.literal("keyOf"),
1080
- render: FieldRender,
1081
- preview: zod.z.literal(true).optional(),
1082
1072
  path: SourcePath,
1083
1073
  schema: zod.z.union([zod.z.object({
1084
1074
  type: zod.z.literal("object"),
@@ -1089,82 +1079,54 @@ var SerializedKeyOfSchema = zod.z.lazy(function () {
1089
1079
  opt: zod.z["boolean"]().optional()
1090
1080
  })]).optional(),
1091
1081
  values: zod.z.union([zod.z.literal("string"), zod.z.array(zod.z.string())]),
1092
- opt: zod.z["boolean"](),
1093
- readonly: zod.z["boolean"]().optional(),
1094
- hidden: zod.z["boolean"]().optional()
1095
- });
1082
+ opt: zod.z["boolean"]()
1083
+ }));
1096
1084
  });
1097
1085
  var FileOptions = zod.z.object({
1098
1086
  accept: zod.z.string().optional()
1099
1087
  });
1100
- var SerializedFileSchema = zod.z.object({
1088
+ var SerializedFileSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1101
1089
  type: zod.z.literal("file"),
1102
- render: FieldRender,
1103
- preview: zod.z.literal(true).optional(),
1104
1090
  options: FileOptions.optional(),
1105
1091
  opt: zod.z["boolean"](),
1106
- readonly: zod.z["boolean"]().optional(),
1107
- hidden: zod.z["boolean"]().optional()
1108
- });
1092
+ // See `SerializedImageSchema` above: `.remote()` and the gallery a
1093
+ // gallery-backed field reads from.
1094
+ remote: zod.z["boolean"]().optional(),
1095
+ referencedModule: zod.z.string().optional()
1096
+ }));
1109
1097
  var DateOptions = zod.z.object({
1110
1098
  from: zod.z.string().optional(),
1111
1099
  to: zod.z.string().optional()
1112
1100
  });
1113
- var SerializedDateSchema = zod.z.object({
1101
+ var SerializedDateSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1114
1102
  type: zod.z.literal("date"),
1115
- render: FieldRender,
1116
- preview: zod.z.literal(true).optional(),
1117
1103
  options: DateOptions.optional(),
1118
- opt: zod.z["boolean"](),
1119
- customValidate: zod.z["boolean"]().optional(),
1120
- readonly: zod.z["boolean"]().optional(),
1121
- hidden: zod.z["boolean"]().optional(),
1122
- description: zod.z.string().optional()
1123
- });
1124
- var SerializedDateTimeSchema = zod.z.object({
1104
+ opt: zod.z["boolean"]()
1105
+ }));
1106
+ var SerializedDateTimeSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1125
1107
  type: zod.z.literal("dateTime"),
1126
- render: FieldRender,
1127
- preview: zod.z.literal(true).optional(),
1128
1108
  options: DateOptions.optional(),
1129
- opt: zod.z["boolean"](),
1130
- customValidate: zod.z["boolean"]().optional(),
1131
- readonly: zod.z["boolean"]().optional(),
1132
- hidden: zod.z["boolean"]().optional(),
1133
- description: zod.z.string().optional()
1134
- });
1109
+ opt: zod.z["boolean"]()
1110
+ }));
1135
1111
  var ColorOptions = zod.z.object({
1136
1112
  format: zod.z["enum"](["hex", "rgb", "hsl", "oklch"]).optional(),
1137
1113
  alpha: zod.z["boolean"]().optional()
1138
1114
  });
1139
- var SerializedColorSchema = zod.z.object({
1115
+ var SerializedColorSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1140
1116
  type: zod.z.literal("color"),
1141
- render: FieldRender,
1142
- preview: zod.z.literal(true).optional(),
1143
1117
  options: ColorOptions.optional(),
1144
- opt: zod.z["boolean"](),
1145
- customValidate: zod.z["boolean"]().optional(),
1146
- readonly: zod.z["boolean"]().optional(),
1147
- hidden: zod.z["boolean"]().optional(),
1148
- description: zod.z.string().optional()
1149
- });
1118
+ opt: zod.z["boolean"]()
1119
+ }));
1150
1120
  var CodeOptions = zod.z.object({
1151
1121
  language: zod.z["enum"](core.CODE_LANGUAGES).optional()
1152
1122
  });
1153
- var SerializedCodeSchema = zod.z.object({
1123
+ var SerializedCodeSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1154
1124
  type: zod.z.literal("code"),
1155
- render: FieldRender,
1156
- preview: zod.z.literal(true).optional(),
1157
1125
  options: CodeOptions.optional(),
1158
- opt: zod.z["boolean"](),
1159
- customValidate: zod.z["boolean"]().optional(),
1160
- readonly: zod.z["boolean"]().optional(),
1161
- hidden: zod.z["boolean"]().optional(),
1162
- description: zod.z.string().optional()
1163
- });
1164
- var SerializedRouteSchema = zod.z.object({
1126
+ opt: zod.z["boolean"]()
1127
+ }));
1128
+ var SerializedRouteSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1165
1129
  type: zod.z.literal("route"),
1166
- render: FieldRender,
1167
- preview: zod.z.literal(true).optional(),
1168
1130
  options: zod.z.object({
1169
1131
  include: zod.z.object({
1170
1132
  source: zod.z.string(),
@@ -1176,11 +1138,8 @@ var SerializedRouteSchema = zod.z.object({
1176
1138
  }).optional(),
1177
1139
  customValidate: zod.z["boolean"]().optional()
1178
1140
  }).optional(),
1179
- opt: zod.z["boolean"](),
1180
- customValidate: zod.z["boolean"]().optional(),
1181
- readonly: zod.z["boolean"]().optional(),
1182
- hidden: zod.z["boolean"]().optional()
1183
- });
1141
+ opt: zod.z["boolean"]()
1142
+ }));
1184
1143
 
1185
1144
  // A settings module, and each section inside one, serialize as this - the
1186
1145
  // shape is recursive because `items` holds sections which are themselves
@@ -1188,17 +1147,11 @@ var SerializedRouteSchema = zod.z.object({
1188
1147
  // (see core/src/schema/settings.ts), but they are accepted here because they
1189
1148
  // are part of the declared type.
1190
1149
  var SerializedSettingsSchema = zod.z.lazy(function () {
1191
- return zod.z.object({
1150
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1192
1151
  type: zod.z.literal("settings"),
1193
- render: FieldRender,
1194
- preview: zod.z.literal(true).optional(),
1195
1152
  items: zod.z.record(zod.z.string(), SerializedSchema),
1196
- opt: zod.z["boolean"](),
1197
- customValidate: zod.z["boolean"]().optional(),
1198
- readonly: zod.z["boolean"]().optional(),
1199
- hidden: zod.z["boolean"]().optional(),
1200
- description: zod.z.string().optional()
1201
- });
1153
+ opt: zod.z["boolean"]()
1154
+ }));
1202
1155
  });
1203
1156
  var SerializedSchema = zod.z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedUnionSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
1204
1157
 
@@ -5329,6 +5282,28 @@ function getRefParts(ref) {
5329
5282
  * them — an agent and an editor searching the same project should find the same
5330
5283
  * things.
5331
5284
  */
5285
+ /**
5286
+ * The parts of FlexSearch's `Index` this module uses.
5287
+ *
5288
+ * Structural, rather than importing flexsearch's own `Index`, because
5289
+ * {@link SearchIndex} is EXPORTED — and a `.d.ts` that names `flexsearch` pulls
5290
+ * that package's `declare module "flexsearch"` into every consumer's TypeScript
5291
+ * program. Ambient module declarations are global to a program, so a consumer
5292
+ * with its own flexsearch at another version then checks its OWN calls against
5293
+ * the copy we dragged in, whichever of the two wins.
5294
+ *
5295
+ * That is not hypothetical: `@valbuild/shared` gained a flexsearch dependency
5296
+ * in 0.123.0 for `search_content`, and valbuild/web — which pins flexsearch
5297
+ * 0.7 and calls `new flexsearch.Document({ language: "en", … })` — stopped
5298
+ * building on 0.123.2, because 0.8's `DocumentOptions` has no `language`. Its
5299
+ * own resolved copy was still 0.7; only the types had been replaced.
5300
+ *
5301
+ * Three methods is all this module calls, and none of their signatures here is
5302
+ * a guess: `add` and `remove` are used in `indexModule`/`removeModule`, and
5303
+ * `search`'s result is only ever `.length`-ed, `.slice`-d and mapped over as
5304
+ * ids. `FlexSearch.Index` is still constructed with the real library in
5305
+ * {@link createSearchIndex} — a value import, which does not reach the `.d.ts`.
5306
+ */
5332
5307
 
5333
5308
  /** An index with nothing in it. Fill it with {@link indexModule}. */
5334
5309
  function createSearchIndex() {
@@ -5430,9 +5405,9 @@ function removeModule(searchIndex, moduleFilePath) {
5430
5405
  _step;
5431
5406
  try {
5432
5407
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
5433
- var id = _step.value;
5434
- index.remove(id);
5435
- pathToLabel["delete"](id);
5408
+ var _id = _step.value;
5409
+ index.remove(_id);
5410
+ pathToLabel["delete"](_id);
5436
5411
  }
5437
5412
  } catch (err) {
5438
5413
  _iterator.e(err);
@@ -911,97 +911,96 @@ var InlineRender = zod.z.object({
911
911
  as: zod.z.literal("inline")
912
912
  });
913
913
  var FieldRender = InlineRender.optional();
914
- var SerializedStringSchema = zod.z.object({
915
- type: zod.z.literal("string"),
914
+
915
+ /**
916
+ * The fields EVERY serialized schema carries, in one place.
917
+ *
918
+ * Spread into each schema below rather than retyped, because retyping them is
919
+ * exactly how they went missing: `customValidate` and `description` were
920
+ * declared on six of the eighteen schemas and silently dropped by the other
921
+ * twelve. A field added to the base of `SerializedSchema` needs adding here and
922
+ * nowhere else.
923
+ *
924
+ * `type`, `opt` and whatever is particular to the schema stay at the call site
925
+ * - those differ per schema, so there is nothing to share.
926
+ */
927
+ var commonSchemaFields = {
916
928
  render: FieldRender,
929
+ preview: zod.z.literal(true).optional(),
930
+ // Whether the schema declares a `.validate()`. The function cannot serialize,
931
+ // so this flag is what tells the Studio to run the custom validators on the
932
+ // main thread against the real instance - dropped, and they never run at all.
933
+ // See `hasCustomValidate` in `ui/spa/validation/customValidate.ts`.
934
+ customValidate: zod.z["boolean"]().optional(),
935
+ readonly: zod.z["boolean"]().optional(),
936
+ hidden: zod.z["boolean"]().optional(),
937
+ description: zod.z.string().optional()
938
+ };
939
+ var SerializedStringSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
940
+ type: zod.z.literal("string"),
917
941
  // `.multiline()`. Stripped like any undeclared key if it goes missing here,
918
942
  // which is a single-line input where the author asked for a text box.
919
943
  multiline: zod.z["boolean"]().optional(),
920
- preview: zod.z.literal(true).optional(),
921
944
  options: zod.z.object({
922
945
  maxLength: zod.z.number().optional(),
923
946
  minLength: zod.z.number().optional(),
924
947
  regexp: zod.z.object({
948
+ // `.regexp(re, message)`'s message. Written by
949
+ // `StringSchema.executeSerialize`, so omitting it here strips the
950
+ // author's own wording and leaves the generic "Expected string to
951
+ // match reg exp: …" in its place.
952
+ message: zod.z.string().optional(),
925
953
  source: zod.z.string(),
926
954
  flags: zod.z.string()
927
- }).optional()
955
+ }).optional(),
956
+ customValidate: zod.z["boolean"]().optional()
928
957
  }).optional(),
929
958
  opt: zod.z["boolean"](),
930
- raw: zod.z["boolean"](),
931
- readonly: zod.z["boolean"]().optional(),
932
- hidden: zod.z["boolean"]().optional()
933
- });
934
- var SerializedLiteralSchema = zod.z.object({
959
+ raw: zod.z["boolean"]()
960
+ }));
961
+ var SerializedLiteralSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
935
962
  type: zod.z.literal("literal"),
936
- render: FieldRender,
937
- preview: zod.z.literal(true).optional(),
938
963
  value: zod.z.string(),
939
- opt: zod.z["boolean"](),
940
- readonly: zod.z["boolean"]().optional(),
941
- hidden: zod.z["boolean"]().optional()
942
- });
943
- var SerializedBooleanSchema = zod.z.object({
964
+ opt: zod.z["boolean"]()
965
+ }));
966
+ var SerializedBooleanSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
944
967
  type: zod.z.literal("boolean"),
945
- render: FieldRender,
946
- preview: zod.z.literal(true).optional(),
947
- opt: zod.z["boolean"](),
948
- readonly: zod.z["boolean"]().optional(),
949
- hidden: zod.z["boolean"]().optional()
950
- });
951
- var SerializedNumberSchema = zod.z.object({
968
+ opt: zod.z["boolean"]()
969
+ }));
970
+ var SerializedNumberSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
952
971
  type: zod.z.literal("number"),
953
- render: FieldRender,
954
- preview: zod.z.literal(true).optional(),
955
972
  options: zod.z.object({
956
973
  max: zod.z.number().optional(),
957
974
  min: zod.z.number().optional()
958
975
  }).optional(),
959
- opt: zod.z["boolean"](),
960
- readonly: zod.z["boolean"]().optional(),
961
- hidden: zod.z["boolean"]().optional()
962
- });
976
+ opt: zod.z["boolean"]()
977
+ }));
963
978
  var SerializedObjectSchema = zod.z.lazy(function () {
964
- return zod.z.object({
979
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
965
980
  type: zod.z.literal("object"),
966
- render: FieldRender,
967
- preview: zod.z.literal(true).optional(),
968
981
  items: zod.z.record(zod.z.string(), SerializedSchema),
969
- opt: zod.z["boolean"](),
970
- readonly: zod.z["boolean"]().optional(),
971
- hidden: zod.z["boolean"]().optional()
972
- });
982
+ opt: zod.z["boolean"]()
983
+ }));
973
984
  });
974
985
  var SerializedArraySchema = zod.z.lazy(function () {
975
- return zod.z.object({
986
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
976
987
  type: zod.z.literal("array"),
977
- render: FieldRender,
978
- preview: zod.z.literal(true).optional(),
979
988
  item: SerializedSchema,
980
- opt: zod.z["boolean"](),
981
- readonly: zod.z["boolean"]().optional(),
982
- hidden: zod.z["boolean"]().optional()
983
- });
989
+ opt: zod.z["boolean"]()
990
+ }));
984
991
  });
985
992
  var SerializedUnionSchema = zod.z.lazy(function () {
986
- return zod.z.union([zod.z.object({
993
+ return zod.z.union([zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
987
994
  type: zod.z.literal("union"),
988
- render: FieldRender,
989
- preview: zod.z.literal(true).optional(),
990
995
  key: SerializedLiteralSchema,
991
996
  items: zod.z.array(SerializedLiteralSchema),
992
- opt: zod.z["boolean"](),
993
- readonly: zod.z["boolean"]().optional(),
994
- hidden: zod.z["boolean"]().optional()
995
- }), zod.z.object({
997
+ opt: zod.z["boolean"]()
998
+ })), zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
996
999
  type: zod.z.literal("union"),
997
- render: FieldRender,
998
- preview: zod.z.literal(true).optional(),
999
1000
  key: zod.z.string(),
1000
1001
  items: zod.z.array(SerializedObjectSchema),
1001
- opt: zod.z["boolean"](),
1002
- readonly: zod.z["boolean"]().optional(),
1003
- hidden: zod.z["boolean"]().optional()
1004
- })]);
1002
+ opt: zod.z["boolean"]()
1003
+ }))]);
1005
1004
  });
1006
1005
  var ImageEncodeOption = zod.z.union([zod.z.literal(false), zod.z.object({
1007
1006
  type: zod.z.literal("webp"),
@@ -1014,15 +1013,16 @@ var ImageOptions = zod.z.object({
1014
1013
  accept: zod.z.string().optional(),
1015
1014
  encode: ImageEncodeOption.optional()
1016
1015
  });
1017
- var SerializedImageSchema = zod.z.object({
1016
+ var SerializedImageSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1018
1017
  type: zod.z.literal("image"),
1019
- render: FieldRender,
1020
- preview: zod.z.literal(true).optional(),
1021
1018
  options: ImageOptions.optional(),
1022
1019
  opt: zod.z["boolean"](),
1023
- readonly: zod.z["boolean"]().optional(),
1024
- hidden: zod.z["boolean"]().optional()
1025
- });
1020
+ // `.remote()`, and the gallery a gallery-backed field reads its metadata
1021
+ // from. Neither is cosmetic: dropped, a remote field looks local and a
1022
+ // gallery-backed one looks like it holds its own dimensions.
1023
+ remote: zod.z["boolean"]().optional(),
1024
+ referencedModule: zod.z.string().optional()
1025
+ }));
1026
1026
  var RichTextOptions = zod.z.lazy(function () {
1027
1027
  return zod.z.object({
1028
1028
  // Set by `.maxLength()` / `.minLength()`, not by the options argument.
@@ -1045,20 +1045,14 @@ var RichTextOptions = zod.z.lazy(function () {
1045
1045
  img: zod.z.union([zod.z["boolean"](), SerializedImageSchema]).optional()
1046
1046
  });
1047
1047
  });
1048
- var SerializedRichTextSchema = zod.z.object({
1048
+ var SerializedRichTextSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1049
1049
  type: zod.z.literal("richtext"),
1050
- render: FieldRender,
1051
- preview: zod.z.literal(true).optional(),
1052
1050
  options: RichTextOptions.optional(),
1053
- opt: zod.z["boolean"](),
1054
- readonly: zod.z["boolean"]().optional(),
1055
- hidden: zod.z["boolean"]().optional()
1056
- });
1051
+ opt: zod.z["boolean"]()
1052
+ }));
1057
1053
  var SerializedRecordSchema = zod.z.lazy(function () {
1058
- return zod.z.object({
1054
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1059
1055
  type: zod.z.literal("record"),
1060
- render: FieldRender,
1061
- preview: zod.z.literal(true).optional(),
1062
1056
  item: SerializedSchema,
1063
1057
  opt: zod.z["boolean"](),
1064
1058
  // Optional gallery marker for files/images
@@ -1069,16 +1063,12 @@ var SerializedRecordSchema = zod.z.lazy(function () {
1069
1063
  remote: zod.z["boolean"]().optional(),
1070
1064
  encode: ImageEncodeOption.optional(),
1071
1065
  alt: SerializedSchema.optional(),
1072
- moduleMetadata: zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.any())).optional(),
1073
- readonly: zod.z["boolean"]().optional(),
1074
- hidden: zod.z["boolean"]().optional()
1075
- }).passthrough();
1066
+ moduleMetadata: zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.any())).optional()
1067
+ })).passthrough();
1076
1068
  });
1077
1069
  var SerializedKeyOfSchema = zod.z.lazy(function () {
1078
- return zod.z.object({
1070
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1079
1071
  type: zod.z.literal("keyOf"),
1080
- render: FieldRender,
1081
- preview: zod.z.literal(true).optional(),
1082
1072
  path: SourcePath,
1083
1073
  schema: zod.z.union([zod.z.object({
1084
1074
  type: zod.z.literal("object"),
@@ -1089,82 +1079,54 @@ var SerializedKeyOfSchema = zod.z.lazy(function () {
1089
1079
  opt: zod.z["boolean"]().optional()
1090
1080
  })]).optional(),
1091
1081
  values: zod.z.union([zod.z.literal("string"), zod.z.array(zod.z.string())]),
1092
- opt: zod.z["boolean"](),
1093
- readonly: zod.z["boolean"]().optional(),
1094
- hidden: zod.z["boolean"]().optional()
1095
- });
1082
+ opt: zod.z["boolean"]()
1083
+ }));
1096
1084
  });
1097
1085
  var FileOptions = zod.z.object({
1098
1086
  accept: zod.z.string().optional()
1099
1087
  });
1100
- var SerializedFileSchema = zod.z.object({
1088
+ var SerializedFileSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1101
1089
  type: zod.z.literal("file"),
1102
- render: FieldRender,
1103
- preview: zod.z.literal(true).optional(),
1104
1090
  options: FileOptions.optional(),
1105
1091
  opt: zod.z["boolean"](),
1106
- readonly: zod.z["boolean"]().optional(),
1107
- hidden: zod.z["boolean"]().optional()
1108
- });
1092
+ // See `SerializedImageSchema` above: `.remote()` and the gallery a
1093
+ // gallery-backed field reads from.
1094
+ remote: zod.z["boolean"]().optional(),
1095
+ referencedModule: zod.z.string().optional()
1096
+ }));
1109
1097
  var DateOptions = zod.z.object({
1110
1098
  from: zod.z.string().optional(),
1111
1099
  to: zod.z.string().optional()
1112
1100
  });
1113
- var SerializedDateSchema = zod.z.object({
1101
+ var SerializedDateSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1114
1102
  type: zod.z.literal("date"),
1115
- render: FieldRender,
1116
- preview: zod.z.literal(true).optional(),
1117
1103
  options: DateOptions.optional(),
1118
- opt: zod.z["boolean"](),
1119
- customValidate: zod.z["boolean"]().optional(),
1120
- readonly: zod.z["boolean"]().optional(),
1121
- hidden: zod.z["boolean"]().optional(),
1122
- description: zod.z.string().optional()
1123
- });
1124
- var SerializedDateTimeSchema = zod.z.object({
1104
+ opt: zod.z["boolean"]()
1105
+ }));
1106
+ var SerializedDateTimeSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1125
1107
  type: zod.z.literal("dateTime"),
1126
- render: FieldRender,
1127
- preview: zod.z.literal(true).optional(),
1128
1108
  options: DateOptions.optional(),
1129
- opt: zod.z["boolean"](),
1130
- customValidate: zod.z["boolean"]().optional(),
1131
- readonly: zod.z["boolean"]().optional(),
1132
- hidden: zod.z["boolean"]().optional(),
1133
- description: zod.z.string().optional()
1134
- });
1109
+ opt: zod.z["boolean"]()
1110
+ }));
1135
1111
  var ColorOptions = zod.z.object({
1136
1112
  format: zod.z["enum"](["hex", "rgb", "hsl", "oklch"]).optional(),
1137
1113
  alpha: zod.z["boolean"]().optional()
1138
1114
  });
1139
- var SerializedColorSchema = zod.z.object({
1115
+ var SerializedColorSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1140
1116
  type: zod.z.literal("color"),
1141
- render: FieldRender,
1142
- preview: zod.z.literal(true).optional(),
1143
1117
  options: ColorOptions.optional(),
1144
- opt: zod.z["boolean"](),
1145
- customValidate: zod.z["boolean"]().optional(),
1146
- readonly: zod.z["boolean"]().optional(),
1147
- hidden: zod.z["boolean"]().optional(),
1148
- description: zod.z.string().optional()
1149
- });
1118
+ opt: zod.z["boolean"]()
1119
+ }));
1150
1120
  var CodeOptions = zod.z.object({
1151
1121
  language: zod.z["enum"](core.CODE_LANGUAGES).optional()
1152
1122
  });
1153
- var SerializedCodeSchema = zod.z.object({
1123
+ var SerializedCodeSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1154
1124
  type: zod.z.literal("code"),
1155
- render: FieldRender,
1156
- preview: zod.z.literal(true).optional(),
1157
1125
  options: CodeOptions.optional(),
1158
- opt: zod.z["boolean"](),
1159
- customValidate: zod.z["boolean"]().optional(),
1160
- readonly: zod.z["boolean"]().optional(),
1161
- hidden: zod.z["boolean"]().optional(),
1162
- description: zod.z.string().optional()
1163
- });
1164
- var SerializedRouteSchema = zod.z.object({
1126
+ opt: zod.z["boolean"]()
1127
+ }));
1128
+ var SerializedRouteSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1165
1129
  type: zod.z.literal("route"),
1166
- render: FieldRender,
1167
- preview: zod.z.literal(true).optional(),
1168
1130
  options: zod.z.object({
1169
1131
  include: zod.z.object({
1170
1132
  source: zod.z.string(),
@@ -1176,11 +1138,8 @@ var SerializedRouteSchema = zod.z.object({
1176
1138
  }).optional(),
1177
1139
  customValidate: zod.z["boolean"]().optional()
1178
1140
  }).optional(),
1179
- opt: zod.z["boolean"](),
1180
- customValidate: zod.z["boolean"]().optional(),
1181
- readonly: zod.z["boolean"]().optional(),
1182
- hidden: zod.z["boolean"]().optional()
1183
- });
1141
+ opt: zod.z["boolean"]()
1142
+ }));
1184
1143
 
1185
1144
  // A settings module, and each section inside one, serialize as this - the
1186
1145
  // shape is recursive because `items` holds sections which are themselves
@@ -1188,17 +1147,11 @@ var SerializedRouteSchema = zod.z.object({
1188
1147
  // (see core/src/schema/settings.ts), but they are accepted here because they
1189
1148
  // are part of the declared type.
1190
1149
  var SerializedSettingsSchema = zod.z.lazy(function () {
1191
- return zod.z.object({
1150
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1192
1151
  type: zod.z.literal("settings"),
1193
- render: FieldRender,
1194
- preview: zod.z.literal(true).optional(),
1195
1152
  items: zod.z.record(zod.z.string(), SerializedSchema),
1196
- opt: zod.z["boolean"](),
1197
- customValidate: zod.z["boolean"]().optional(),
1198
- readonly: zod.z["boolean"]().optional(),
1199
- hidden: zod.z["boolean"]().optional(),
1200
- description: zod.z.string().optional()
1201
- });
1153
+ opt: zod.z["boolean"]()
1154
+ }));
1202
1155
  });
1203
1156
  var SerializedSchema = zod.z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedUnionSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
1204
1157
 
@@ -5329,6 +5282,28 @@ function getRefParts(ref) {
5329
5282
  * them — an agent and an editor searching the same project should find the same
5330
5283
  * things.
5331
5284
  */
5285
+ /**
5286
+ * The parts of FlexSearch's `Index` this module uses.
5287
+ *
5288
+ * Structural, rather than importing flexsearch's own `Index`, because
5289
+ * {@link SearchIndex} is EXPORTED — and a `.d.ts` that names `flexsearch` pulls
5290
+ * that package's `declare module "flexsearch"` into every consumer's TypeScript
5291
+ * program. Ambient module declarations are global to a program, so a consumer
5292
+ * with its own flexsearch at another version then checks its OWN calls against
5293
+ * the copy we dragged in, whichever of the two wins.
5294
+ *
5295
+ * That is not hypothetical: `@valbuild/shared` gained a flexsearch dependency
5296
+ * in 0.123.0 for `search_content`, and valbuild/web — which pins flexsearch
5297
+ * 0.7 and calls `new flexsearch.Document({ language: "en", … })` — stopped
5298
+ * building on 0.123.2, because 0.8's `DocumentOptions` has no `language`. Its
5299
+ * own resolved copy was still 0.7; only the types had been replaced.
5300
+ *
5301
+ * Three methods is all this module calls, and none of their signatures here is
5302
+ * a guess: `add` and `remove` are used in `indexModule`/`removeModule`, and
5303
+ * `search`'s result is only ever `.length`-ed, `.slice`-d and mapped over as
5304
+ * ids. `FlexSearch.Index` is still constructed with the real library in
5305
+ * {@link createSearchIndex} — a value import, which does not reach the `.d.ts`.
5306
+ */
5332
5307
 
5333
5308
  /** An index with nothing in it. Fill it with {@link indexModule}. */
5334
5309
  function createSearchIndex() {
@@ -5430,9 +5405,9 @@ function removeModule(searchIndex, moduleFilePath) {
5430
5405
  _step;
5431
5406
  try {
5432
5407
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
5433
- var id = _step.value;
5434
- index.remove(id);
5435
- pathToLabel["delete"](id);
5408
+ var _id = _step.value;
5409
+ index.remove(_id);
5410
+ pathToLabel["delete"](_id);
5436
5411
  }
5437
5412
  } catch (err) {
5438
5413
  _iterator.e(err);
@@ -904,97 +904,96 @@ var InlineRender = z.object({
904
904
  as: z.literal("inline")
905
905
  });
906
906
  var FieldRender = InlineRender.optional();
907
- var SerializedStringSchema = z.object({
908
- type: z.literal("string"),
907
+
908
+ /**
909
+ * The fields EVERY serialized schema carries, in one place.
910
+ *
911
+ * Spread into each schema below rather than retyped, because retyping them is
912
+ * exactly how they went missing: `customValidate` and `description` were
913
+ * declared on six of the eighteen schemas and silently dropped by the other
914
+ * twelve. A field added to the base of `SerializedSchema` needs adding here and
915
+ * nowhere else.
916
+ *
917
+ * `type`, `opt` and whatever is particular to the schema stay at the call site
918
+ * - those differ per schema, so there is nothing to share.
919
+ */
920
+ var commonSchemaFields = {
909
921
  render: FieldRender,
922
+ preview: z.literal(true).optional(),
923
+ // Whether the schema declares a `.validate()`. The function cannot serialize,
924
+ // so this flag is what tells the Studio to run the custom validators on the
925
+ // main thread against the real instance - dropped, and they never run at all.
926
+ // See `hasCustomValidate` in `ui/spa/validation/customValidate.ts`.
927
+ customValidate: z["boolean"]().optional(),
928
+ readonly: z["boolean"]().optional(),
929
+ hidden: z["boolean"]().optional(),
930
+ description: z.string().optional()
931
+ };
932
+ var SerializedStringSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
933
+ type: z.literal("string"),
910
934
  // `.multiline()`. Stripped like any undeclared key if it goes missing here,
911
935
  // which is a single-line input where the author asked for a text box.
912
936
  multiline: z["boolean"]().optional(),
913
- preview: z.literal(true).optional(),
914
937
  options: z.object({
915
938
  maxLength: z.number().optional(),
916
939
  minLength: z.number().optional(),
917
940
  regexp: z.object({
941
+ // `.regexp(re, message)`'s message. Written by
942
+ // `StringSchema.executeSerialize`, so omitting it here strips the
943
+ // author's own wording and leaves the generic "Expected string to
944
+ // match reg exp: …" in its place.
945
+ message: z.string().optional(),
918
946
  source: z.string(),
919
947
  flags: z.string()
920
- }).optional()
948
+ }).optional(),
949
+ customValidate: z["boolean"]().optional()
921
950
  }).optional(),
922
951
  opt: z["boolean"](),
923
- raw: z["boolean"](),
924
- readonly: z["boolean"]().optional(),
925
- hidden: z["boolean"]().optional()
926
- });
927
- var SerializedLiteralSchema = z.object({
952
+ raw: z["boolean"]()
953
+ }));
954
+ var SerializedLiteralSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
928
955
  type: z.literal("literal"),
929
- render: FieldRender,
930
- preview: z.literal(true).optional(),
931
956
  value: z.string(),
932
- opt: z["boolean"](),
933
- readonly: z["boolean"]().optional(),
934
- hidden: z["boolean"]().optional()
935
- });
936
- var SerializedBooleanSchema = z.object({
957
+ opt: z["boolean"]()
958
+ }));
959
+ var SerializedBooleanSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
937
960
  type: z.literal("boolean"),
938
- render: FieldRender,
939
- preview: z.literal(true).optional(),
940
- opt: z["boolean"](),
941
- readonly: z["boolean"]().optional(),
942
- hidden: z["boolean"]().optional()
943
- });
944
- var SerializedNumberSchema = z.object({
961
+ opt: z["boolean"]()
962
+ }));
963
+ var SerializedNumberSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
945
964
  type: z.literal("number"),
946
- render: FieldRender,
947
- preview: z.literal(true).optional(),
948
965
  options: z.object({
949
966
  max: z.number().optional(),
950
967
  min: z.number().optional()
951
968
  }).optional(),
952
- opt: z["boolean"](),
953
- readonly: z["boolean"]().optional(),
954
- hidden: z["boolean"]().optional()
955
- });
969
+ opt: z["boolean"]()
970
+ }));
956
971
  var SerializedObjectSchema = z.lazy(function () {
957
- return z.object({
972
+ return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
958
973
  type: z.literal("object"),
959
- render: FieldRender,
960
- preview: z.literal(true).optional(),
961
974
  items: z.record(z.string(), SerializedSchema),
962
- opt: z["boolean"](),
963
- readonly: z["boolean"]().optional(),
964
- hidden: z["boolean"]().optional()
965
- });
975
+ opt: z["boolean"]()
976
+ }));
966
977
  });
967
978
  var SerializedArraySchema = z.lazy(function () {
968
- return z.object({
979
+ return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
969
980
  type: z.literal("array"),
970
- render: FieldRender,
971
- preview: z.literal(true).optional(),
972
981
  item: SerializedSchema,
973
- opt: z["boolean"](),
974
- readonly: z["boolean"]().optional(),
975
- hidden: z["boolean"]().optional()
976
- });
982
+ opt: z["boolean"]()
983
+ }));
977
984
  });
978
985
  var SerializedUnionSchema = z.lazy(function () {
979
- return z.union([z.object({
986
+ return z.union([z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
980
987
  type: z.literal("union"),
981
- render: FieldRender,
982
- preview: z.literal(true).optional(),
983
988
  key: SerializedLiteralSchema,
984
989
  items: z.array(SerializedLiteralSchema),
985
- opt: z["boolean"](),
986
- readonly: z["boolean"]().optional(),
987
- hidden: z["boolean"]().optional()
988
- }), z.object({
990
+ opt: z["boolean"]()
991
+ })), z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
989
992
  type: z.literal("union"),
990
- render: FieldRender,
991
- preview: z.literal(true).optional(),
992
993
  key: z.string(),
993
994
  items: z.array(SerializedObjectSchema),
994
- opt: z["boolean"](),
995
- readonly: z["boolean"]().optional(),
996
- hidden: z["boolean"]().optional()
997
- })]);
995
+ opt: z["boolean"]()
996
+ }))]);
998
997
  });
999
998
  var ImageEncodeOption = z.union([z.literal(false), z.object({
1000
999
  type: z.literal("webp"),
@@ -1007,15 +1006,16 @@ var ImageOptions = z.object({
1007
1006
  accept: z.string().optional(),
1008
1007
  encode: ImageEncodeOption.optional()
1009
1008
  });
1010
- var SerializedImageSchema = z.object({
1009
+ var SerializedImageSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1011
1010
  type: z.literal("image"),
1012
- render: FieldRender,
1013
- preview: z.literal(true).optional(),
1014
1011
  options: ImageOptions.optional(),
1015
1012
  opt: z["boolean"](),
1016
- readonly: z["boolean"]().optional(),
1017
- hidden: z["boolean"]().optional()
1018
- });
1013
+ // `.remote()`, and the gallery a gallery-backed field reads its metadata
1014
+ // from. Neither is cosmetic: dropped, a remote field looks local and a
1015
+ // gallery-backed one looks like it holds its own dimensions.
1016
+ remote: z["boolean"]().optional(),
1017
+ referencedModule: z.string().optional()
1018
+ }));
1019
1019
  var RichTextOptions = z.lazy(function () {
1020
1020
  return z.object({
1021
1021
  // Set by `.maxLength()` / `.minLength()`, not by the options argument.
@@ -1038,20 +1038,14 @@ var RichTextOptions = z.lazy(function () {
1038
1038
  img: z.union([z["boolean"](), SerializedImageSchema]).optional()
1039
1039
  });
1040
1040
  });
1041
- var SerializedRichTextSchema = z.object({
1041
+ var SerializedRichTextSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1042
1042
  type: z.literal("richtext"),
1043
- render: FieldRender,
1044
- preview: z.literal(true).optional(),
1045
1043
  options: RichTextOptions.optional(),
1046
- opt: z["boolean"](),
1047
- readonly: z["boolean"]().optional(),
1048
- hidden: z["boolean"]().optional()
1049
- });
1044
+ opt: z["boolean"]()
1045
+ }));
1050
1046
  var SerializedRecordSchema = z.lazy(function () {
1051
- return z.object({
1047
+ return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1052
1048
  type: z.literal("record"),
1053
- render: FieldRender,
1054
- preview: z.literal(true).optional(),
1055
1049
  item: SerializedSchema,
1056
1050
  opt: z["boolean"](),
1057
1051
  // Optional gallery marker for files/images
@@ -1062,16 +1056,12 @@ var SerializedRecordSchema = z.lazy(function () {
1062
1056
  remote: z["boolean"]().optional(),
1063
1057
  encode: ImageEncodeOption.optional(),
1064
1058
  alt: SerializedSchema.optional(),
1065
- moduleMetadata: z.record(z.string(), z.record(z.string(), z.any())).optional(),
1066
- readonly: z["boolean"]().optional(),
1067
- hidden: z["boolean"]().optional()
1068
- }).passthrough();
1059
+ moduleMetadata: z.record(z.string(), z.record(z.string(), z.any())).optional()
1060
+ })).passthrough();
1069
1061
  });
1070
1062
  var SerializedKeyOfSchema = z.lazy(function () {
1071
- return z.object({
1063
+ return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1072
1064
  type: z.literal("keyOf"),
1073
- render: FieldRender,
1074
- preview: z.literal(true).optional(),
1075
1065
  path: SourcePath,
1076
1066
  schema: z.union([z.object({
1077
1067
  type: z.literal("object"),
@@ -1082,82 +1072,54 @@ var SerializedKeyOfSchema = z.lazy(function () {
1082
1072
  opt: z["boolean"]().optional()
1083
1073
  })]).optional(),
1084
1074
  values: z.union([z.literal("string"), z.array(z.string())]),
1085
- opt: z["boolean"](),
1086
- readonly: z["boolean"]().optional(),
1087
- hidden: z["boolean"]().optional()
1088
- });
1075
+ opt: z["boolean"]()
1076
+ }));
1089
1077
  });
1090
1078
  var FileOptions = z.object({
1091
1079
  accept: z.string().optional()
1092
1080
  });
1093
- var SerializedFileSchema = z.object({
1081
+ var SerializedFileSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1094
1082
  type: z.literal("file"),
1095
- render: FieldRender,
1096
- preview: z.literal(true).optional(),
1097
1083
  options: FileOptions.optional(),
1098
1084
  opt: z["boolean"](),
1099
- readonly: z["boolean"]().optional(),
1100
- hidden: z["boolean"]().optional()
1101
- });
1085
+ // See `SerializedImageSchema` above: `.remote()` and the gallery a
1086
+ // gallery-backed field reads from.
1087
+ remote: z["boolean"]().optional(),
1088
+ referencedModule: z.string().optional()
1089
+ }));
1102
1090
  var DateOptions = z.object({
1103
1091
  from: z.string().optional(),
1104
1092
  to: z.string().optional()
1105
1093
  });
1106
- var SerializedDateSchema = z.object({
1094
+ var SerializedDateSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1107
1095
  type: z.literal("date"),
1108
- render: FieldRender,
1109
- preview: z.literal(true).optional(),
1110
1096
  options: DateOptions.optional(),
1111
- opt: z["boolean"](),
1112
- customValidate: z["boolean"]().optional(),
1113
- readonly: z["boolean"]().optional(),
1114
- hidden: z["boolean"]().optional(),
1115
- description: z.string().optional()
1116
- });
1117
- var SerializedDateTimeSchema = z.object({
1097
+ opt: z["boolean"]()
1098
+ }));
1099
+ var SerializedDateTimeSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1118
1100
  type: z.literal("dateTime"),
1119
- render: FieldRender,
1120
- preview: z.literal(true).optional(),
1121
1101
  options: DateOptions.optional(),
1122
- opt: z["boolean"](),
1123
- customValidate: z["boolean"]().optional(),
1124
- readonly: z["boolean"]().optional(),
1125
- hidden: z["boolean"]().optional(),
1126
- description: z.string().optional()
1127
- });
1102
+ opt: z["boolean"]()
1103
+ }));
1128
1104
  var ColorOptions = z.object({
1129
1105
  format: z["enum"](["hex", "rgb", "hsl", "oklch"]).optional(),
1130
1106
  alpha: z["boolean"]().optional()
1131
1107
  });
1132
- var SerializedColorSchema = z.object({
1108
+ var SerializedColorSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1133
1109
  type: z.literal("color"),
1134
- render: FieldRender,
1135
- preview: z.literal(true).optional(),
1136
1110
  options: ColorOptions.optional(),
1137
- opt: z["boolean"](),
1138
- customValidate: z["boolean"]().optional(),
1139
- readonly: z["boolean"]().optional(),
1140
- hidden: z["boolean"]().optional(),
1141
- description: z.string().optional()
1142
- });
1111
+ opt: z["boolean"]()
1112
+ }));
1143
1113
  var CodeOptions = z.object({
1144
1114
  language: z["enum"](CODE_LANGUAGES).optional()
1145
1115
  });
1146
- var SerializedCodeSchema = z.object({
1116
+ var SerializedCodeSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1147
1117
  type: z.literal("code"),
1148
- render: FieldRender,
1149
- preview: z.literal(true).optional(),
1150
1118
  options: CodeOptions.optional(),
1151
- opt: z["boolean"](),
1152
- customValidate: z["boolean"]().optional(),
1153
- readonly: z["boolean"]().optional(),
1154
- hidden: z["boolean"]().optional(),
1155
- description: z.string().optional()
1156
- });
1157
- var SerializedRouteSchema = z.object({
1119
+ opt: z["boolean"]()
1120
+ }));
1121
+ var SerializedRouteSchema = z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1158
1122
  type: z.literal("route"),
1159
- render: FieldRender,
1160
- preview: z.literal(true).optional(),
1161
1123
  options: z.object({
1162
1124
  include: z.object({
1163
1125
  source: z.string(),
@@ -1169,11 +1131,8 @@ var SerializedRouteSchema = z.object({
1169
1131
  }).optional(),
1170
1132
  customValidate: z["boolean"]().optional()
1171
1133
  }).optional(),
1172
- opt: z["boolean"](),
1173
- customValidate: z["boolean"]().optional(),
1174
- readonly: z["boolean"]().optional(),
1175
- hidden: z["boolean"]().optional()
1176
- });
1134
+ opt: z["boolean"]()
1135
+ }));
1177
1136
 
1178
1137
  // A settings module, and each section inside one, serialize as this - the
1179
1138
  // shape is recursive because `items` holds sections which are themselves
@@ -1181,17 +1140,11 @@ var SerializedRouteSchema = z.object({
1181
1140
  // (see core/src/schema/settings.ts), but they are accepted here because they
1182
1141
  // are part of the declared type.
1183
1142
  var SerializedSettingsSchema = z.lazy(function () {
1184
- return z.object({
1143
+ return z.object(_objectSpread2(_objectSpread2({}, commonSchemaFields), {}, {
1185
1144
  type: z.literal("settings"),
1186
- render: FieldRender,
1187
- preview: z.literal(true).optional(),
1188
1145
  items: z.record(z.string(), SerializedSchema),
1189
- opt: z["boolean"](),
1190
- customValidate: z["boolean"]().optional(),
1191
- readonly: z["boolean"]().optional(),
1192
- hidden: z["boolean"]().optional(),
1193
- description: z.string().optional()
1194
- });
1146
+ opt: z["boolean"]()
1147
+ }));
1195
1148
  });
1196
1149
  var SerializedSchema = z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedUnionSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
1197
1150
 
@@ -5322,6 +5275,28 @@ function getRefParts(ref) {
5322
5275
  * them — an agent and an editor searching the same project should find the same
5323
5276
  * things.
5324
5277
  */
5278
+ /**
5279
+ * The parts of FlexSearch's `Index` this module uses.
5280
+ *
5281
+ * Structural, rather than importing flexsearch's own `Index`, because
5282
+ * {@link SearchIndex} is EXPORTED — and a `.d.ts` that names `flexsearch` pulls
5283
+ * that package's `declare module "flexsearch"` into every consumer's TypeScript
5284
+ * program. Ambient module declarations are global to a program, so a consumer
5285
+ * with its own flexsearch at another version then checks its OWN calls against
5286
+ * the copy we dragged in, whichever of the two wins.
5287
+ *
5288
+ * That is not hypothetical: `@valbuild/shared` gained a flexsearch dependency
5289
+ * in 0.123.0 for `search_content`, and valbuild/web — which pins flexsearch
5290
+ * 0.7 and calls `new flexsearch.Document({ language: "en", … })` — stopped
5291
+ * building on 0.123.2, because 0.8's `DocumentOptions` has no `language`. Its
5292
+ * own resolved copy was still 0.7; only the types had been replaced.
5293
+ *
5294
+ * Three methods is all this module calls, and none of their signatures here is
5295
+ * a guess: `add` and `remove` are used in `indexModule`/`removeModule`, and
5296
+ * `search`'s result is only ever `.length`-ed, `.slice`-d and mapped over as
5297
+ * ids. `FlexSearch.Index` is still constructed with the real library in
5298
+ * {@link createSearchIndex} — a value import, which does not reach the `.d.ts`.
5299
+ */
5325
5300
 
5326
5301
  /** An index with nothing in it. Fill it with {@link indexModule}. */
5327
5302
  function createSearchIndex() {
@@ -5423,9 +5398,9 @@ function removeModule(searchIndex, moduleFilePath) {
5423
5398
  _step;
5424
5399
  try {
5425
5400
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
5426
- var id = _step.value;
5427
- index.remove(id);
5428
- pathToLabel["delete"](id);
5401
+ var _id = _step.value;
5402
+ index.remove(_id);
5403
+ pathToLabel["delete"](_id);
5429
5404
  }
5430
5405
  } catch (err) {
5431
5406
  _iterator.e(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valbuild/shared",
3
- "version": "0.123.2",
3
+ "version": "0.123.3",
4
4
  "private": false,
5
5
  "description": "Val shared types and utilities",
6
6
  "repository": {