@valbuild/shared 0.125.0 → 0.126.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,129 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.126.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#652](https://github.com/valbuild/val/pull/652) [`f2fe70d`](https://github.com/valbuild/val/commit/f2fe70dab2b65000dfaf289f09c70b4a8291467a) Thanks [@freekh](https://github.com/freekh)! - `s.union` is now `s.discriminatedUnion` and `s.enum`.
8
+
9
+ `s.union` did two unrelated jobs and worked out which one you meant from its
10
+ first argument: a string key meant a tagged union of objects, literal schemas
11
+ meant a set of allowed strings. Those are now two schemas with two names.
12
+
13
+ ```ts
14
+ // A fixed set of strings — presents as a dropdown
15
+ s.enum("primary", "secondary", "ghost"); // Schema<"primary" | "secondary" | "ghost">
16
+
17
+ // One of several object shapes, told apart by a tag field
18
+ s.discriminatedUnion(
19
+ "type",
20
+ s.object({ type: s.literal("hero"), heading: s.string() }),
21
+ s.object({ type: s.literal("quote"), text: s.string() }),
22
+ );
23
+ ```
24
+
25
+ `s.enum` takes the strings directly, so the `s.literal(...)` wrapper is gone.
26
+
27
+ **`s.union` still works** — it is deprecated, and it builds exactly the schema
28
+ above, so nothing has to change today:
29
+
30
+ ```ts
31
+ s.union(s.literal("one"), s.literal("two")); // → s.enum("one", "two")
32
+ s.union("type", pageA, pageB); // → s.discriminatedUnion("type", pageA, pageB)
33
+ ```
34
+
35
+ The two are different kinds of node, and that is the reason for the split. A
36
+ discriminated union is a container: the selected variant's fields are the fields
37
+ being edited, and everything that walks a schema descends through it. An enum is
38
+ a leaf — a string with a closed domain — so nothing recurses into it. Told apart
39
+ only by the shape of `key`, every consumer had to re-derive which one it was
40
+ holding; each now has its own serialized type (`"discriminated-union"` and
41
+ `"enum"`) and Val Studio has a field per kind rather than one field that
42
+ branches.
43
+
44
+ Two behaviour changes fall out of the split, both of them fixes:
45
+
46
+ - A value that is not a string at all now fails an enum's validation with a
47
+ type error. `s.union` of literals only ever checked the value against its
48
+ literals when the value WAS a string, so a number or an object where an enum
49
+ was declared validated clean.
50
+ - An enum field now shows its validation errors in Val Studio where the field
51
+ is opened on its own — the module editor and the canvas's fields column — and
52
+ gets the compact error layout inside an inline list row. It is a leaf now, so
53
+ it goes through the same error rendering as every other leaf field; the string
54
+ union bypassed it and showed nothing in those places.
55
+
56
+ Several latent crashes in the old `s.union` are fixed on the way past, all of
57
+ them cases where it threw a `TypeError` instead of reporting:
58
+
59
+ - A required discriminated union holding `null` now reports a type error rather
60
+ than throwing, and resolving a path underneath a nullable one that is `null`
61
+ gives the error the API promises instead of a crash.
62
+ - `s.literal("")` is a legal discriminator tag, and `s.enum("")` a legal value.
63
+ Both used to be treated as absent by a truthiness check — in path resolution,
64
+ in stega encoding, and in the message that lists a union's valid tags. The
65
+ editor's dropdowns handle them too: an empty value is reserved by the select
66
+ component and had to be mapped around.
67
+ - A variant that omits the discriminator entirely is now reported as the schema
68
+ error it is, instead of throwing while the check looked for it.
69
+ - An enum's value is now indexed for search, like every other string leaf. The
70
+ old string union was never indexed at all, so searching for one of its values
71
+ could not find the field.
72
+ - A nullable discriminated union set to `null` no longer renders a spinner that
73
+ never resolves.
74
+
75
+ `s.discriminatedUnion` also requires at least one variant, as `s.enum` requires
76
+ at least one value: a union with nothing to select is not a thing to write, and
77
+ everything downstream reads the first variant where it needs any.
78
+
79
+ If you read serialized schemas yourself, that is the breaking part: `type` is no
80
+ longer `"union"`, an enum carries `values: string[]` instead of a `key` plus
81
+ `items` of literal schemas, and `UnionSchema` is no longer a class.
82
+ `SerializedUnionSchema`, `SerializedStringUnionSchema`,
83
+ `SerializedObjectUnionSchema` and `UnionSchema` remain as deprecated type
84
+ aliases.
85
+
86
+ ### Patch Changes
87
+
88
+ - [#664](https://github.com/valbuild/val/pull/664) [`5bfd630`](https://github.com/valbuild/val/commit/5bfd630b63dee2189e238f20fe72ecc5537160f7) Thanks [@freekh](https://github.com/freekh)! - Show the git message on deployments Val did not publish
89
+
90
+ The deploy feed could only name a publish when Val itself had made the commit:
91
+ the message came off Val's own `ValCommit`, and every other deployment — a
92
+ developer's push, a merged pull request, a revert — showed a seven-character
93
+ sha. On most projects those are the majority, so "what went out at 14:02?" had
94
+ no answer in the Studio.
95
+
96
+ A deployment can now carry its own `commitMessage`, which the Studio uses
97
+ wherever there is no Val commit to prefer. It is optional and nullable, so a
98
+ content service that does not report messages is unaffected — those publishes
99
+ keep showing the short sha, exactly as before.
100
+
101
+ Deployment rows also show only the subject line of a message now. A git message
102
+ is a subject, a blank line and a body, and the rows are one truncated line — so
103
+ a real push arrived as "Subject The body went on like this…". The classic
104
+ Draft changes view still has the whole message in its tooltip.
105
+
106
+ - [#659](https://github.com/valbuild/val/pull/659) [`ccbcda6`](https://github.com/valbuild/val/commit/ccbcda60b3e3c465071229ae1ba28ac735483e63) Thanks [@freekh](https://github.com/freekh)! - Discarding changes now clears the validation errors, previews and search results
107
+ that were computed from them.
108
+
109
+ A discard that removed a module's last pending change announced itself only as
110
+ a "drop", and the stores that keep validation results, previews and the search
111
+ index listened only for changes being applied. So after discarding, the Studio
112
+ kept showing the errors and previews of the discarded edit until something else
113
+ touched the module.
114
+
115
+ Fields that reference another module's keys (`s.keyOf(...)` and `s.route()`)
116
+ also follow that module now. Renaming a page and then discarding the rename left
117
+ every `keyOf` field pointing at it reporting that the key does not exist — about
118
+ a key that was back — because the module holding the reference had not changed
119
+ and was never re-checked. The validation store now tracks which records a
120
+ module's errors resolve against and re-checks it when their keys change, and only
121
+ then: editing content inside a referenced page does not re-validate its
122
+ referrers.
123
+
124
+ - Updated dependencies [[`f2fe70d`](https://github.com/valbuild/val/commit/f2fe70dab2b65000dfaf289f09c70b4a8291467a), [`171208a`](https://github.com/valbuild/val/commit/171208a20177e68ed5a8b1a6fdaabfe893a6aa5f)]:
125
+ - @valbuild/core@0.126.0
126
+
3
127
  ## 0.125.0
4
128
 
5
129
  ### Minor Changes
@@ -443,6 +443,14 @@ export declare const Api: {
443
443
  createdAt: z.ZodString;
444
444
  commitMessage: z.ZodNullable<z.ZodString>;
445
445
  }, z.core.$strip>>;
446
+ deployments: z.ZodOptional<z.ZodArray<z.ZodObject<{
447
+ deploymentId: z.ZodString;
448
+ deploymentState: z.ZodString;
449
+ commitSha: z.ZodString;
450
+ updatedAt: z.ZodString;
451
+ createdAt: z.ZodString;
452
+ commitMessage: z.ZodOptional<z.ZodNullable<z.ZodString>>;
453
+ }, z.core.$strip>>>;
446
454
  config: z.ZodObject<{
447
455
  project: z.ZodOptional<z.ZodString>;
448
456
  root: z.ZodOptional<z.ZodString>;
@@ -14,12 +14,37 @@ export declare function findSimilar(key: string, targets: string[]): {
14
14
  target: string;
15
15
  distance: number;
16
16
  }[];
17
+ /**
18
+ * The record a `keyof:check-keys` error resolves against — the value and the
19
+ * schema at the error's `sourcePath`.
20
+ *
21
+ * Exported for the one consumer that has to know what a resolution DEPENDED
22
+ * on rather than what it concluded: `ValidationStore` re-reads the keys here
23
+ * to decide whether a change to the referenced module can have moved the
24
+ * answer. Keep this and {@link checkKeyIsValid} reading the same record, or the
25
+ * store will invalidate on one record and the resolution answer from another.
26
+ */
27
+ export declare function getKeyOfRecordAt(sourcePath: SourcePath, snapshot: SchemaSourceSnapshot): {
28
+ source: unknown;
29
+ schema: SerializedSchema | undefined;
30
+ };
17
31
  export type ResolvedFix = {
18
32
  status: "resolved";
19
33
  } | {
20
34
  status: "remaining";
21
35
  error: ValidationError;
22
36
  };
37
+ /**
38
+ * The record a `keyof:check-keys` error resolves against, or null for any other
39
+ * error — including a malformed marker, which
40
+ * {@link resolveSchemaSourceFixForError} reports and this has nothing to say
41
+ * about.
42
+ *
43
+ * For a consumer that needs to know what the resolution READS, not what it
44
+ * concludes: `ValidationStore` keeps this per validated module, so that a change
45
+ * to the referenced record can invalidate the module holding the reference.
46
+ */
47
+ export declare function keyOfRecordPath(error: ValidationError): SourcePath | null;
23
48
  /**
24
49
  * Resolves a single `keyof:check-keys` or `router:check-route` error against
25
50
  * the given schema/source snapshot. Returns:
@@ -1,12 +1,13 @@
1
1
  import { z } from "zod";
2
- import { type SerializedSchema as SerializedSchemaT, type SerializedStringSchema as SerializedStringSchemaT, type SerializedLiteralSchema as SerializedLiteralSchemaT, type SerializedBooleanSchema as SerializedBooleanSchemaT, type SerializedNumberSchema as SerializedNumberSchemaT, type SerializedObjectSchema as SerializedObjectSchemaT, type SerializedArraySchema as SerializedArraySchemaT, type SerializedUnionSchema as SerializedUnionSchemaT, type SerializedRichTextSchema as SerializedRichTextSchemaT, type SerializedRichTextOptions as SerializedRichTextOptionsT, type SerializedRecordSchema as SerializedRecordSchemaT, type SerializedKeyOfSchema as SerializedKeyOfSchemaT, type SerializedRouteSchema as SerializedRouteSchemaT, type SerializedFileSchema as SerializedFileSchemaT, type SerializedDateSchema as SerializedDateSchemaT, type SerializedDateTimeSchema as SerializedDateTimeSchemaT, type SerializedColorSchema as SerializedColorSchemaT, type SerializedCodeSchema as SerializedCodeSchemaT, type SerializedImageSchema as SerializedImageSchemaT, type SerializedSettingsSchema as SerializedSettingsSchemaT } from "@valbuild/core";
2
+ import { type SerializedSchema as SerializedSchemaT, type SerializedStringSchema as SerializedStringSchemaT, type SerializedLiteralSchema as SerializedLiteralSchemaT, type SerializedBooleanSchema as SerializedBooleanSchemaT, type SerializedNumberSchema as SerializedNumberSchemaT, type SerializedObjectSchema as SerializedObjectSchemaT, type SerializedArraySchema as SerializedArraySchemaT, type SerializedDiscriminatedUnionSchema as SerializedDiscriminatedUnionSchemaT, type SerializedEnumSchema as SerializedEnumSchemaT, type SerializedRichTextSchema as SerializedRichTextSchemaT, type SerializedRichTextOptions as SerializedRichTextOptionsT, type SerializedRecordSchema as SerializedRecordSchemaT, type SerializedKeyOfSchema as SerializedKeyOfSchemaT, type SerializedRouteSchema as SerializedRouteSchemaT, type SerializedFileSchema as SerializedFileSchemaT, type SerializedDateSchema as SerializedDateSchemaT, type SerializedDateTimeSchema as SerializedDateTimeSchemaT, type SerializedColorSchema as SerializedColorSchemaT, type SerializedCodeSchema as SerializedCodeSchemaT, type SerializedImageSchema as SerializedImageSchemaT, type SerializedSettingsSchema as SerializedSettingsSchemaT } from "@valbuild/core";
3
3
  export declare const SerializedStringSchema: z.ZodType<SerializedStringSchemaT>;
4
4
  export declare const SerializedLiteralSchema: z.ZodType<SerializedLiteralSchemaT>;
5
5
  export declare const SerializedBooleanSchema: z.ZodType<SerializedBooleanSchemaT>;
6
6
  export declare const SerializedNumberSchema: z.ZodType<SerializedNumberSchemaT>;
7
7
  export declare const SerializedObjectSchema: z.ZodType<SerializedObjectSchemaT>;
8
8
  export declare const SerializedArraySchema: z.ZodType<SerializedArraySchemaT>;
9
- export declare const SerializedUnionSchema: z.ZodType<SerializedUnionSchemaT>;
9
+ export declare const SerializedDiscriminatedUnionSchema: z.ZodType<SerializedDiscriminatedUnionSchemaT>;
10
+ export declare const SerializedEnumSchema: z.ZodType<SerializedEnumSchemaT>;
10
11
  export declare const ImageEncodeOption: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
11
12
  type: z.ZodLiteral<"webp">;
12
13
  quality: z.ZodOptional<z.ZodNumber>;
@@ -5,5 +5,6 @@ export declare const ValDeployment: z.ZodObject<{
5
5
  commitSha: z.ZodString;
6
6
  updatedAt: z.ZodString;
7
7
  createdAt: z.ZodString;
8
+ commitMessage: z.ZodOptional<z.ZodNullable<z.ZodString>>;
8
9
  }, z.core.$strip>;
9
10
  export type ValDeployment = z.infer<typeof ValDeployment>;
@@ -989,19 +989,19 @@ var SerializedArraySchema = zod.z.lazy(function () {
989
989
  opt: zod.z["boolean"]()
990
990
  }));
991
991
  });
992
- var SerializedUnionSchema = zod.z.lazy(function () {
993
- return zod.z.union([zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
994
- type: zod.z.literal("union"),
995
- key: SerializedLiteralSchema,
996
- items: zod.z.array(SerializedLiteralSchema),
997
- opt: zod.z["boolean"]()
998
- })), zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
999
- type: zod.z.literal("union"),
992
+ var SerializedDiscriminatedUnionSchema = zod.z.lazy(function () {
993
+ return zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
994
+ type: zod.z.literal("discriminated-union"),
1000
995
  key: zod.z.string(),
1001
996
  items: zod.z.array(SerializedObjectSchema),
1002
997
  opt: zod.z["boolean"]()
1003
- }))]);
998
+ }));
1004
999
  });
1000
+ var SerializedEnumSchema = zod.z.object(getSitemapTree._objectSpread2(getSitemapTree._objectSpread2({}, commonSchemaFields), {}, {
1001
+ type: zod.z.literal("enum"),
1002
+ values: zod.z.array(zod.z.string()),
1003
+ opt: zod.z["boolean"]()
1004
+ }));
1005
1005
  var ImageEncodeOption = zod.z.union([zod.z.literal(false), zod.z.object({
1006
1006
  type: zod.z.literal("webp"),
1007
1007
  quality: zod.z.number().optional(),
@@ -1153,7 +1153,7 @@ var SerializedSettingsSchema = zod.z.lazy(function () {
1153
1153
  opt: zod.z["boolean"]()
1154
1154
  }));
1155
1155
  });
1156
- var SerializedSchema = zod.z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedUnionSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
1156
+ var SerializedSchema = zod.z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
1157
1157
 
1158
1158
  var ValCommit = zod.z.object({
1159
1159
  commitSha: zod.z.string(),
@@ -1165,6 +1165,31 @@ var ValCommit = zod.z.object({
1165
1165
  commitMessage: zod.z.string().nullable()
1166
1166
  });
1167
1167
 
1168
+ var ValDeployment = zod.z.object({
1169
+ deploymentId: zod.z.string(),
1170
+ deploymentState: zod.z.string(),
1171
+ commitSha: zod.z.string(),
1172
+ updatedAt: zod.z.string(),
1173
+ createdAt: zod.z.string(),
1174
+ /**
1175
+ * The commit message, as the deployment's host reported it.
1176
+ *
1177
+ * Val's OWN publishes carry their message on the commit (`ValCommit`), and
1178
+ * that is the one the Studio prefers — it is the message Val wrote. This is
1179
+ * for every OTHER deployment: a developer's push, a merged pull request, a
1180
+ * revert. Those have no Val commit at all, so without this the deploy feed
1181
+ * could only name them by their short sha, and "what went out at 14:02?" had
1182
+ * no answer inside the Studio.
1183
+ *
1184
+ * Optional AND nullable, and the two mean different things: absent is a
1185
+ * content service that does not report messages (or an older one), null is
1186
+ * one that reports having none. Both render as the short sha, so nothing
1187
+ * depends on telling them apart — but a schema that demanded the field would
1188
+ * reject the whole deployment feed of a service that does not send it.
1189
+ */
1190
+ commitMessage: zod.z.string().nullable().optional()
1191
+ });
1192
+
1168
1193
  /**
1169
1194
  * The wire shapes for reading history.
1170
1195
  *
@@ -1757,6 +1782,21 @@ var Api = {
1757
1782
  */
1758
1783
  headCommitSha: zod.z.string().optional(),
1759
1784
  commits: zod.z.array(ValCommit),
1785
+ /**
1786
+ * The publishes the content service knows about.
1787
+ *
1788
+ * Declared, at last. `getStat` has always returned these in
1789
+ * `http` mode and the Studio has always read them - the response
1790
+ * validator strips unknown keys rather than rejecting them, and
1791
+ * the raw json is what the client hands back, so they arrived
1792
+ * while the contract said they could not. That was survivable
1793
+ * until a deployment started carrying a commit MESSAGE: a field
1794
+ * nobody has declared is a field the next person deletes.
1795
+ *
1796
+ * Optional, because a content service that reports no
1797
+ * deployments sends none.
1798
+ */
1799
+ deployments: zod.z.array(ValDeployment).optional(),
1760
1800
  config: ValConfig,
1761
1801
  profileId: zod.z.string().nullable(),
1762
1802
  mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")])
@@ -3249,19 +3289,9 @@ function explainIncompatible(reason) {
3249
3289
  * not a failure: it is what makes the caller return `unknown` rather than guess.
3250
3290
  */
3251
3291
  function variantOf(schema, value) {
3252
- if (schema.type !== "union") {
3292
+ if (schema.type !== "discriminated-union") {
3253
3293
  return undefined;
3254
3294
  }
3255
- // String union: the value IS the discriminator.
3256
- if (typeof schema.key !== "string") {
3257
- if (typeof value !== "string") {
3258
- return undefined;
3259
- }
3260
- return schema.items.find(function (item) {
3261
- return item.type === "literal" && item.value === value;
3262
- });
3263
- }
3264
- // Object union: the discriminator is a literal at `key`.
3265
3295
  var discriminator = schema.key;
3266
3296
  if (!isJsonObject(value)) {
3267
3297
  return undefined;
@@ -3279,9 +3309,6 @@ function variantOf(schema, value) {
3279
3309
  });
3280
3310
  }
3281
3311
  function describeVariant(schema, key) {
3282
- if (schema.type === "literal") {
3283
- return schema.value;
3284
- }
3285
3312
  if (schema.type === "object") {
3286
3313
  var tag = schema.items[key];
3287
3314
  if ((tag === null || tag === void 0 ? void 0 : tag.type) === "literal") {
@@ -3299,11 +3326,24 @@ function checkCompatibility(from, to) {
3299
3326
  });
3300
3327
  }
3301
3328
 
3329
+ // An enum's domain IS its schema, so the value decides: whatever the old
3330
+ // schema was — an enum, a literal, a plain string — the only question is
3331
+ // whether this string is still offered here.
3332
+ if (to.schema.type === "enum") {
3333
+ if (typeof from.value === "string" && to.schema.values.includes(from.value)) {
3334
+ return compatible;
3335
+ }
3336
+ return incompatible({
3337
+ kind: "no-matching-variant",
3338
+ variants: to.schema.values
3339
+ });
3340
+ }
3341
+
3302
3342
  // A changed union is not itself disqualifying. What matters is whether the
3303
3343
  // variant being restored still has somewhere to go — comparing the unions
3304
3344
  // wholesale would refuse a restore that is perfectly safe.
3305
- if (to.schema.type === "union") {
3306
- var key = typeof to.schema.key === "string" ? to.schema.key : "";
3345
+ if (to.schema.type === "discriminated-union") {
3346
+ var key = to.schema.key;
3307
3347
  var _iterator = getSitemapTree._createForOfIteratorHelper(to.schema.items),
3308
3348
  _step;
3309
3349
  try {
@@ -3328,9 +3368,9 @@ function checkCompatibility(from, to) {
3328
3368
  });
3329
3369
  }
3330
3370
 
3331
- // Coming FROM a union into something that is not one: only the variant this
3332
- // value actually is matters, so narrow before comparing.
3333
- if (from.schema.type === "union") {
3371
+ // Coming FROM a discriminated union into something that is not one: only the
3372
+ // variant this value actually is matters, so narrow before comparing.
3373
+ if (from.schema.type === "discriminated-union") {
3334
3374
  var _variant = variantOf(from.schema, from.value);
3335
3375
  if (_variant === undefined) {
3336
3376
  return {
@@ -3343,6 +3383,25 @@ function checkCompatibility(from, to) {
3343
3383
  value: from.value
3344
3384
  }, to);
3345
3385
  }
3386
+
3387
+ // Coming FROM an enum into something that is not one: the value it holds is
3388
+ // all the old schema contributes, so compare as that one value.
3389
+ if (from.schema.type === "enum") {
3390
+ if (typeof from.value !== "string") {
3391
+ return {
3392
+ status: "unknown",
3393
+ message: "The old value is not one of the options it was stored as, so we cannot tell whether it fits here."
3394
+ };
3395
+ }
3396
+ return checkCompatibility({
3397
+ schema: {
3398
+ type: "literal",
3399
+ value: from.value,
3400
+ opt: from.schema.opt
3401
+ },
3402
+ value: from.value
3403
+ }, to);
3404
+ }
3346
3405
  if (from.schema.type !== to.schema.type) {
3347
3406
  return incompatible({
3348
3407
  kind: "type-changed",
@@ -3576,14 +3635,6 @@ var SharedValConfig = zod.z.object({
3576
3635
  * Config that is shared between the client and server.
3577
3636
  */
3578
3637
 
3579
- var ValDeployment = zod.z.object({
3580
- deploymentId: zod.z.string(),
3581
- deploymentState: zod.z.string(),
3582
- commitSha: zod.z.string(),
3583
- updatedAt: zod.z.string(),
3584
- createdAt: zod.z.string()
3585
- });
3586
-
3587
3638
  /**
3588
3639
  * Shared route validation utilities
3589
3640
  */
@@ -3756,6 +3807,16 @@ function findSimilar(key, targets) {
3756
3807
  return a.distance - b.distance;
3757
3808
  });
3758
3809
  }
3810
+ /**
3811
+ * The record a `keyof:check-keys` error resolves against — the value and the
3812
+ * schema at the error's `sourcePath`.
3813
+ *
3814
+ * Exported for the one consumer that has to know what a resolution DEPENDED
3815
+ * on rather than what it concluded: `ValidationStore` re-reads the keys here
3816
+ * to decide whether a change to the referenced module can have moved the
3817
+ * answer. Keep this and {@link checkKeyIsValid} reading the same record, or the
3818
+ * store will invalidate on one record and the resolution answer from another.
3819
+ */
3759
3820
  function getKeyOfRecordAt(sourcePath, snapshot) {
3760
3821
  var _Internal$splitModule = core.Internal.splitModuleFilePathAndModulePath(sourcePath),
3761
3822
  _Internal$splitModule2 = _slicedToArray(_Internal$splitModule, 2),
@@ -3899,6 +3960,29 @@ function checkRouteIsValid(route, includePattern, excludePattern, snapshot) {
3899
3960
  error: false
3900
3961
  };
3901
3962
  }
3963
+ /**
3964
+ * The record a `keyof:check-keys` error resolves against, or null for any other
3965
+ * error — including a malformed marker, which
3966
+ * {@link resolveSchemaSourceFixForError} reports and this has nothing to say
3967
+ * about.
3968
+ *
3969
+ * For a consumer that needs to know what the resolution READS, not what it
3970
+ * concludes: `ValidationStore` keeps this per validated module, so that a change
3971
+ * to the referenced record can invalidate the module holding the reference.
3972
+ */
3973
+ function keyOfRecordPath(error) {
3974
+ var _error$fixes;
3975
+ if (!((_error$fixes = error.fixes) !== null && _error$fixes !== void 0 ? _error$fixes : []).includes("keyof:check-keys")) {
3976
+ return null;
3977
+ }
3978
+ var value = error.value;
3979
+ if (!value || getSitemapTree._typeof(value) !== "object") {
3980
+ return null;
3981
+ }
3982
+ var sourcePath = value.sourcePath;
3983
+ return typeof sourcePath === "string" ? sourcePath : null;
3984
+ }
3985
+
3902
3986
  /**
3903
3987
  * Resolves a single `keyof:check-keys` or `router:check-route` error against
3904
3988
  * the given schema/source snapshot. Returns:
@@ -3907,8 +3991,8 @@ function checkRouteIsValid(route, includePattern, excludePattern, snapshot) {
3907
3991
  * - { status: "remaining"; error } if invalid (rewritten error to surface)
3908
3992
  */
3909
3993
  function resolveSchemaSourceFixForError(error, snapshot) {
3910
- var _error$fixes;
3911
- var fixes = (_error$fixes = error.fixes) !== null && _error$fixes !== void 0 ? _error$fixes : [];
3994
+ var _error$fixes2;
3995
+ var fixes = (_error$fixes2 = error.fixes) !== null && _error$fixes2 !== void 0 ? _error$fixes2 : [];
3912
3996
  if (fixes.includes("keyof:check-keys")) {
3913
3997
  if (!error.value) {
3914
3998
  return {
@@ -4137,11 +4221,20 @@ function emptyOf(schema) {
4137
4221
  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
4138
4222
  } else if (schema.type === "literal") {
4139
4223
  return schema.value;
4140
- } else if (schema.type === "union") {
4141
- if (typeof schema.key === "string") {
4142
- return emptyOf(schema.items[0]);
4224
+ } else if (schema.type === "discriminated-union") {
4225
+ // The first variant is what a new value starts as. `s.discriminatedUnion`
4226
+ // requires one, so an empty `items` means a serialized schema that was not
4227
+ // built by it — say so rather than returning `undefined`, which is not
4228
+ // JSON and would be written into a patch as a missing key.
4229
+ if (schema.items.length === 0) {
4230
+ throw Error("Cannot create an empty value for a discriminated union with no variants");
4143
4231
  }
4144
- return schema.key.value;
4232
+ return emptyOf(schema.items[0]);
4233
+ } else if (schema.type === "enum") {
4234
+ if (schema.values.length === 0) {
4235
+ throw Error("Cannot create an empty value for an enum with no values");
4236
+ }
4237
+ return schema.values[0];
4145
4238
  } else if (schema.type === "date") {
4146
4239
  return clampDateString(formatLocalDate(new Date()), schema.options);
4147
4240
  } else if (schema.type === "dateTime") {
@@ -4229,24 +4322,19 @@ function resolveSerializedSchemaAtPath(schema, path) {
4229
4322
  schema: current,
4230
4323
  remainingPath: path.slice(i)
4231
4324
  };
4232
- } else if (current.type === "union") {
4233
- if (typeof current.key === "string") {
4234
- var _iterator2 = getSitemapTree._createForOfIteratorHelper(current.items),
4235
- _step2;
4236
- try {
4237
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
4238
- var variant = _step2.value;
4239
- var resolved = resolveSerializedSchemaAtPath(variant, path.slice(i));
4240
- if (resolved.kind !== "unresolved") return resolved;
4241
- }
4242
- } catch (err) {
4243
- _iterator2.e(err);
4244
- } finally {
4245
- _iterator2.f();
4325
+ } else if (current.type === "discriminated-union") {
4326
+ var _iterator2 = getSitemapTree._createForOfIteratorHelper(current.items),
4327
+ _step2;
4328
+ try {
4329
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
4330
+ var variant = _step2.value;
4331
+ var resolved = resolveSerializedSchemaAtPath(variant, path.slice(i));
4332
+ if (resolved.kind !== "unresolved") return resolved;
4246
4333
  }
4247
- return {
4248
- kind: "unresolved"
4249
- };
4334
+ } catch (err) {
4335
+ _iterator2.e(err);
4336
+ } finally {
4337
+ _iterator2.f();
4250
4338
  }
4251
4339
  return {
4252
4340
  kind: "unresolved"
@@ -5175,6 +5263,18 @@ function traverseSchemaSource(source, schema, path, callback) {
5175
5263
  return;
5176
5264
  }
5177
5265
 
5266
+ // Handle enum: a leaf, like a literal with more than one allowed value
5267
+ if (schema.type === "enum") {
5268
+ if (typeof source === "string") {
5269
+ callback({
5270
+ source: source,
5271
+ schema: schema,
5272
+ path: path
5273
+ });
5274
+ }
5275
+ return;
5276
+ }
5277
+
5178
5278
  // Handle route
5179
5279
  if (schema.type === "route") {
5180
5280
  if (typeof source === "string") {
@@ -5222,36 +5322,24 @@ function traverseSchemaSource(source, schema, path, callback) {
5222
5322
  return;
5223
5323
  }
5224
5324
 
5225
- // Handle union
5226
- if (schema.type === "union") {
5325
+ // Handle discriminated union: descend through the variant the value takes
5326
+ if (schema.type === "discriminated-union") {
5227
5327
  var schemaKey = schema.key;
5228
- if (typeof schemaKey === "string") {
5229
- // Tagged union - find matching sub-schema
5230
- if (source && getSitemapTree._typeof(source) === "object" && !Array.isArray(source) && schemaKey in source) {
5231
- var itemKey = source[schemaKey];
5232
- if (typeof itemKey === "string") {
5233
- var schemaOfItem = schema.items.filter(function (item) {
5234
- return item.type === "object";
5235
- }).find(function (item) {
5236
- var itemKeySchema = item.items[schemaKey];
5237
- if ((itemKeySchema === null || itemKeySchema === void 0 ? void 0 : itemKeySchema.type) === "literal") {
5238
- return itemKeySchema.value === itemKey;
5239
- }
5240
- return false;
5241
- });
5242
- if (schemaOfItem) {
5243
- traverseSchemaSource(source, schemaOfItem, path, callback);
5328
+ if (source && getSitemapTree._typeof(source) === "object" && !Array.isArray(source) && schemaKey in source) {
5329
+ var itemKey = source[schemaKey];
5330
+ if (typeof itemKey === "string") {
5331
+ var schemaOfItem = schema.items.filter(function (item) {
5332
+ return item.type === "object";
5333
+ }).find(function (item) {
5334
+ var itemKeySchema = item.items[schemaKey];
5335
+ if ((itemKeySchema === null || itemKeySchema === void 0 ? void 0 : itemKeySchema.type) === "literal") {
5336
+ return itemKeySchema.value === itemKey;
5244
5337
  }
5245
- }
5246
- }
5247
- } else {
5248
- // Literal union - treat as primitive
5249
- if (typeof source === "string") {
5250
- callback({
5251
- source: source,
5252
- schema: schema,
5253
- path: path
5338
+ return false;
5254
5339
  });
5340
+ if (schemaOfItem) {
5341
+ traverseSchemaSource(source, schemaOfItem, path, callback);
5342
+ }
5255
5343
  }
5256
5344
  }
5257
5345
  return;
@@ -5411,7 +5499,11 @@ function indexModule(searchIndex, moduleFilePath, source, schema) {
5411
5499
  var label = "";
5412
5500
 
5413
5501
  // Handle primitives
5414
- if (schema.type === "string" || schema.type === "number" || schema.type === "boolean" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route") {
5502
+ if (schema.type === "string" || schema.type === "number" || schema.type === "boolean" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route" ||
5503
+ // An enum's value is a string the editor chose; it is as searchable as
5504
+ // any other string leaf. The old string union was never indexed at all,
5505
+ // which is the gap this closes.
5506
+ schema.type === "enum") {
5415
5507
  var _source$toString, _source$toString2;
5416
5508
  searchText = (_source$toString = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString !== void 0 ? _source$toString : "";
5417
5509
  label = (_source$toString2 = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString2 !== void 0 ? _source$toString2 : "";
@@ -5612,6 +5704,8 @@ exports.SerializedCodeSchema = SerializedCodeSchema;
5612
5704
  exports.SerializedColorSchema = SerializedColorSchema;
5613
5705
  exports.SerializedDateSchema = SerializedDateSchema;
5614
5706
  exports.SerializedDateTimeSchema = SerializedDateTimeSchema;
5707
+ exports.SerializedDiscriminatedUnionSchema = SerializedDiscriminatedUnionSchema;
5708
+ exports.SerializedEnumSchema = SerializedEnumSchema;
5615
5709
  exports.SerializedFileSchema = SerializedFileSchema;
5616
5710
  exports.SerializedImageSchema = SerializedImageSchema;
5617
5711
  exports.SerializedKeyOfSchema = SerializedKeyOfSchema;
@@ -5624,7 +5718,6 @@ exports.SerializedRouteSchema = SerializedRouteSchema;
5624
5718
  exports.SerializedSchema = SerializedSchema;
5625
5719
  exports.SerializedSettingsSchema = SerializedSettingsSchema;
5626
5720
  exports.SerializedStringSchema = SerializedStringSchema;
5627
- exports.SerializedUnionSchema = SerializedUnionSchema;
5628
5721
  exports.SharedValConfig = SharedValConfig;
5629
5722
  exports.VAL_ENABLE_COOKIE_NAME = VAL_ENABLE_COOKIE_NAME;
5630
5723
  exports.VAL_SESSION_COOKIE = VAL_SESSION_COOKIE;
@@ -5654,6 +5747,7 @@ exports.flattenRichText = flattenRichText;
5654
5747
  exports.getErrorMessageFromUnknownJson = getErrorMessageFromUnknownJson;
5655
5748
  exports.getFilenameFromRef = getFilenameFromRef;
5656
5749
  exports.getInlineImgInfo = getInlineImgInfo;
5750
+ exports.getKeyOfRecordAt = getKeyOfRecordAt;
5657
5751
  exports.getRefParts = getRefParts;
5658
5752
  exports.getRoutesOf = getRoutesOf;
5659
5753
  exports.getRoutesWithModulePaths = getRoutesWithModulePaths;
@@ -5662,6 +5756,7 @@ exports.indexModule = indexModule;
5662
5756
  exports.isMimeTypeAccepted = isMimeTypeAccepted;
5663
5757
  exports.isRemoteSchema = isRemoteSchema;
5664
5758
  exports.isSkippedSource = isSkippedSource;
5759
+ exports.keyOfRecordPath = keyOfRecordPath;
5665
5760
  exports.levenshtein = levenshtein;
5666
5761
  exports.newestCommitSha = newestCommitSha;
5667
5762
  exports.partitionValidationErrors = partitionValidationErrors;