@valbuild/server 0.125.0 → 0.127.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,215 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.127.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#608](https://github.com/valbuild/val/pull/608) [`7fa8699`](https://github.com/valbuild/val/commit/7fa869974bc895af992a7d5c18b76253636d7d65) Thanks [@freekh](https://github.com/freekh)! - A record whose schema declares its keys now holds every one of them.
8
+
9
+ Two key schemas enumerate their keys: `s.locale()`, whose set is the project's
10
+ `locales.available`, and a union of literals. For those, the keys are part of the
11
+ schema, so a missing one is a hole in the content rather than content nobody has
12
+ written yet — and validation now says so, naming what is missing.
13
+
14
+ ```typescript
15
+ s.record(s.locale(), s.object({ title: s.string() }));
16
+ // Missing key: 'nb-NO'. This record's keys are declared by its schema, so
17
+ // every one of them is an entry — an entry nobody has written yet is null,
18
+ // not absent.
19
+ ```
20
+
21
+ **An entry nobody has written yet is `null`.** Not an absent key: a null entry is
22
+ data you can count, filter and see in a diff, and it means half-translated
23
+ content stays _valid_ rather than blocking a publish. The value type of such a
24
+ record widens by `null` to match, so writing one in a `.val.ts` type-checks:
25
+
26
+ ```typescript
27
+ c.define(
28
+ "/content/jacket.val.ts",
29
+ s.record(s.locale(), s.object({ title: s.string() })),
30
+ {
31
+ "en-US": { title: "Winter jacket" },
32
+ "nb-NO": null, // nobody has translated this yet
33
+ },
34
+ );
35
+ ```
36
+
37
+ **This changes `s.record(s.union(...), item)`**, and closes a gap that was
38
+ already there: `s.record(s.union(s.literal("a"), s.literal("b")), item)` types as
39
+ `Record<"a" | "b", T>`, so TypeScript demanded both keys while the validator only
40
+ checked the ones present. It now checks them too, and — as above — accepts `null`
41
+ for an entry that has not been filled in. If you have such a record with keys
42
+ missing, validation will report them; adding the keys with `null` values is the
43
+ fix, and creating one from the Studio does it for you.
44
+
45
+ `emptyOf` creates these records with every key already in them rather than
46
+ empty, since an empty one is already missing keys. In the Studio use the
47
+ `useEmptyOf()` hook rather than importing `emptyOf` directly: a locale record's
48
+ keys are in the settings module, and the hook is what has read it.
49
+
50
+ - [#608](https://github.com/valbuild/val/pull/608) [`5b7fe05`](https://github.com/valbuild/val/commit/5b7fe05cec6365f9cd1de9ba31e65ed4a87edb63) Thanks [@freekh](https://github.com/freekh)! - `s.locale()`: one of the project's languages.
51
+
52
+ The languages themselves are declared in the settings module (`locales.available`);
53
+ this says that a value is one of them.
54
+
55
+ ```typescript
56
+ // a field: everything in this entry is in this language
57
+ s.record(s.string(), s.object({ locale: s.locale(), title: s.string() }));
58
+
59
+ // a key: one entry per language
60
+ s.record(s.locale(), s.object({ title: s.string() }));
61
+ ```
62
+
63
+ Every locale in content is checked against the project's list, the way `keyOf`
64
+ and `route` are checked against what they point at. An undeclared language names
65
+ the ones the project has; a project that has declared none is told to declare
66
+ them rather than told the value is wrong.
67
+
68
+ A locale is stored as the tag itself — the value in content is `nb-NO`, and a
69
+ record keyed by `s.locale()` has `nb-NO` as its key. Spelling one differently
70
+ where it is stored (`/no/…` as a URL segment) is a real need and is deliberately
71
+ not in this release: it changes what is accepted as well as what is shown, so it
72
+ is being designed on its own rather than folded in here.
73
+
74
+ A locale is **never stega encoded**: it ends up in `<html lang>`, in `hreflang`
75
+ and in `Intl` constructors, none of which survive invisible characters.
76
+
77
+ `assistant.translation` joins the settings module alongside `context` and `tone`
78
+ — a note per language, keyed by language, so only the target language's rules are
79
+ sent when translating into it.
80
+
81
+ ### Patch Changes
82
+
83
+ - Updated dependencies [[`7fa8699`](https://github.com/valbuild/val/commit/7fa869974bc895af992a7d5c18b76253636d7d65), [`600308d`](https://github.com/valbuild/val/commit/600308d0174990ad9f5c417147d160273489c65a), [`5b7fe05`](https://github.com/valbuild/val/commit/5b7fe05cec6365f9cd1de9ba31e65ed4a87edb63), [`88262ac`](https://github.com/valbuild/val/commit/88262ac8db068650a664981ef73556457d87741a), [`7072e07`](https://github.com/valbuild/val/commit/7072e07623c953a09ac14388ae22dada0b431ce3), [`29811c3`](https://github.com/valbuild/val/commit/29811c3f8c7e001a950e6f4833af6888e6a4efea), [`9983116`](https://github.com/valbuild/val/commit/99831164c5151aad7ca69de79e1d0d59878be251)]:
84
+ - @valbuild/core@0.127.0
85
+ - @valbuild/shared@0.127.0
86
+ - @valbuild/ui@0.127.0
87
+
88
+ ## 0.126.0
89
+
90
+ ### Patch Changes
91
+
92
+ - [#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
93
+
94
+ The deploy feed could only name a publish when Val itself had made the commit:
95
+ the message came off Val's own `ValCommit`, and every other deployment — a
96
+ developer's push, a merged pull request, a revert — showed a seven-character
97
+ sha. On most projects those are the majority, so "what went out at 14:02?" had
98
+ no answer in the Studio.
99
+
100
+ A deployment can now carry its own `commitMessage`, which the Studio uses
101
+ wherever there is no Val commit to prefer. It is optional and nullable, so a
102
+ content service that does not report messages is unaffected — those publishes
103
+ keep showing the short sha, exactly as before.
104
+
105
+ Deployment rows also show only the subject line of a message now. A git message
106
+ is a subject, a blank line and a body, and the rows are one truncated line — so
107
+ a real push arrived as "Subject The body went on like this…". The classic
108
+ Draft changes view still has the whole message in its tooltip.
109
+
110
+ - [#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`.
111
+
112
+ `s.union` did two unrelated jobs and worked out which one you meant from its
113
+ first argument: a string key meant a tagged union of objects, literal schemas
114
+ meant a set of allowed strings. Those are now two schemas with two names.
115
+
116
+ ```ts
117
+ // A fixed set of strings — presents as a dropdown
118
+ s.enum("primary", "secondary", "ghost"); // Schema<"primary" | "secondary" | "ghost">
119
+
120
+ // One of several object shapes, told apart by a tag field
121
+ s.discriminatedUnion(
122
+ "type",
123
+ s.object({ type: s.literal("hero"), heading: s.string() }),
124
+ s.object({ type: s.literal("quote"), text: s.string() }),
125
+ );
126
+ ```
127
+
128
+ `s.enum` takes the strings directly, so the `s.literal(...)` wrapper is gone.
129
+
130
+ **`s.union` still works** — it is deprecated, and it builds exactly the schema
131
+ above, so nothing has to change today:
132
+
133
+ ```ts
134
+ s.union(s.literal("one"), s.literal("two")); // → s.enum("one", "two")
135
+ s.union("type", pageA, pageB); // → s.discriminatedUnion("type", pageA, pageB)
136
+ ```
137
+
138
+ The two are different kinds of node, and that is the reason for the split. A
139
+ discriminated union is a container: the selected variant's fields are the fields
140
+ being edited, and everything that walks a schema descends through it. An enum is
141
+ a leaf — a string with a closed domain — so nothing recurses into it. Told apart
142
+ only by the shape of `key`, every consumer had to re-derive which one it was
143
+ holding; each now has its own serialized type (`"discriminated-union"` and
144
+ `"enum"`) and Val Studio has a field per kind rather than one field that
145
+ branches.
146
+
147
+ Two behaviour changes fall out of the split, both of them fixes:
148
+
149
+ - A value that is not a string at all now fails an enum's validation with a
150
+ type error. `s.union` of literals only ever checked the value against its
151
+ literals when the value WAS a string, so a number or an object where an enum
152
+ was declared validated clean.
153
+ - An enum field now shows its validation errors in Val Studio where the field
154
+ is opened on its own — the module editor and the canvas's fields column — and
155
+ gets the compact error layout inside an inline list row. It is a leaf now, so
156
+ it goes through the same error rendering as every other leaf field; the string
157
+ union bypassed it and showed nothing in those places.
158
+
159
+ Several latent crashes in the old `s.union` are fixed on the way past, all of
160
+ them cases where it threw a `TypeError` instead of reporting:
161
+
162
+ - A required discriminated union holding `null` now reports a type error rather
163
+ than throwing, and resolving a path underneath a nullable one that is `null`
164
+ gives the error the API promises instead of a crash.
165
+ - `s.literal("")` is a legal discriminator tag, and `s.enum("")` a legal value.
166
+ Both used to be treated as absent by a truthiness check — in path resolution,
167
+ in stega encoding, and in the message that lists a union's valid tags. The
168
+ editor's dropdowns handle them too: an empty value is reserved by the select
169
+ component and had to be mapped around.
170
+ - A variant that omits the discriminator entirely is now reported as the schema
171
+ error it is, instead of throwing while the check looked for it.
172
+ - An enum's value is now indexed for search, like every other string leaf. The
173
+ old string union was never indexed at all, so searching for one of its values
174
+ could not find the field.
175
+ - A nullable discriminated union set to `null` no longer renders a spinner that
176
+ never resolves.
177
+
178
+ `s.discriminatedUnion` also requires at least one variant, as `s.enum` requires
179
+ at least one value: a union with nothing to select is not a thing to write, and
180
+ everything downstream reads the first variant where it needs any.
181
+
182
+ If you read serialized schemas yourself, that is the breaking part: `type` is no
183
+ longer `"union"`, an enum carries `values: string[]` instead of a `key` plus
184
+ `items` of literal schemas, and `UnionSchema` is no longer a class.
185
+ `SerializedUnionSchema`, `SerializedStringUnionSchema`,
186
+ `SerializedObjectUnionSchema` and `UnionSchema` remain as deprecated type
187
+ aliases.
188
+
189
+ - [#666](https://github.com/valbuild/val/pull/666) [`5c18c99`](https://github.com/valbuild/val/commit/5c18c99ecc84651f82123481fc042063db953833) Thanks [@freekh](https://github.com/freekh)! - `next build` no longer warns `module.createRequire failed parsing argument.`
190
+
191
+ Every Next app that bundles `@valbuild/server` into its Val API route got this
192
+ on every build, twice, with an import trace that led from `route.ts` down into
193
+ `valbuild-server.esm.js` and stopped there:
194
+
195
+ ```
196
+ ⚠ ./node_modules/.../@valbuild/server/dist/valbuild-server.esm.js
197
+ module.createRequire failed parsing argument.
198
+ ```
199
+
200
+ Nothing was wrong. webpack special-cases a `createRequire` binding imported from
201
+ `node:module` and tries to resolve the call's argument at build time; the two
202
+ calls in this package take a path inside the user's project, known only at
203
+ runtime, so there was nothing to resolve and nothing the warning could tell
204
+ anyone. Both now go through a helper that reaches the same function through the
205
+ `Module` class, which that analysis does not tag. Runtime behaviour is
206
+ unchanged, and a lint rule keeps the direct import from coming back.
207
+
208
+ - Updated dependencies [[`719ad6b`](https://github.com/valbuild/val/commit/719ad6b607bcf136d0dbde9e90bf4b8a843561a4), [`9830277`](https://github.com/valbuild/val/commit/9830277e9aaca8da3030f629c2656ec58da47e45), [`64bfd0a`](https://github.com/valbuild/val/commit/64bfd0a6c85832ea5169b53e47087f22e193df36), [`7782979`](https://github.com/valbuild/val/commit/7782979e9b52f2015a6e72dc981e630d4f8c78e2), [`5bfd630`](https://github.com/valbuild/val/commit/5bfd630b63dee2189e238f20fe72ecc5537160f7), [`ccbcda6`](https://github.com/valbuild/val/commit/ccbcda60b3e3c465071229ae1ba28ac735483e63), [`f2fe70d`](https://github.com/valbuild/val/commit/f2fe70dab2b65000dfaf289f09c70b4a8291467a), [`755e1a3`](https://github.com/valbuild/val/commit/755e1a3953775cb8d2c2dce87d6810d3dc329640), [`c6b1ec8`](https://github.com/valbuild/val/commit/c6b1ec84f1883750a4cfe5f70470b177621e971f), [`656f680`](https://github.com/valbuild/val/commit/656f680043c640f678625a64e690389ab23a0a69), [`610a041`](https://github.com/valbuild/val/commit/610a0414b120b521f38a2eb1182b3778bf778b2b), [`171208a`](https://github.com/valbuild/val/commit/171208a20177e68ed5a8b1a6fdaabfe893a6aa5f)]:
209
+ - @valbuild/ui@0.126.0
210
+ - @valbuild/shared@0.126.0
211
+ - @valbuild/core@0.126.0
212
+
3
213
  ## 0.125.0
4
214
 
5
215
  ### Patch Changes
@@ -206,6 +206,6 @@ export declare function handleJsonValuesExtractEntry(ctx: FixHandlerContext): Pr
206
206
  * — just not by this command.
207
207
  */
208
208
  export declare function handleExternalUpload(): Promise<FixHandlerResult>;
209
- export declare const currentFixHandlers: Record<Exclude<ValidationFix, "keyof:check-keys" | "router:check-route">, FixHandler>;
209
+ export declare const currentFixHandlers: Record<Exclude<ValidationFix, "keyof:check-keys" | "router:check-route" | "locale:check-locale" | "record:fill-keys">, FixHandler>;
210
210
  export declare const fixHandlers: Record<string, FixHandler>;
211
211
  export declare function createDefaultValFSHost(): IValFSHost;
@@ -696,6 +696,32 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
696
696
  return parsedConfigFile.options;
697
697
  };
698
698
 
699
+ /**
700
+ * Node's `require`, resolving as if from `filename`.
701
+ *
702
+ * Use this instead of importing `createRequire` from `node:module` directly.
703
+ * webpack special-cases a `createRequire` binding imported from `module` /
704
+ * `node:module`: it tries to resolve the call's argument at build time, and
705
+ * when the argument is not a literal it gives up with
706
+ *
707
+ * module.createRequire failed parsing argument.
708
+ *
709
+ * Our argument is never a literal — it is a path inside the user's project,
710
+ * known only at runtime — so there is nothing for that analysis to find, and
711
+ * nothing the warning can tell anyone. It is emitted on every build of every
712
+ * Next app that bundles `@valbuild/server` into its Val API route, which is
713
+ * all of them. Reaching `createRequire` through the `Module` class is the same
714
+ * function and is not what webpack tags, so the call is invisible to it.
715
+ *
716
+ * It has to stay a real `node:module` import: jest's module registry hands out
717
+ * its own `node:module`, and a `require` obtained around it (via
718
+ * `process.getBuiltinModule`, say) would resolve against the real filesystem
719
+ * rather than the registry the tests run in.
720
+ */
721
+ function createNodeRequire(filename) {
722
+ return node_module.Module.createRequire(filename);
723
+ }
724
+
699
725
  /**
700
726
  * The filesystem seam `loadValModules` reads through.
701
727
  *
@@ -829,7 +855,7 @@ function loadModule(absPath, cache, compilerOptions, host) {
829
855
  // Insert into the cache before evaluating so cyclic imports resolve.
830
856
  cache[absPath] = moduleObj;
831
857
  const dirName = path__namespace["default"].dirname(absPath);
832
- const realRequire = node_module.Module.createRequire(absPath);
858
+ const realRequire = createNodeRequire(absPath);
833
859
  const customRequire = spec => {
834
860
  var _ts$resolveModuleName;
835
861
  if (isStubbedSpecifier(spec)) {
@@ -1136,7 +1162,7 @@ function findNestedJsonValuesRecords(schema, path = []) {
1136
1162
  case "array":
1137
1163
  rec(current.item, currentPath.concat("*"));
1138
1164
  return;
1139
- case "union":
1165
+ case "discriminated-union":
1140
1166
  for (let i = 0; i < current.items.length; i++) {
1141
1167
  rec(current.items[i], currentPath.concat(`union[${i}]`));
1142
1168
  }
@@ -1722,6 +1748,7 @@ async function createService(projectRoot, host = {
1722
1748
  return new Service(projectRoot, sourceFileHandler, extracted);
1723
1749
  }
1724
1750
  class Service {
1751
+ projectRoot;
1725
1752
  constructor(projectRoot, sourceFileHandler, extracted) {
1726
1753
  this.sourceFileHandler = sourceFileHandler;
1727
1754
  this.extracted = extracted;
@@ -2361,17 +2388,18 @@ function findNestedJsonValuesModuleErrors(schemas) {
2361
2388
  // #region ValOps
2362
2389
  class ValOps {
2363
2390
  /** Sources from val modules, immutable (without patches or anything) */
2364
-
2391
+ sources;
2365
2392
  /** The sha256 / hash of all sources + all schemas + config */
2366
-
2393
+ baseSha;
2367
2394
  /** The sha256 / hash of all sources */
2368
-
2395
+ sourcesSha;
2369
2396
  /** The sha256 / hash of config */
2370
-
2397
+ configSha;
2371
2398
  /** Schema from val modules, immutable */
2372
-
2399
+ schemas;
2373
2400
  /** The sha256 / hash of schema + config - if this changes users needs to reload */
2374
-
2401
+ schemaSha;
2402
+ modulesErrors;
2375
2403
  /**
2376
2404
  * What the SHAs above are a fold over, so they can be recomputed.
2377
2405
  *
@@ -2379,7 +2407,7 @@ class ValOps {
2379
2407
  * without re-evaluating the modules is a save, and it has to be able to move
2380
2408
  * the SHAs with them.
2381
2409
  */
2382
-
2410
+ shaEntries;
2383
2411
  /**
2384
2412
  * The extraction's OWN module errors, which are what the fold was given.
2385
2413
  *
@@ -2387,7 +2415,7 @@ class ValOps {
2387
2415
  * `.jsonValues()` errors concatenated on, and those were never part of the
2388
2416
  * hash. Re-folding with the wrong list changes the base SHA for no reason.
2389
2417
  */
2390
-
2418
+ shaModuleErrors;
2391
2419
  /**
2392
2420
  * What a save has told us each `.jsonValues()` entry now holds.
2393
2421
  *
@@ -3147,7 +3175,8 @@ class ValOps {
3147
3175
  // (must include all modules, not just those being validated, since conflicts can come from any module)
3148
3176
  const galleryDirectoryToModules = new Map();
3149
3177
  // Build a schema/source snapshot so the shared resolver can cross-reference
3150
- // keyof:check-keys and router:check-route against every module's data.
3178
+ // the marker fixes — a keyOf's key, a route, a locale tag, a record's
3179
+ // declared keys — against every module's data. See `SCHEMA_SOURCE_FIXES`.
3151
3180
  const snapshot = {
3152
3181
  schemas: {},
3153
3182
  sources: {}
@@ -3227,7 +3256,7 @@ class ValOps {
3227
3256
  };
3228
3257
  if (validationErrors) {
3229
3258
  for (const validationError of validationErrors) {
3230
- var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4, _validationError$fixe5, _validationError$fixe6, _validationError$fixe7, _validationError$fixe8;
3259
+ var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4, _validationError$fixe5, _validationError$fixe6;
3231
3260
  if (isOnlyFileCheckValidationError(validationError)) {
3232
3261
  if (files[sourcePath]) {
3233
3262
  addError({
@@ -3241,13 +3270,13 @@ class ValOps {
3241
3270
  }
3242
3271
  } else if ((_validationError$fixe = validationError.fixes) !== null && _validationError$fixe !== void 0 && _validationError$fixe.includes("image:check-remote") || (_validationError$fixe2 = validationError.fixes) !== null && _validationError$fixe2 !== void 0 && _validationError$fixe2.includes("file:check-remote")) {
3243
3272
  remoteFiles[sourcePath] = validationError.value;
3244
- } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("keyof:check-keys") || (_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("router:check-route")) {
3273
+ } else if (internal.isSchemaSourceFixError(validationError)) {
3245
3274
  const resolved = internal.resolveSchemaSourceFixForError(validationError, snapshot);
3246
3275
  if (resolved && resolved.status === "remaining") {
3247
3276
  addError(resolved.error);
3248
3277
  }
3249
3278
  // resolved.status === "resolved" → drop silently
3250
- } else if ((_validationError$fixe5 = validationError.fixes) !== null && _validationError$fixe5 !== void 0 && _validationError$fixe5.includes("images:check-unique-folder") || (_validationError$fixe6 = validationError.fixes) !== null && _validationError$fixe6 !== void 0 && _validationError$fixe6.includes("files:check-unique-folder")) {
3279
+ } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("images:check-unique-folder") || (_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("files:check-unique-folder")) {
3251
3280
  const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
3252
3281
  if (!validationError.value || typeof validationError.value !== "object") {
3253
3282
  addError({
@@ -3272,7 +3301,7 @@ class ValOps {
3272
3301
  // If conflictingModules is empty, directory is unique — silently drop the error.
3273
3302
  }
3274
3303
  }
3275
- } else if ((_validationError$fixe7 = validationError.fixes) !== null && _validationError$fixe7 !== void 0 && _validationError$fixe7.includes("images:check-all-files") || (_validationError$fixe8 = validationError.fixes) !== null && _validationError$fixe8 !== void 0 && _validationError$fixe8.includes("files:check-all-files")) ; else {
3304
+ } else if ((_validationError$fixe5 = validationError.fixes) !== null && _validationError$fixe5 !== void 0 && _validationError$fixe5.includes("images:check-all-files") || (_validationError$fixe6 = validationError.fixes) !== null && _validationError$fixe6 !== void 0 && _validationError$fixe6.includes("files:check-all-files")) ; else {
3276
3305
  addError(validationError);
3277
3306
  }
3278
3307
  }
@@ -4132,8 +4161,8 @@ class ValOps {
4132
4161
  // #endregion history
4133
4162
  }
4134
4163
  function isOnlyFileCheckValidationError(validationError) {
4135
- var _validationError$fixe9;
4136
- if ((_validationError$fixe9 = validationError.fixes) !== null && _validationError$fixe9 !== void 0 && _validationError$fixe9.every(f => f === "file:check-metadata" || f === "image:check-metadata")) {
4164
+ var _validationError$fixe7;
4165
+ if ((_validationError$fixe7 = validationError.fixes) !== null && _validationError$fixe7 !== void 0 && _validationError$fixe7.every(f => f === "file:check-metadata" || f === "image:check-metadata")) {
4137
4166
  return true;
4138
4167
  }
4139
4168
  return false;
@@ -5654,6 +5683,7 @@ function serializeSchemaSafely(schema) {
5654
5683
  }
5655
5684
  class ValOpsFS extends ValOps {
5656
5685
  static VAL_DIR = ".val";
5686
+ host;
5657
5687
  constructor(contentUrl, rootDir, valModules, options) {
5658
5688
  super(valModules, options);
5659
5689
  this.contentUrl = contentUrl;
@@ -5666,6 +5696,7 @@ class ValOpsFS extends ValOps {
5666
5696
  * Change detection for `.jsonValues()` entry files, which no sha can see (their
5667
5697
  * content lives behind a thunk that `JSON.stringify` drops).
5668
5698
  */
5699
+ jsonEntryFilesFingerprint;
5669
5700
 
5670
5701
  /**
5671
5702
  * Unpublished changes repair threw away, waiting to be told to somebody.
@@ -7015,7 +7046,11 @@ const GetApplicablePatches = z.z.object({
7015
7046
  commitSha: z.z.string(),
7016
7047
  deploymentState: z.z.string(),
7017
7048
  createdAt: z.z.string(),
7018
- updatedAt: z.z.string()
7049
+ updatedAt: z.z.string(),
7050
+ // The git message, where the content service knows it. See
7051
+ // `ValDeployment`: this is the only source for a deployment that Val
7052
+ // did not publish, and zod would otherwise strip it here.
7053
+ commitMessage: z.z.string().nullable().optional()
7019
7054
  })).optional()
7020
7055
  });
7021
7056
  const FilesResponse = z.z.object({
@@ -7191,6 +7226,8 @@ const NonceResponse = z.z.object({
7191
7226
  */
7192
7227
  const PATCH_GROUPS_CACHE_MS = 1000;
7193
7228
  class ValOpsHttp extends ValOps {
7229
+ authHeaders;
7230
+ root;
7194
7231
  constructor(contentUrl, project, commitSha,
7195
7232
  // TODO: CommitSha
7196
7233
  branch,
@@ -7616,7 +7653,8 @@ class ValOpsHttp extends ValOps {
7616
7653
  deploymentId: deployment.deploymentId,
7617
7654
  deploymentState: deployment.deploymentState,
7618
7655
  createdAt: deployment.createdAt,
7619
- updatedAt: deployment.updatedAt
7656
+ updatedAt: deployment.updatedAt,
7657
+ commitMessage: deployment.commitMessage
7620
7658
  });
7621
7659
  }
7622
7660
  }
@@ -13545,6 +13583,8 @@ const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
13545
13583
  const MAX_OBJECT_KEY_SIZE = 2 ** 27; // https://stackoverflow.com/questions/13367391/is-there-a-limit-on-length-of-the-key-string-in-js-object
13546
13584
 
13547
13585
  class ValModuleLoader {
13586
+ cache;
13587
+ cacheSize;
13548
13588
  constructor(projectRoot, compilerOptions,
13549
13589
  // TODO: remove this?
13550
13590
  sourceFileHandler, host = {
@@ -15224,9 +15264,10 @@ async function handleExternalUpload() {
15224
15264
  };
15225
15265
  }
15226
15266
 
15227
- // Fix handler registry. `keyof:check-keys` and `router:check-route` are
15228
- // resolved upfront by the shared resolveSchemaSourceFixes — they never reach
15229
- // this registry, so they're excluded from the key set.
15267
+ // Fix handler registry. `keyof:check-keys`, `router:check-route`,
15268
+ // `locale:check-locale` and `record:fill-keys` are resolved upfront by the
15269
+ // shared resolveSchemaSourceFixes — they never reach this registry, so they're
15270
+ // excluded from the key set.
15230
15271
  const currentFixHandlers = {
15231
15272
  "image:check-metadata": handleFileMetadata,
15232
15273
  "image:add-metadata": handleFileMetadata,
@@ -15386,7 +15427,7 @@ async function evalValConfigFile(projectRoot, configFileName) {
15386
15427
  },
15387
15428
  fileName: valConfigPath
15388
15429
  });
15389
- const projectRootRequire = node_module.createRequire(valConfigPath);
15430
+ const projectRootRequire = createNodeRequire(valConfigPath);
15390
15431
  const exportsObj = {};
15391
15432
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15392
15433
  const sandbox = {
@@ -696,6 +696,32 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
696
696
  return parsedConfigFile.options;
697
697
  };
698
698
 
699
+ /**
700
+ * Node's `require`, resolving as if from `filename`.
701
+ *
702
+ * Use this instead of importing `createRequire` from `node:module` directly.
703
+ * webpack special-cases a `createRequire` binding imported from `module` /
704
+ * `node:module`: it tries to resolve the call's argument at build time, and
705
+ * when the argument is not a literal it gives up with
706
+ *
707
+ * module.createRequire failed parsing argument.
708
+ *
709
+ * Our argument is never a literal — it is a path inside the user's project,
710
+ * known only at runtime — so there is nothing for that analysis to find, and
711
+ * nothing the warning can tell anyone. It is emitted on every build of every
712
+ * Next app that bundles `@valbuild/server` into its Val API route, which is
713
+ * all of them. Reaching `createRequire` through the `Module` class is the same
714
+ * function and is not what webpack tags, so the call is invisible to it.
715
+ *
716
+ * It has to stay a real `node:module` import: jest's module registry hands out
717
+ * its own `node:module`, and a `require` obtained around it (via
718
+ * `process.getBuiltinModule`, say) would resolve against the real filesystem
719
+ * rather than the registry the tests run in.
720
+ */
721
+ function createNodeRequire(filename) {
722
+ return node_module.Module.createRequire(filename);
723
+ }
724
+
699
725
  /**
700
726
  * The filesystem seam `loadValModules` reads through.
701
727
  *
@@ -829,7 +855,7 @@ function loadModule(absPath, cache, compilerOptions, host) {
829
855
  // Insert into the cache before evaluating so cyclic imports resolve.
830
856
  cache[absPath] = moduleObj;
831
857
  const dirName = path__namespace["default"].dirname(absPath);
832
- const realRequire = node_module.Module.createRequire(absPath);
858
+ const realRequire = createNodeRequire(absPath);
833
859
  const customRequire = spec => {
834
860
  var _ts$resolveModuleName;
835
861
  if (isStubbedSpecifier(spec)) {
@@ -1136,7 +1162,7 @@ function findNestedJsonValuesRecords(schema, path = []) {
1136
1162
  case "array":
1137
1163
  rec(current.item, currentPath.concat("*"));
1138
1164
  return;
1139
- case "union":
1165
+ case "discriminated-union":
1140
1166
  for (let i = 0; i < current.items.length; i++) {
1141
1167
  rec(current.items[i], currentPath.concat(`union[${i}]`));
1142
1168
  }
@@ -1722,6 +1748,7 @@ async function createService(projectRoot, host = {
1722
1748
  return new Service(projectRoot, sourceFileHandler, extracted);
1723
1749
  }
1724
1750
  class Service {
1751
+ projectRoot;
1725
1752
  constructor(projectRoot, sourceFileHandler, extracted) {
1726
1753
  this.sourceFileHandler = sourceFileHandler;
1727
1754
  this.extracted = extracted;
@@ -2361,17 +2388,18 @@ function findNestedJsonValuesModuleErrors(schemas) {
2361
2388
  // #region ValOps
2362
2389
  class ValOps {
2363
2390
  /** Sources from val modules, immutable (without patches or anything) */
2364
-
2391
+ sources;
2365
2392
  /** The sha256 / hash of all sources + all schemas + config */
2366
-
2393
+ baseSha;
2367
2394
  /** The sha256 / hash of all sources */
2368
-
2395
+ sourcesSha;
2369
2396
  /** The sha256 / hash of config */
2370
-
2397
+ configSha;
2371
2398
  /** Schema from val modules, immutable */
2372
-
2399
+ schemas;
2373
2400
  /** The sha256 / hash of schema + config - if this changes users needs to reload */
2374
-
2401
+ schemaSha;
2402
+ modulesErrors;
2375
2403
  /**
2376
2404
  * What the SHAs above are a fold over, so they can be recomputed.
2377
2405
  *
@@ -2379,7 +2407,7 @@ class ValOps {
2379
2407
  * without re-evaluating the modules is a save, and it has to be able to move
2380
2408
  * the SHAs with them.
2381
2409
  */
2382
-
2410
+ shaEntries;
2383
2411
  /**
2384
2412
  * The extraction's OWN module errors, which are what the fold was given.
2385
2413
  *
@@ -2387,7 +2415,7 @@ class ValOps {
2387
2415
  * `.jsonValues()` errors concatenated on, and those were never part of the
2388
2416
  * hash. Re-folding with the wrong list changes the base SHA for no reason.
2389
2417
  */
2390
-
2418
+ shaModuleErrors;
2391
2419
  /**
2392
2420
  * What a save has told us each `.jsonValues()` entry now holds.
2393
2421
  *
@@ -3147,7 +3175,8 @@ class ValOps {
3147
3175
  // (must include all modules, not just those being validated, since conflicts can come from any module)
3148
3176
  const galleryDirectoryToModules = new Map();
3149
3177
  // Build a schema/source snapshot so the shared resolver can cross-reference
3150
- // keyof:check-keys and router:check-route against every module's data.
3178
+ // the marker fixes — a keyOf's key, a route, a locale tag, a record's
3179
+ // declared keys — against every module's data. See `SCHEMA_SOURCE_FIXES`.
3151
3180
  const snapshot = {
3152
3181
  schemas: {},
3153
3182
  sources: {}
@@ -3227,7 +3256,7 @@ class ValOps {
3227
3256
  };
3228
3257
  if (validationErrors) {
3229
3258
  for (const validationError of validationErrors) {
3230
- var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4, _validationError$fixe5, _validationError$fixe6, _validationError$fixe7, _validationError$fixe8;
3259
+ var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4, _validationError$fixe5, _validationError$fixe6;
3231
3260
  if (isOnlyFileCheckValidationError(validationError)) {
3232
3261
  if (files[sourcePath]) {
3233
3262
  addError({
@@ -3241,13 +3270,13 @@ class ValOps {
3241
3270
  }
3242
3271
  } else if ((_validationError$fixe = validationError.fixes) !== null && _validationError$fixe !== void 0 && _validationError$fixe.includes("image:check-remote") || (_validationError$fixe2 = validationError.fixes) !== null && _validationError$fixe2 !== void 0 && _validationError$fixe2.includes("file:check-remote")) {
3243
3272
  remoteFiles[sourcePath] = validationError.value;
3244
- } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("keyof:check-keys") || (_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("router:check-route")) {
3273
+ } else if (internal.isSchemaSourceFixError(validationError)) {
3245
3274
  const resolved = internal.resolveSchemaSourceFixForError(validationError, snapshot);
3246
3275
  if (resolved && resolved.status === "remaining") {
3247
3276
  addError(resolved.error);
3248
3277
  }
3249
3278
  // resolved.status === "resolved" → drop silently
3250
- } else if ((_validationError$fixe5 = validationError.fixes) !== null && _validationError$fixe5 !== void 0 && _validationError$fixe5.includes("images:check-unique-folder") || (_validationError$fixe6 = validationError.fixes) !== null && _validationError$fixe6 !== void 0 && _validationError$fixe6.includes("files:check-unique-folder")) {
3279
+ } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("images:check-unique-folder") || (_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("files:check-unique-folder")) {
3251
3280
  const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
3252
3281
  if (!validationError.value || typeof validationError.value !== "object") {
3253
3282
  addError({
@@ -3272,7 +3301,7 @@ class ValOps {
3272
3301
  // If conflictingModules is empty, directory is unique — silently drop the error.
3273
3302
  }
3274
3303
  }
3275
- } else if ((_validationError$fixe7 = validationError.fixes) !== null && _validationError$fixe7 !== void 0 && _validationError$fixe7.includes("images:check-all-files") || (_validationError$fixe8 = validationError.fixes) !== null && _validationError$fixe8 !== void 0 && _validationError$fixe8.includes("files:check-all-files")) ; else {
3304
+ } else if ((_validationError$fixe5 = validationError.fixes) !== null && _validationError$fixe5 !== void 0 && _validationError$fixe5.includes("images:check-all-files") || (_validationError$fixe6 = validationError.fixes) !== null && _validationError$fixe6 !== void 0 && _validationError$fixe6.includes("files:check-all-files")) ; else {
3276
3305
  addError(validationError);
3277
3306
  }
3278
3307
  }
@@ -4132,8 +4161,8 @@ class ValOps {
4132
4161
  // #endregion history
4133
4162
  }
4134
4163
  function isOnlyFileCheckValidationError(validationError) {
4135
- var _validationError$fixe9;
4136
- if ((_validationError$fixe9 = validationError.fixes) !== null && _validationError$fixe9 !== void 0 && _validationError$fixe9.every(f => f === "file:check-metadata" || f === "image:check-metadata")) {
4164
+ var _validationError$fixe7;
4165
+ if ((_validationError$fixe7 = validationError.fixes) !== null && _validationError$fixe7 !== void 0 && _validationError$fixe7.every(f => f === "file:check-metadata" || f === "image:check-metadata")) {
4137
4166
  return true;
4138
4167
  }
4139
4168
  return false;
@@ -5654,6 +5683,7 @@ function serializeSchemaSafely(schema) {
5654
5683
  }
5655
5684
  class ValOpsFS extends ValOps {
5656
5685
  static VAL_DIR = ".val";
5686
+ host;
5657
5687
  constructor(contentUrl, rootDir, valModules, options) {
5658
5688
  super(valModules, options);
5659
5689
  this.contentUrl = contentUrl;
@@ -5666,6 +5696,7 @@ class ValOpsFS extends ValOps {
5666
5696
  * Change detection for `.jsonValues()` entry files, which no sha can see (their
5667
5697
  * content lives behind a thunk that `JSON.stringify` drops).
5668
5698
  */
5699
+ jsonEntryFilesFingerprint;
5669
5700
 
5670
5701
  /**
5671
5702
  * Unpublished changes repair threw away, waiting to be told to somebody.
@@ -7015,7 +7046,11 @@ const GetApplicablePatches = z.z.object({
7015
7046
  commitSha: z.z.string(),
7016
7047
  deploymentState: z.z.string(),
7017
7048
  createdAt: z.z.string(),
7018
- updatedAt: z.z.string()
7049
+ updatedAt: z.z.string(),
7050
+ // The git message, where the content service knows it. See
7051
+ // `ValDeployment`: this is the only source for a deployment that Val
7052
+ // did not publish, and zod would otherwise strip it here.
7053
+ commitMessage: z.z.string().nullable().optional()
7019
7054
  })).optional()
7020
7055
  });
7021
7056
  const FilesResponse = z.z.object({
@@ -7191,6 +7226,8 @@ const NonceResponse = z.z.object({
7191
7226
  */
7192
7227
  const PATCH_GROUPS_CACHE_MS = 1000;
7193
7228
  class ValOpsHttp extends ValOps {
7229
+ authHeaders;
7230
+ root;
7194
7231
  constructor(contentUrl, project, commitSha,
7195
7232
  // TODO: CommitSha
7196
7233
  branch,
@@ -7616,7 +7653,8 @@ class ValOpsHttp extends ValOps {
7616
7653
  deploymentId: deployment.deploymentId,
7617
7654
  deploymentState: deployment.deploymentState,
7618
7655
  createdAt: deployment.createdAt,
7619
- updatedAt: deployment.updatedAt
7656
+ updatedAt: deployment.updatedAt,
7657
+ commitMessage: deployment.commitMessage
7620
7658
  });
7621
7659
  }
7622
7660
  }
@@ -13545,6 +13583,8 @@ const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
13545
13583
  const MAX_OBJECT_KEY_SIZE = 2 ** 27; // https://stackoverflow.com/questions/13367391/is-there-a-limit-on-length-of-the-key-string-in-js-object
13546
13584
 
13547
13585
  class ValModuleLoader {
13586
+ cache;
13587
+ cacheSize;
13548
13588
  constructor(projectRoot, compilerOptions,
13549
13589
  // TODO: remove this?
13550
13590
  sourceFileHandler, host = {
@@ -15224,9 +15264,10 @@ async function handleExternalUpload() {
15224
15264
  };
15225
15265
  }
15226
15266
 
15227
- // Fix handler registry. `keyof:check-keys` and `router:check-route` are
15228
- // resolved upfront by the shared resolveSchemaSourceFixes — they never reach
15229
- // this registry, so they're excluded from the key set.
15267
+ // Fix handler registry. `keyof:check-keys`, `router:check-route`,
15268
+ // `locale:check-locale` and `record:fill-keys` are resolved upfront by the
15269
+ // shared resolveSchemaSourceFixes — they never reach this registry, so they're
15270
+ // excluded from the key set.
15230
15271
  const currentFixHandlers = {
15231
15272
  "image:check-metadata": handleFileMetadata,
15232
15273
  "image:add-metadata": handleFileMetadata,
@@ -15386,7 +15427,7 @@ async function evalValConfigFile(projectRoot, configFileName) {
15386
15427
  },
15387
15428
  fileName: valConfigPath
15388
15429
  });
15389
- const projectRootRequire = node_module.createRequire(valConfigPath);
15430
+ const projectRootRequire = createNodeRequire(valConfigPath);
15390
15431
  const exportsObj = {};
15391
15432
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15392
15433
  const sandbox = {
@@ -7,8 +7,8 @@ import * as path from 'path';
7
7
  import path__default from 'path';
8
8
  import fs, { promises } from 'fs';
9
9
  import vm from 'node:vm';
10
- import { Module, createRequire } from 'node:module';
11
- import { resolveSchemaSourceFixForError, Patch, getErrorMessageFromUnknownJson, JSONValue, PatchGroup, newestCommitSha, SerializedSchema, VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE, VAL_SESSION_COOKIE, Api } from '@valbuild/shared/internal';
10
+ import { Module } from 'node:module';
11
+ import { isSchemaSourceFixError, resolveSchemaSourceFixForError, Patch, getErrorMessageFromUnknownJson, JSONValue, PatchGroup, newestCommitSha, SerializedSchema, VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE, VAL_SESSION_COOKIE, Api } from '@valbuild/shared/internal';
12
12
  import { createUIRequestHandler } from '@valbuild/ui/server';
13
13
  import crypto$1 from 'crypto';
14
14
  import z$1, { z } from 'zod';
@@ -662,6 +662,32 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
662
662
  return parsedConfigFile.options;
663
663
  };
664
664
 
665
+ /**
666
+ * Node's `require`, resolving as if from `filename`.
667
+ *
668
+ * Use this instead of importing `createRequire` from `node:module` directly.
669
+ * webpack special-cases a `createRequire` binding imported from `module` /
670
+ * `node:module`: it tries to resolve the call's argument at build time, and
671
+ * when the argument is not a literal it gives up with
672
+ *
673
+ * module.createRequire failed parsing argument.
674
+ *
675
+ * Our argument is never a literal — it is a path inside the user's project,
676
+ * known only at runtime — so there is nothing for that analysis to find, and
677
+ * nothing the warning can tell anyone. It is emitted on every build of every
678
+ * Next app that bundles `@valbuild/server` into its Val API route, which is
679
+ * all of them. Reaching `createRequire` through the `Module` class is the same
680
+ * function and is not what webpack tags, so the call is invisible to it.
681
+ *
682
+ * It has to stay a real `node:module` import: jest's module registry hands out
683
+ * its own `node:module`, and a `require` obtained around it (via
684
+ * `process.getBuiltinModule`, say) would resolve against the real filesystem
685
+ * rather than the registry the tests run in.
686
+ */
687
+ function createNodeRequire(filename) {
688
+ return Module.createRequire(filename);
689
+ }
690
+
665
691
  /**
666
692
  * The filesystem seam `loadValModules` reads through.
667
693
  *
@@ -795,7 +821,7 @@ function loadModule(absPath, cache, compilerOptions, host) {
795
821
  // Insert into the cache before evaluating so cyclic imports resolve.
796
822
  cache[absPath] = moduleObj;
797
823
  const dirName = path__default.dirname(absPath);
798
- const realRequire = Module.createRequire(absPath);
824
+ const realRequire = createNodeRequire(absPath);
799
825
  const customRequire = spec => {
800
826
  var _ts$resolveModuleName;
801
827
  if (isStubbedSpecifier(spec)) {
@@ -1102,7 +1128,7 @@ function findNestedJsonValuesRecords(schema, path = []) {
1102
1128
  case "array":
1103
1129
  rec(current.item, currentPath.concat("*"));
1104
1130
  return;
1105
- case "union":
1131
+ case "discriminated-union":
1106
1132
  for (let i = 0; i < current.items.length; i++) {
1107
1133
  rec(current.items[i], currentPath.concat(`union[${i}]`));
1108
1134
  }
@@ -1688,6 +1714,7 @@ async function createService(projectRoot, host = {
1688
1714
  return new Service(projectRoot, sourceFileHandler, extracted);
1689
1715
  }
1690
1716
  class Service {
1717
+ projectRoot;
1691
1718
  constructor(projectRoot, sourceFileHandler, extracted) {
1692
1719
  this.sourceFileHandler = sourceFileHandler;
1693
1720
  this.extracted = extracted;
@@ -2327,17 +2354,18 @@ function findNestedJsonValuesModuleErrors(schemas) {
2327
2354
  // #region ValOps
2328
2355
  class ValOps {
2329
2356
  /** Sources from val modules, immutable (without patches or anything) */
2330
-
2357
+ sources;
2331
2358
  /** The sha256 / hash of all sources + all schemas + config */
2332
-
2359
+ baseSha;
2333
2360
  /** The sha256 / hash of all sources */
2334
-
2361
+ sourcesSha;
2335
2362
  /** The sha256 / hash of config */
2336
-
2363
+ configSha;
2337
2364
  /** Schema from val modules, immutable */
2338
-
2365
+ schemas;
2339
2366
  /** The sha256 / hash of schema + config - if this changes users needs to reload */
2340
-
2367
+ schemaSha;
2368
+ modulesErrors;
2341
2369
  /**
2342
2370
  * What the SHAs above are a fold over, so they can be recomputed.
2343
2371
  *
@@ -2345,7 +2373,7 @@ class ValOps {
2345
2373
  * without re-evaluating the modules is a save, and it has to be able to move
2346
2374
  * the SHAs with them.
2347
2375
  */
2348
-
2376
+ shaEntries;
2349
2377
  /**
2350
2378
  * The extraction's OWN module errors, which are what the fold was given.
2351
2379
  *
@@ -2353,7 +2381,7 @@ class ValOps {
2353
2381
  * `.jsonValues()` errors concatenated on, and those were never part of the
2354
2382
  * hash. Re-folding with the wrong list changes the base SHA for no reason.
2355
2383
  */
2356
-
2384
+ shaModuleErrors;
2357
2385
  /**
2358
2386
  * What a save has told us each `.jsonValues()` entry now holds.
2359
2387
  *
@@ -3113,7 +3141,8 @@ class ValOps {
3113
3141
  // (must include all modules, not just those being validated, since conflicts can come from any module)
3114
3142
  const galleryDirectoryToModules = new Map();
3115
3143
  // Build a schema/source snapshot so the shared resolver can cross-reference
3116
- // keyof:check-keys and router:check-route against every module's data.
3144
+ // the marker fixes — a keyOf's key, a route, a locale tag, a record's
3145
+ // declared keys — against every module's data. See `SCHEMA_SOURCE_FIXES`.
3117
3146
  const snapshot = {
3118
3147
  schemas: {},
3119
3148
  sources: {}
@@ -3193,7 +3222,7 @@ class ValOps {
3193
3222
  };
3194
3223
  if (validationErrors) {
3195
3224
  for (const validationError of validationErrors) {
3196
- var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4, _validationError$fixe5, _validationError$fixe6, _validationError$fixe7, _validationError$fixe8;
3225
+ var _validationError$fixe, _validationError$fixe2, _validationError$fixe3, _validationError$fixe4, _validationError$fixe5, _validationError$fixe6;
3197
3226
  if (isOnlyFileCheckValidationError(validationError)) {
3198
3227
  if (files[sourcePath]) {
3199
3228
  addError({
@@ -3207,13 +3236,13 @@ class ValOps {
3207
3236
  }
3208
3237
  } else if ((_validationError$fixe = validationError.fixes) !== null && _validationError$fixe !== void 0 && _validationError$fixe.includes("image:check-remote") || (_validationError$fixe2 = validationError.fixes) !== null && _validationError$fixe2 !== void 0 && _validationError$fixe2.includes("file:check-remote")) {
3209
3238
  remoteFiles[sourcePath] = validationError.value;
3210
- } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("keyof:check-keys") || (_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("router:check-route")) {
3239
+ } else if (isSchemaSourceFixError(validationError)) {
3211
3240
  const resolved = resolveSchemaSourceFixForError(validationError, snapshot);
3212
3241
  if (resolved && resolved.status === "remaining") {
3213
3242
  addError(resolved.error);
3214
3243
  }
3215
3244
  // resolved.status === "resolved" → drop silently
3216
- } else if ((_validationError$fixe5 = validationError.fixes) !== null && _validationError$fixe5 !== void 0 && _validationError$fixe5.includes("images:check-unique-folder") || (_validationError$fixe6 = validationError.fixes) !== null && _validationError$fixe6 !== void 0 && _validationError$fixe6.includes("files:check-unique-folder")) {
3245
+ } else if ((_validationError$fixe3 = validationError.fixes) !== null && _validationError$fixe3 !== void 0 && _validationError$fixe3.includes("images:check-unique-folder") || (_validationError$fixe4 = validationError.fixes) !== null && _validationError$fixe4 !== void 0 && _validationError$fixe4.includes("files:check-unique-folder")) {
3217
3246
  const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
3218
3247
  if (!validationError.value || typeof validationError.value !== "object") {
3219
3248
  addError({
@@ -3238,7 +3267,7 @@ class ValOps {
3238
3267
  // If conflictingModules is empty, directory is unique — silently drop the error.
3239
3268
  }
3240
3269
  }
3241
- } else if ((_validationError$fixe7 = validationError.fixes) !== null && _validationError$fixe7 !== void 0 && _validationError$fixe7.includes("images:check-all-files") || (_validationError$fixe8 = validationError.fixes) !== null && _validationError$fixe8 !== void 0 && _validationError$fixe8.includes("files:check-all-files")) ; else {
3270
+ } else if ((_validationError$fixe5 = validationError.fixes) !== null && _validationError$fixe5 !== void 0 && _validationError$fixe5.includes("images:check-all-files") || (_validationError$fixe6 = validationError.fixes) !== null && _validationError$fixe6 !== void 0 && _validationError$fixe6.includes("files:check-all-files")) ; else {
3242
3271
  addError(validationError);
3243
3272
  }
3244
3273
  }
@@ -4098,8 +4127,8 @@ class ValOps {
4098
4127
  // #endregion history
4099
4128
  }
4100
4129
  function isOnlyFileCheckValidationError(validationError) {
4101
- var _validationError$fixe9;
4102
- if ((_validationError$fixe9 = validationError.fixes) !== null && _validationError$fixe9 !== void 0 && _validationError$fixe9.every(f => f === "file:check-metadata" || f === "image:check-metadata")) {
4130
+ var _validationError$fixe7;
4131
+ if ((_validationError$fixe7 = validationError.fixes) !== null && _validationError$fixe7 !== void 0 && _validationError$fixe7.every(f => f === "file:check-metadata" || f === "image:check-metadata")) {
4103
4132
  return true;
4104
4133
  }
4105
4134
  return false;
@@ -5620,6 +5649,7 @@ function serializeSchemaSafely(schema) {
5620
5649
  }
5621
5650
  class ValOpsFS extends ValOps {
5622
5651
  static VAL_DIR = ".val";
5652
+ host;
5623
5653
  constructor(contentUrl, rootDir, valModules, options) {
5624
5654
  super(valModules, options);
5625
5655
  this.contentUrl = contentUrl;
@@ -5632,6 +5662,7 @@ class ValOpsFS extends ValOps {
5632
5662
  * Change detection for `.jsonValues()` entry files, which no sha can see (their
5633
5663
  * content lives behind a thunk that `JSON.stringify` drops).
5634
5664
  */
5665
+ jsonEntryFilesFingerprint;
5635
5666
 
5636
5667
  /**
5637
5668
  * Unpublished changes repair threw away, waiting to be told to somebody.
@@ -6981,7 +7012,11 @@ const GetApplicablePatches = z.object({
6981
7012
  commitSha: z.string(),
6982
7013
  deploymentState: z.string(),
6983
7014
  createdAt: z.string(),
6984
- updatedAt: z.string()
7015
+ updatedAt: z.string(),
7016
+ // The git message, where the content service knows it. See
7017
+ // `ValDeployment`: this is the only source for a deployment that Val
7018
+ // did not publish, and zod would otherwise strip it here.
7019
+ commitMessage: z.string().nullable().optional()
6985
7020
  })).optional()
6986
7021
  });
6987
7022
  const FilesResponse = z.object({
@@ -7157,6 +7192,8 @@ const NonceResponse = z.object({
7157
7192
  */
7158
7193
  const PATCH_GROUPS_CACHE_MS = 1000;
7159
7194
  class ValOpsHttp extends ValOps {
7195
+ authHeaders;
7196
+ root;
7160
7197
  constructor(contentUrl, project, commitSha,
7161
7198
  // TODO: CommitSha
7162
7199
  branch,
@@ -7582,7 +7619,8 @@ class ValOpsHttp extends ValOps {
7582
7619
  deploymentId: deployment.deploymentId,
7583
7620
  deploymentState: deployment.deploymentState,
7584
7621
  createdAt: deployment.createdAt,
7585
- updatedAt: deployment.updatedAt
7622
+ updatedAt: deployment.updatedAt,
7623
+ commitMessage: deployment.commitMessage
7586
7624
  });
7587
7625
  }
7588
7626
  }
@@ -13511,6 +13549,8 @@ const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
13511
13549
  const MAX_OBJECT_KEY_SIZE = 2 ** 27; // https://stackoverflow.com/questions/13367391/is-there-a-limit-on-length-of-the-key-string-in-js-object
13512
13550
 
13513
13551
  class ValModuleLoader {
13552
+ cache;
13553
+ cacheSize;
13514
13554
  constructor(projectRoot, compilerOptions,
13515
13555
  // TODO: remove this?
13516
13556
  sourceFileHandler, host = {
@@ -15190,9 +15230,10 @@ async function handleExternalUpload() {
15190
15230
  };
15191
15231
  }
15192
15232
 
15193
- // Fix handler registry. `keyof:check-keys` and `router:check-route` are
15194
- // resolved upfront by the shared resolveSchemaSourceFixes — they never reach
15195
- // this registry, so they're excluded from the key set.
15233
+ // Fix handler registry. `keyof:check-keys`, `router:check-route`,
15234
+ // `locale:check-locale` and `record:fill-keys` are resolved upfront by the
15235
+ // shared resolveSchemaSourceFixes — they never reach this registry, so they're
15236
+ // excluded from the key set.
15196
15237
  const currentFixHandlers = {
15197
15238
  "image:check-metadata": handleFileMetadata,
15198
15239
  "image:add-metadata": handleFileMetadata,
@@ -15352,7 +15393,7 @@ async function evalValConfigFile(projectRoot, configFileName) {
15352
15393
  },
15353
15394
  fileName: valConfigPath
15354
15395
  });
15355
- const projectRootRequire = createRequire(valConfigPath);
15396
+ const projectRootRequire = createNodeRequire(valConfigPath);
15356
15397
  const exportsObj = {};
15357
15398
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15358
15399
  const sandbox = {
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "types": "dist/valbuild-server.cjs.d.ts",
19
- "version": "0.125.0",
19
+ "version": "0.127.0",
20
20
  "devDependencies": {
21
21
  "@prettier/sync": "^0.6.1",
22
22
  "@types/jest": "^30.0.0"
@@ -29,9 +29,9 @@
29
29
  "typescript": "^6.0.3",
30
30
  "zod": "^4.4.3",
31
31
  "zod-validation-error": "^5.0.0",
32
- "@valbuild/core": "0.125.0",
33
- "@valbuild/shared": "0.125.0",
34
- "@valbuild/ui": "0.125.0"
32
+ "@valbuild/shared": "0.127.0",
33
+ "@valbuild/ui": "0.127.0",
34
+ "@valbuild/core": "0.127.0"
35
35
  },
36
36
  "engines": {
37
37
  "node": "^20.19.0 || >=22"