@valbuild/server 0.126.0 → 0.128.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,99 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.128.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`8b52b33`](https://github.com/valbuild/val/commit/8b52b33e1f629f14f66cc71dcc7cf415d210c7d4), [`362fb49`](https://github.com/valbuild/val/commit/362fb49f30d2b04c4ff78d54dca2bf5ad978a05c), [`c595799`](https://github.com/valbuild/val/commit/c59579977a436ec530c6c69f2b340ab9829d97ab)]:
8
+ - @valbuild/core@0.128.0
9
+ - @valbuild/shared@0.128.0
10
+ - @valbuild/ui@0.127.0
11
+
12
+ ## 0.127.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [#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.
17
+
18
+ Two key schemas enumerate their keys: `s.locale()`, whose set is the project's
19
+ `locales.available`, and a union of literals. For those, the keys are part of the
20
+ schema, so a missing one is a hole in the content rather than content nobody has
21
+ written yet — and validation now says so, naming what is missing.
22
+
23
+ ```typescript
24
+ s.record(s.locale(), s.object({ title: s.string() }));
25
+ // Missing key: 'nb-NO'. This record's keys are declared by its schema, so
26
+ // every one of them is an entry — an entry nobody has written yet is null,
27
+ // not absent.
28
+ ```
29
+
30
+ **An entry nobody has written yet is `null`.** Not an absent key: a null entry is
31
+ data you can count, filter and see in a diff, and it means half-translated
32
+ content stays _valid_ rather than blocking a publish. The value type of such a
33
+ record widens by `null` to match, so writing one in a `.val.ts` type-checks:
34
+
35
+ ```typescript
36
+ c.define(
37
+ "/content/jacket.val.ts",
38
+ s.record(s.locale(), s.object({ title: s.string() })),
39
+ {
40
+ "en-US": { title: "Winter jacket" },
41
+ "nb-NO": null, // nobody has translated this yet
42
+ },
43
+ );
44
+ ```
45
+
46
+ **This changes `s.record(s.union(...), item)`**, and closes a gap that was
47
+ already there: `s.record(s.union(s.literal("a"), s.literal("b")), item)` types as
48
+ `Record<"a" | "b", T>`, so TypeScript demanded both keys while the validator only
49
+ checked the ones present. It now checks them too, and — as above — accepts `null`
50
+ for an entry that has not been filled in. If you have such a record with keys
51
+ missing, validation will report them; adding the keys with `null` values is the
52
+ fix, and creating one from the Studio does it for you.
53
+
54
+ `emptyOf` creates these records with every key already in them rather than
55
+ empty, since an empty one is already missing keys. In the Studio use the
56
+ `useEmptyOf()` hook rather than importing `emptyOf` directly: a locale record's
57
+ keys are in the settings module, and the hook is what has read it.
58
+
59
+ - [#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.
60
+
61
+ The languages themselves are declared in the settings module (`locales.available`);
62
+ this says that a value is one of them.
63
+
64
+ ```typescript
65
+ // a field: everything in this entry is in this language
66
+ s.record(s.string(), s.object({ locale: s.locale(), title: s.string() }));
67
+
68
+ // a key: one entry per language
69
+ s.record(s.locale(), s.object({ title: s.string() }));
70
+ ```
71
+
72
+ Every locale in content is checked against the project's list, the way `keyOf`
73
+ and `route` are checked against what they point at. An undeclared language names
74
+ the ones the project has; a project that has declared none is told to declare
75
+ them rather than told the value is wrong.
76
+
77
+ A locale is stored as the tag itself — the value in content is `nb-NO`, and a
78
+ record keyed by `s.locale()` has `nb-NO` as its key. Spelling one differently
79
+ where it is stored (`/no/…` as a URL segment) is a real need and is deliberately
80
+ not in this release: it changes what is accepted as well as what is shown, so it
81
+ is being designed on its own rather than folded in here.
82
+
83
+ A locale is **never stega encoded**: it ends up in `<html lang>`, in `hreflang`
84
+ and in `Intl` constructors, none of which survive invisible characters.
85
+
86
+ `assistant.translation` joins the settings module alongside `context` and `tone`
87
+ — a note per language, keyed by language, so only the target language's rules are
88
+ sent when translating into it.
89
+
90
+ ### Patch Changes
91
+
92
+ - 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)]:
93
+ - @valbuild/core@0.127.0
94
+ - @valbuild/shared@0.127.0
95
+ - @valbuild/ui@0.127.0
96
+
3
97
  ## 0.126.0
4
98
 
5
99
  ### Patch Changes
@@ -184,7 +184,7 @@ export declare function handleUniqueFolderCheck(ctx: FixHandlerContext): Promise
184
184
  */
185
185
  export declare function checkGalleryFiles(input: {
186
186
  entryKeys: string[];
187
- directory: string;
187
+ dir: string;
188
188
  projectRoot: string;
189
189
  fs: Pick<IValFSHost, "fileExists" | "readDirectory">;
190
190
  }): {
@@ -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;
@@ -1748,6 +1748,7 @@ async function createService(projectRoot, host = {
1748
1748
  return new Service(projectRoot, sourceFileHandler, extracted);
1749
1749
  }
1750
1750
  class Service {
1751
+ projectRoot;
1751
1752
  constructor(projectRoot, sourceFileHandler, extracted) {
1752
1753
  this.sourceFileHandler = sourceFileHandler;
1753
1754
  this.extracted = extracted;
@@ -2387,17 +2388,18 @@ function findNestedJsonValuesModuleErrors(schemas) {
2387
2388
  // #region ValOps
2388
2389
  class ValOps {
2389
2390
  /** Sources from val modules, immutable (without patches or anything) */
2390
-
2391
+ sources;
2391
2392
  /** The sha256 / hash of all sources + all schemas + config */
2392
-
2393
+ baseSha;
2393
2394
  /** The sha256 / hash of all sources */
2394
-
2395
+ sourcesSha;
2395
2396
  /** The sha256 / hash of config */
2396
-
2397
+ configSha;
2397
2398
  /** Schema from val modules, immutable */
2398
-
2399
+ schemas;
2399
2400
  /** The sha256 / hash of schema + config - if this changes users needs to reload */
2400
-
2401
+ schemaSha;
2402
+ modulesErrors;
2401
2403
  /**
2402
2404
  * What the SHAs above are a fold over, so they can be recomputed.
2403
2405
  *
@@ -2405,7 +2407,7 @@ class ValOps {
2405
2407
  * without re-evaluating the modules is a save, and it has to be able to move
2406
2408
  * the SHAs with them.
2407
2409
  */
2408
-
2410
+ shaEntries;
2409
2411
  /**
2410
2412
  * The extraction's OWN module errors, which are what the fold was given.
2411
2413
  *
@@ -2413,7 +2415,7 @@ class ValOps {
2413
2415
  * `.jsonValues()` errors concatenated on, and those were never part of the
2414
2416
  * hash. Re-folding with the wrong list changes the base SHA for no reason.
2415
2417
  */
2416
-
2418
+ shaModuleErrors;
2417
2419
  /**
2418
2420
  * What a save has told us each `.jsonValues()` entry now holds.
2419
2421
  *
@@ -3173,7 +3175,8 @@ class ValOps {
3173
3175
  // (must include all modules, not just those being validated, since conflicts can come from any module)
3174
3176
  const galleryDirectoryToModules = new Map();
3175
3177
  // Build a schema/source snapshot so the shared resolver can cross-reference
3176
- // 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`.
3177
3180
  const snapshot = {
3178
3181
  schemas: {},
3179
3182
  sources: {}
@@ -3186,8 +3189,8 @@ class ValOps {
3186
3189
  if (sourceForModule !== undefined) {
3187
3190
  snapshot.sources[moduleFilePath] = sourceForModule;
3188
3191
  }
3189
- if (serialized.type === "record" && serialized.mediaType && serialized.directory) {
3190
- const dir = serialized.directory;
3192
+ if (serialized.type === "record" && serialized.mediaType && serialized.dir) {
3193
+ const dir = serialized.dir;
3191
3194
  const existing = galleryDirectoryToModules.get(dir);
3192
3195
  if (existing) {
3193
3196
  existing.push(moduleFilePath);
@@ -3253,7 +3256,7 @@ class ValOps {
3253
3256
  };
3254
3257
  if (validationErrors) {
3255
3258
  for (const validationError of validationErrors) {
3256
- 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;
3257
3260
  if (isOnlyFileCheckValidationError(validationError)) {
3258
3261
  if (files[sourcePath]) {
3259
3262
  addError({
@@ -3267,13 +3270,13 @@ class ValOps {
3267
3270
  }
3268
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")) {
3269
3272
  remoteFiles[sourcePath] = validationError.value;
3270
- } 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)) {
3271
3274
  const resolved = internal.resolveSchemaSourceFixForError(validationError, snapshot);
3272
3275
  if (resolved && resolved.status === "remaining") {
3273
3276
  addError(resolved.error);
3274
3277
  }
3275
3278
  // resolved.status === "resolved" → drop silently
3276
- } 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")) {
3277
3280
  const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
3278
3281
  if (!validationError.value || typeof validationError.value !== "object") {
3279
3282
  addError({
@@ -3281,10 +3284,10 @@ class ValOps {
3281
3284
  typeError: true
3282
3285
  });
3283
3286
  } else {
3284
- const directory = "directory" in validationError.value && validationError.value.directory;
3287
+ const directory = "dir" in validationError.value && validationError.value.dir;
3285
3288
  if (typeof directory !== "string") {
3286
3289
  addError({
3287
- message: `Expected gallery validation error 'value' to have property 'directory' of type 'string'. Found: ${typeof directory}. ${TYPE_ERROR_MESSAGE}`,
3290
+ message: `Expected gallery validation error 'value' to have property 'dir' of type 'string'. Found: ${typeof directory}. ${TYPE_ERROR_MESSAGE}`,
3288
3291
  typeError: true
3289
3292
  });
3290
3293
  } else {
@@ -3298,7 +3301,7 @@ class ValOps {
3298
3301
  // If conflictingModules is empty, directory is unique — silently drop the error.
3299
3302
  }
3300
3303
  }
3301
- } 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 {
3302
3305
  addError(validationError);
3303
3306
  }
3304
3307
  }
@@ -4158,8 +4161,8 @@ class ValOps {
4158
4161
  // #endregion history
4159
4162
  }
4160
4163
  function isOnlyFileCheckValidationError(validationError) {
4161
- var _validationError$fixe9;
4162
- 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")) {
4163
4166
  return true;
4164
4167
  }
4165
4168
  return false;
@@ -5680,6 +5683,7 @@ function serializeSchemaSafely(schema) {
5680
5683
  }
5681
5684
  class ValOpsFS extends ValOps {
5682
5685
  static VAL_DIR = ".val";
5686
+ host;
5683
5687
  constructor(contentUrl, rootDir, valModules, options) {
5684
5688
  super(valModules, options);
5685
5689
  this.contentUrl = contentUrl;
@@ -5692,6 +5696,7 @@ class ValOpsFS extends ValOps {
5692
5696
  * Change detection for `.jsonValues()` entry files, which no sha can see (their
5693
5697
  * content lives behind a thunk that `JSON.stringify` drops).
5694
5698
  */
5699
+ jsonEntryFilesFingerprint;
5695
5700
 
5696
5701
  /**
5697
5702
  * Unpublished changes repair threw away, waiting to be told to somebody.
@@ -7221,6 +7226,8 @@ const NonceResponse = z.z.object({
7221
7226
  */
7222
7227
  const PATCH_GROUPS_CACHE_MS = 1000;
7223
7228
  class ValOpsHttp extends ValOps {
7229
+ authHeaders;
7230
+ root;
7224
7231
  constructor(contentUrl, project, commitSha,
7225
7232
  // TODO: CommitSha
7226
7233
  branch,
@@ -13576,6 +13583,8 @@ const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
13576
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
13577
13584
 
13578
13585
  class ValModuleLoader {
13586
+ cache;
13587
+ cacheSize;
13579
13588
  constructor(projectRoot, compilerOptions,
13580
13589
  // TODO: remove this?
13581
13590
  sourceFileHandler, host = {
@@ -14955,7 +14964,7 @@ async function handleRemoteFileUpload(ctx) {
14955
14964
  return uploadRemoteFileCore(ctx, fileRefProp, fileSourceMetadata, resolveRemoteFileSchema);
14956
14965
  }
14957
14966
 
14958
- // Gallery (s.images({ ... }).remote() / s.files({ ... }).remote()) upload.
14967
+ // Gallery (s.imageset({ ... }).remote() / s.fileset({ ... }).remote()) upload.
14959
14968
  // Unlike a single image/file field, a gallery entry is keyed by its local file
14960
14969
  // path and the value is bare metadata (no FileSource), so we derive the file
14961
14970
  // ref from the key and synthesize the image/file schema from the record's
@@ -14996,10 +15005,10 @@ async function handleRemoteGalleryFileUpload(ctx) {
14996
15005
  // synthesize the serialized image/file schema (matching how single fields
14997
15006
  // serialize) for the remote ref's validation hash.
14998
15007
  //
14999
- // `accept`/`directory` come from the RECORD that holds the entry, resolved
15008
+ // `accept`/`dir` come from the RECORD that holds the entry, resolved
15000
15009
  // from the entry's PARENT path - not from the module root, which is only the
15001
15010
  // record when the gallery is the whole module. A nested gallery
15002
- // (s.object({ gallery: s.images(...) })) would otherwise synthesize a schema
15011
+ // (s.object({ gallery: s.imageset(...) })) would otherwise synthesize a schema
15003
15012
  // with no options and bake a validation hash into the remote ref that can
15004
15013
  // never validate, so a mismatch fails fast instead of uploading.
15005
15014
  const [, parentModulePath] = core.Internal.splitModuleFilePathAndModulePath(core.Internal.parentOfSourcePath(ctx.sourcePath));
@@ -15012,7 +15021,7 @@ async function handleRemoteGalleryFileUpload(ctx) {
15012
15021
  }
15013
15022
  const {
15014
15023
  accept,
15015
- directory
15024
+ dir
15016
15025
  } = recordSchema;
15017
15026
  const schema = mediaType === "image" ? {
15018
15027
  type: "image",
@@ -15021,8 +15030,8 @@ async function handleRemoteGalleryFileUpload(ctx) {
15021
15030
  ...(accept ? {
15022
15031
  accept
15023
15032
  } : {}),
15024
- ...(directory ? {
15025
- directory
15033
+ ...(dir ? {
15034
+ dir
15026
15035
  } : {})
15027
15036
  }
15028
15037
  } : {
@@ -15062,14 +15071,14 @@ async function handleRemoteFileCheck() {
15062
15071
  }
15063
15072
  async function handleUniqueFolderCheck(ctx) {
15064
15073
  const value = ctx.validationError.value;
15065
- if (!value || typeof value.directory !== "string") {
15074
+ if (!value || typeof value.dir !== "string") {
15066
15075
  return {
15067
15076
  success: false,
15068
15077
  errorMessage: `Unexpected value in unique folder check for ${ctx.sourcePath}`
15069
15078
  };
15070
15079
  }
15071
15080
  const {
15072
- directory
15081
+ dir
15073
15082
  } = value;
15074
15083
  const conflicts = [];
15075
15084
  for (const file of ctx.valFiles) {
@@ -15079,14 +15088,14 @@ async function handleUniqueFolderCheck(ctx) {
15079
15088
  validate: false
15080
15089
  });
15081
15090
  const schema = otherModule.schema;
15082
- if ((schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.directory === directory && schema.mediaType) {
15091
+ if ((schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.dir === dir && schema.mediaType) {
15083
15092
  conflicts.push(otherModuleFilePath);
15084
15093
  }
15085
15094
  }
15086
15095
  if (conflicts.length > 0) {
15087
15096
  return {
15088
15097
  success: false,
15089
- errorMessage: `Gallery directory '${directory}' in ${ctx.moduleFilePath} is also used by: ${conflicts.join(", ")}. Each gallery must use a unique directory.`
15098
+ errorMessage: `Gallery directory '${dir}' in ${ctx.moduleFilePath} is also used by: ${conflicts.join(", ")}. Each gallery must use a unique directory.`
15090
15099
  };
15091
15100
  }
15092
15101
  return {
@@ -15116,7 +15125,7 @@ async function handleUniqueFolderCheck(ctx) {
15116
15125
  */
15117
15126
  function checkGalleryFiles(input) {
15118
15127
  const {
15119
- directory,
15128
+ dir,
15120
15129
  projectRoot,
15121
15130
  fs
15122
15131
  } = input;
@@ -15125,7 +15134,7 @@ function checkGalleryFiles(input) {
15125
15134
  const missingTrackedFiles = entries.filter(entry => !entry.remote && !fs.fileExists(path__namespace["default"].join(projectRoot, entry.localPath))).map(entry => entry.localPath);
15126
15135
  const filesInDir = [];
15127
15136
  try {
15128
- const found = fs.readDirectory(path__namespace["default"].join(projectRoot, directory), undefined, undefined, ["**/*"]);
15137
+ const found = fs.readDirectory(path__namespace["default"].join(projectRoot, dir), undefined, undefined, ["**/*"]);
15129
15138
  for (const entry of found) {
15130
15139
  filesInDir.push("/" + path__namespace["default"].relative(projectRoot, entry).split(path__namespace["default"].sep).join("/"));
15131
15140
  }
@@ -15139,14 +15148,14 @@ function checkGalleryFiles(input) {
15139
15148
  }
15140
15149
  async function handleCheckAllFiles(ctx) {
15141
15150
  const value = ctx.validationError.value;
15142
- if (!value || typeof value.directory !== "string") {
15151
+ if (!value || typeof value.dir !== "string") {
15143
15152
  return {
15144
15153
  success: false,
15145
15154
  errorMessage: `Unexpected value in check-all-files for ${ctx.sourcePath}`
15146
15155
  };
15147
15156
  }
15148
15157
  const {
15149
- directory
15158
+ dir
15150
15159
  } = value;
15151
15160
  const source = ctx.valModule.source;
15152
15161
  if (!source || typeof source !== "object" || Array.isArray(source)) {
@@ -15160,7 +15169,7 @@ async function handleCheckAllFiles(ctx) {
15160
15169
  untrackedFiles
15161
15170
  } = checkGalleryFiles({
15162
15171
  entryKeys: Object.keys(source),
15163
- directory,
15172
+ dir,
15164
15173
  projectRoot: ctx.projectRoot,
15165
15174
  fs: ctx.fs
15166
15175
  });
@@ -15255,9 +15264,10 @@ async function handleExternalUpload() {
15255
15264
  };
15256
15265
  }
15257
15266
 
15258
- // Fix handler registry. `keyof:check-keys` and `router:check-route` are
15259
- // resolved upfront by the shared resolveSchemaSourceFixes — they never reach
15260
- // 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.
15261
15271
  const currentFixHandlers = {
15262
15272
  "image:check-metadata": handleFileMetadata,
15263
15273
  "image:add-metadata": handleFileMetadata,
@@ -1748,6 +1748,7 @@ async function createService(projectRoot, host = {
1748
1748
  return new Service(projectRoot, sourceFileHandler, extracted);
1749
1749
  }
1750
1750
  class Service {
1751
+ projectRoot;
1751
1752
  constructor(projectRoot, sourceFileHandler, extracted) {
1752
1753
  this.sourceFileHandler = sourceFileHandler;
1753
1754
  this.extracted = extracted;
@@ -2387,17 +2388,18 @@ function findNestedJsonValuesModuleErrors(schemas) {
2387
2388
  // #region ValOps
2388
2389
  class ValOps {
2389
2390
  /** Sources from val modules, immutable (without patches or anything) */
2390
-
2391
+ sources;
2391
2392
  /** The sha256 / hash of all sources + all schemas + config */
2392
-
2393
+ baseSha;
2393
2394
  /** The sha256 / hash of all sources */
2394
-
2395
+ sourcesSha;
2395
2396
  /** The sha256 / hash of config */
2396
-
2397
+ configSha;
2397
2398
  /** Schema from val modules, immutable */
2398
-
2399
+ schemas;
2399
2400
  /** The sha256 / hash of schema + config - if this changes users needs to reload */
2400
-
2401
+ schemaSha;
2402
+ modulesErrors;
2401
2403
  /**
2402
2404
  * What the SHAs above are a fold over, so they can be recomputed.
2403
2405
  *
@@ -2405,7 +2407,7 @@ class ValOps {
2405
2407
  * without re-evaluating the modules is a save, and it has to be able to move
2406
2408
  * the SHAs with them.
2407
2409
  */
2408
-
2410
+ shaEntries;
2409
2411
  /**
2410
2412
  * The extraction's OWN module errors, which are what the fold was given.
2411
2413
  *
@@ -2413,7 +2415,7 @@ class ValOps {
2413
2415
  * `.jsonValues()` errors concatenated on, and those were never part of the
2414
2416
  * hash. Re-folding with the wrong list changes the base SHA for no reason.
2415
2417
  */
2416
-
2418
+ shaModuleErrors;
2417
2419
  /**
2418
2420
  * What a save has told us each `.jsonValues()` entry now holds.
2419
2421
  *
@@ -3173,7 +3175,8 @@ class ValOps {
3173
3175
  // (must include all modules, not just those being validated, since conflicts can come from any module)
3174
3176
  const galleryDirectoryToModules = new Map();
3175
3177
  // Build a schema/source snapshot so the shared resolver can cross-reference
3176
- // 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`.
3177
3180
  const snapshot = {
3178
3181
  schemas: {},
3179
3182
  sources: {}
@@ -3186,8 +3189,8 @@ class ValOps {
3186
3189
  if (sourceForModule !== undefined) {
3187
3190
  snapshot.sources[moduleFilePath] = sourceForModule;
3188
3191
  }
3189
- if (serialized.type === "record" && serialized.mediaType && serialized.directory) {
3190
- const dir = serialized.directory;
3192
+ if (serialized.type === "record" && serialized.mediaType && serialized.dir) {
3193
+ const dir = serialized.dir;
3191
3194
  const existing = galleryDirectoryToModules.get(dir);
3192
3195
  if (existing) {
3193
3196
  existing.push(moduleFilePath);
@@ -3253,7 +3256,7 @@ class ValOps {
3253
3256
  };
3254
3257
  if (validationErrors) {
3255
3258
  for (const validationError of validationErrors) {
3256
- 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;
3257
3260
  if (isOnlyFileCheckValidationError(validationError)) {
3258
3261
  if (files[sourcePath]) {
3259
3262
  addError({
@@ -3267,13 +3270,13 @@ class ValOps {
3267
3270
  }
3268
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")) {
3269
3272
  remoteFiles[sourcePath] = validationError.value;
3270
- } 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)) {
3271
3274
  const resolved = internal.resolveSchemaSourceFixForError(validationError, snapshot);
3272
3275
  if (resolved && resolved.status === "remaining") {
3273
3276
  addError(resolved.error);
3274
3277
  }
3275
3278
  // resolved.status === "resolved" → drop silently
3276
- } 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")) {
3277
3280
  const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
3278
3281
  if (!validationError.value || typeof validationError.value !== "object") {
3279
3282
  addError({
@@ -3281,10 +3284,10 @@ class ValOps {
3281
3284
  typeError: true
3282
3285
  });
3283
3286
  } else {
3284
- const directory = "directory" in validationError.value && validationError.value.directory;
3287
+ const directory = "dir" in validationError.value && validationError.value.dir;
3285
3288
  if (typeof directory !== "string") {
3286
3289
  addError({
3287
- message: `Expected gallery validation error 'value' to have property 'directory' of type 'string'. Found: ${typeof directory}. ${TYPE_ERROR_MESSAGE}`,
3290
+ message: `Expected gallery validation error 'value' to have property 'dir' of type 'string'. Found: ${typeof directory}. ${TYPE_ERROR_MESSAGE}`,
3288
3291
  typeError: true
3289
3292
  });
3290
3293
  } else {
@@ -3298,7 +3301,7 @@ class ValOps {
3298
3301
  // If conflictingModules is empty, directory is unique — silently drop the error.
3299
3302
  }
3300
3303
  }
3301
- } 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 {
3302
3305
  addError(validationError);
3303
3306
  }
3304
3307
  }
@@ -4158,8 +4161,8 @@ class ValOps {
4158
4161
  // #endregion history
4159
4162
  }
4160
4163
  function isOnlyFileCheckValidationError(validationError) {
4161
- var _validationError$fixe9;
4162
- 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")) {
4163
4166
  return true;
4164
4167
  }
4165
4168
  return false;
@@ -5680,6 +5683,7 @@ function serializeSchemaSafely(schema) {
5680
5683
  }
5681
5684
  class ValOpsFS extends ValOps {
5682
5685
  static VAL_DIR = ".val";
5686
+ host;
5683
5687
  constructor(contentUrl, rootDir, valModules, options) {
5684
5688
  super(valModules, options);
5685
5689
  this.contentUrl = contentUrl;
@@ -5692,6 +5696,7 @@ class ValOpsFS extends ValOps {
5692
5696
  * Change detection for `.jsonValues()` entry files, which no sha can see (their
5693
5697
  * content lives behind a thunk that `JSON.stringify` drops).
5694
5698
  */
5699
+ jsonEntryFilesFingerprint;
5695
5700
 
5696
5701
  /**
5697
5702
  * Unpublished changes repair threw away, waiting to be told to somebody.
@@ -7221,6 +7226,8 @@ const NonceResponse = z.z.object({
7221
7226
  */
7222
7227
  const PATCH_GROUPS_CACHE_MS = 1000;
7223
7228
  class ValOpsHttp extends ValOps {
7229
+ authHeaders;
7230
+ root;
7224
7231
  constructor(contentUrl, project, commitSha,
7225
7232
  // TODO: CommitSha
7226
7233
  branch,
@@ -13576,6 +13583,8 @@ const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
13576
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
13577
13584
 
13578
13585
  class ValModuleLoader {
13586
+ cache;
13587
+ cacheSize;
13579
13588
  constructor(projectRoot, compilerOptions,
13580
13589
  // TODO: remove this?
13581
13590
  sourceFileHandler, host = {
@@ -14955,7 +14964,7 @@ async function handleRemoteFileUpload(ctx) {
14955
14964
  return uploadRemoteFileCore(ctx, fileRefProp, fileSourceMetadata, resolveRemoteFileSchema);
14956
14965
  }
14957
14966
 
14958
- // Gallery (s.images({ ... }).remote() / s.files({ ... }).remote()) upload.
14967
+ // Gallery (s.imageset({ ... }).remote() / s.fileset({ ... }).remote()) upload.
14959
14968
  // Unlike a single image/file field, a gallery entry is keyed by its local file
14960
14969
  // path and the value is bare metadata (no FileSource), so we derive the file
14961
14970
  // ref from the key and synthesize the image/file schema from the record's
@@ -14996,10 +15005,10 @@ async function handleRemoteGalleryFileUpload(ctx) {
14996
15005
  // synthesize the serialized image/file schema (matching how single fields
14997
15006
  // serialize) for the remote ref's validation hash.
14998
15007
  //
14999
- // `accept`/`directory` come from the RECORD that holds the entry, resolved
15008
+ // `accept`/`dir` come from the RECORD that holds the entry, resolved
15000
15009
  // from the entry's PARENT path - not from the module root, which is only the
15001
15010
  // record when the gallery is the whole module. A nested gallery
15002
- // (s.object({ gallery: s.images(...) })) would otherwise synthesize a schema
15011
+ // (s.object({ gallery: s.imageset(...) })) would otherwise synthesize a schema
15003
15012
  // with no options and bake a validation hash into the remote ref that can
15004
15013
  // never validate, so a mismatch fails fast instead of uploading.
15005
15014
  const [, parentModulePath] = core.Internal.splitModuleFilePathAndModulePath(core.Internal.parentOfSourcePath(ctx.sourcePath));
@@ -15012,7 +15021,7 @@ async function handleRemoteGalleryFileUpload(ctx) {
15012
15021
  }
15013
15022
  const {
15014
15023
  accept,
15015
- directory
15024
+ dir
15016
15025
  } = recordSchema;
15017
15026
  const schema = mediaType === "image" ? {
15018
15027
  type: "image",
@@ -15021,8 +15030,8 @@ async function handleRemoteGalleryFileUpload(ctx) {
15021
15030
  ...(accept ? {
15022
15031
  accept
15023
15032
  } : {}),
15024
- ...(directory ? {
15025
- directory
15033
+ ...(dir ? {
15034
+ dir
15026
15035
  } : {})
15027
15036
  }
15028
15037
  } : {
@@ -15062,14 +15071,14 @@ async function handleRemoteFileCheck() {
15062
15071
  }
15063
15072
  async function handleUniqueFolderCheck(ctx) {
15064
15073
  const value = ctx.validationError.value;
15065
- if (!value || typeof value.directory !== "string") {
15074
+ if (!value || typeof value.dir !== "string") {
15066
15075
  return {
15067
15076
  success: false,
15068
15077
  errorMessage: `Unexpected value in unique folder check for ${ctx.sourcePath}`
15069
15078
  };
15070
15079
  }
15071
15080
  const {
15072
- directory
15081
+ dir
15073
15082
  } = value;
15074
15083
  const conflicts = [];
15075
15084
  for (const file of ctx.valFiles) {
@@ -15079,14 +15088,14 @@ async function handleUniqueFolderCheck(ctx) {
15079
15088
  validate: false
15080
15089
  });
15081
15090
  const schema = otherModule.schema;
15082
- if ((schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.directory === directory && schema.mediaType) {
15091
+ if ((schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.dir === dir && schema.mediaType) {
15083
15092
  conflicts.push(otherModuleFilePath);
15084
15093
  }
15085
15094
  }
15086
15095
  if (conflicts.length > 0) {
15087
15096
  return {
15088
15097
  success: false,
15089
- errorMessage: `Gallery directory '${directory}' in ${ctx.moduleFilePath} is also used by: ${conflicts.join(", ")}. Each gallery must use a unique directory.`
15098
+ errorMessage: `Gallery directory '${dir}' in ${ctx.moduleFilePath} is also used by: ${conflicts.join(", ")}. Each gallery must use a unique directory.`
15090
15099
  };
15091
15100
  }
15092
15101
  return {
@@ -15116,7 +15125,7 @@ async function handleUniqueFolderCheck(ctx) {
15116
15125
  */
15117
15126
  function checkGalleryFiles(input) {
15118
15127
  const {
15119
- directory,
15128
+ dir,
15120
15129
  projectRoot,
15121
15130
  fs
15122
15131
  } = input;
@@ -15125,7 +15134,7 @@ function checkGalleryFiles(input) {
15125
15134
  const missingTrackedFiles = entries.filter(entry => !entry.remote && !fs.fileExists(path__namespace["default"].join(projectRoot, entry.localPath))).map(entry => entry.localPath);
15126
15135
  const filesInDir = [];
15127
15136
  try {
15128
- const found = fs.readDirectory(path__namespace["default"].join(projectRoot, directory), undefined, undefined, ["**/*"]);
15137
+ const found = fs.readDirectory(path__namespace["default"].join(projectRoot, dir), undefined, undefined, ["**/*"]);
15129
15138
  for (const entry of found) {
15130
15139
  filesInDir.push("/" + path__namespace["default"].relative(projectRoot, entry).split(path__namespace["default"].sep).join("/"));
15131
15140
  }
@@ -15139,14 +15148,14 @@ function checkGalleryFiles(input) {
15139
15148
  }
15140
15149
  async function handleCheckAllFiles(ctx) {
15141
15150
  const value = ctx.validationError.value;
15142
- if (!value || typeof value.directory !== "string") {
15151
+ if (!value || typeof value.dir !== "string") {
15143
15152
  return {
15144
15153
  success: false,
15145
15154
  errorMessage: `Unexpected value in check-all-files for ${ctx.sourcePath}`
15146
15155
  };
15147
15156
  }
15148
15157
  const {
15149
- directory
15158
+ dir
15150
15159
  } = value;
15151
15160
  const source = ctx.valModule.source;
15152
15161
  if (!source || typeof source !== "object" || Array.isArray(source)) {
@@ -15160,7 +15169,7 @@ async function handleCheckAllFiles(ctx) {
15160
15169
  untrackedFiles
15161
15170
  } = checkGalleryFiles({
15162
15171
  entryKeys: Object.keys(source),
15163
- directory,
15172
+ dir,
15164
15173
  projectRoot: ctx.projectRoot,
15165
15174
  fs: ctx.fs
15166
15175
  });
@@ -15255,9 +15264,10 @@ async function handleExternalUpload() {
15255
15264
  };
15256
15265
  }
15257
15266
 
15258
- // Fix handler registry. `keyof:check-keys` and `router:check-route` are
15259
- // resolved upfront by the shared resolveSchemaSourceFixes — they never reach
15260
- // 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.
15261
15271
  const currentFixHandlers = {
15262
15272
  "image:check-metadata": handleFileMetadata,
15263
15273
  "image:add-metadata": handleFileMetadata,
@@ -8,7 +8,7 @@ import path__default from 'path';
8
8
  import fs, { promises } from 'fs';
9
9
  import vm from 'node:vm';
10
10
  import { Module } 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';
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';
@@ -1714,6 +1714,7 @@ async function createService(projectRoot, host = {
1714
1714
  return new Service(projectRoot, sourceFileHandler, extracted);
1715
1715
  }
1716
1716
  class Service {
1717
+ projectRoot;
1717
1718
  constructor(projectRoot, sourceFileHandler, extracted) {
1718
1719
  this.sourceFileHandler = sourceFileHandler;
1719
1720
  this.extracted = extracted;
@@ -2353,17 +2354,18 @@ function findNestedJsonValuesModuleErrors(schemas) {
2353
2354
  // #region ValOps
2354
2355
  class ValOps {
2355
2356
  /** Sources from val modules, immutable (without patches or anything) */
2356
-
2357
+ sources;
2357
2358
  /** The sha256 / hash of all sources + all schemas + config */
2358
-
2359
+ baseSha;
2359
2360
  /** The sha256 / hash of all sources */
2360
-
2361
+ sourcesSha;
2361
2362
  /** The sha256 / hash of config */
2362
-
2363
+ configSha;
2363
2364
  /** Schema from val modules, immutable */
2364
-
2365
+ schemas;
2365
2366
  /** The sha256 / hash of schema + config - if this changes users needs to reload */
2366
-
2367
+ schemaSha;
2368
+ modulesErrors;
2367
2369
  /**
2368
2370
  * What the SHAs above are a fold over, so they can be recomputed.
2369
2371
  *
@@ -2371,7 +2373,7 @@ class ValOps {
2371
2373
  * without re-evaluating the modules is a save, and it has to be able to move
2372
2374
  * the SHAs with them.
2373
2375
  */
2374
-
2376
+ shaEntries;
2375
2377
  /**
2376
2378
  * The extraction's OWN module errors, which are what the fold was given.
2377
2379
  *
@@ -2379,7 +2381,7 @@ class ValOps {
2379
2381
  * `.jsonValues()` errors concatenated on, and those were never part of the
2380
2382
  * hash. Re-folding with the wrong list changes the base SHA for no reason.
2381
2383
  */
2382
-
2384
+ shaModuleErrors;
2383
2385
  /**
2384
2386
  * What a save has told us each `.jsonValues()` entry now holds.
2385
2387
  *
@@ -3139,7 +3141,8 @@ class ValOps {
3139
3141
  // (must include all modules, not just those being validated, since conflicts can come from any module)
3140
3142
  const galleryDirectoryToModules = new Map();
3141
3143
  // Build a schema/source snapshot so the shared resolver can cross-reference
3142
- // 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`.
3143
3146
  const snapshot = {
3144
3147
  schemas: {},
3145
3148
  sources: {}
@@ -3152,8 +3155,8 @@ class ValOps {
3152
3155
  if (sourceForModule !== undefined) {
3153
3156
  snapshot.sources[moduleFilePath] = sourceForModule;
3154
3157
  }
3155
- if (serialized.type === "record" && serialized.mediaType && serialized.directory) {
3156
- const dir = serialized.directory;
3158
+ if (serialized.type === "record" && serialized.mediaType && serialized.dir) {
3159
+ const dir = serialized.dir;
3157
3160
  const existing = galleryDirectoryToModules.get(dir);
3158
3161
  if (existing) {
3159
3162
  existing.push(moduleFilePath);
@@ -3219,7 +3222,7 @@ class ValOps {
3219
3222
  };
3220
3223
  if (validationErrors) {
3221
3224
  for (const validationError of validationErrors) {
3222
- 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;
3223
3226
  if (isOnlyFileCheckValidationError(validationError)) {
3224
3227
  if (files[sourcePath]) {
3225
3228
  addError({
@@ -3233,13 +3236,13 @@ class ValOps {
3233
3236
  }
3234
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")) {
3235
3238
  remoteFiles[sourcePath] = validationError.value;
3236
- } 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)) {
3237
3240
  const resolved = resolveSchemaSourceFixForError(validationError, snapshot);
3238
3241
  if (resolved && resolved.status === "remaining") {
3239
3242
  addError(resolved.error);
3240
3243
  }
3241
3244
  // resolved.status === "resolved" → drop silently
3242
- } 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")) {
3243
3246
  const TYPE_ERROR_MESSAGE = `This is most likely a Val version mismatch or Val bug.`;
3244
3247
  if (!validationError.value || typeof validationError.value !== "object") {
3245
3248
  addError({
@@ -3247,10 +3250,10 @@ class ValOps {
3247
3250
  typeError: true
3248
3251
  });
3249
3252
  } else {
3250
- const directory = "directory" in validationError.value && validationError.value.directory;
3253
+ const directory = "dir" in validationError.value && validationError.value.dir;
3251
3254
  if (typeof directory !== "string") {
3252
3255
  addError({
3253
- message: `Expected gallery validation error 'value' to have property 'directory' of type 'string'. Found: ${typeof directory}. ${TYPE_ERROR_MESSAGE}`,
3256
+ message: `Expected gallery validation error 'value' to have property 'dir' of type 'string'. Found: ${typeof directory}. ${TYPE_ERROR_MESSAGE}`,
3254
3257
  typeError: true
3255
3258
  });
3256
3259
  } else {
@@ -3264,7 +3267,7 @@ class ValOps {
3264
3267
  // If conflictingModules is empty, directory is unique — silently drop the error.
3265
3268
  }
3266
3269
  }
3267
- } 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 {
3268
3271
  addError(validationError);
3269
3272
  }
3270
3273
  }
@@ -4124,8 +4127,8 @@ class ValOps {
4124
4127
  // #endregion history
4125
4128
  }
4126
4129
  function isOnlyFileCheckValidationError(validationError) {
4127
- var _validationError$fixe9;
4128
- 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")) {
4129
4132
  return true;
4130
4133
  }
4131
4134
  return false;
@@ -5646,6 +5649,7 @@ function serializeSchemaSafely(schema) {
5646
5649
  }
5647
5650
  class ValOpsFS extends ValOps {
5648
5651
  static VAL_DIR = ".val";
5652
+ host;
5649
5653
  constructor(contentUrl, rootDir, valModules, options) {
5650
5654
  super(valModules, options);
5651
5655
  this.contentUrl = contentUrl;
@@ -5658,6 +5662,7 @@ class ValOpsFS extends ValOps {
5658
5662
  * Change detection for `.jsonValues()` entry files, which no sha can see (their
5659
5663
  * content lives behind a thunk that `JSON.stringify` drops).
5660
5664
  */
5665
+ jsonEntryFilesFingerprint;
5661
5666
 
5662
5667
  /**
5663
5668
  * Unpublished changes repair threw away, waiting to be told to somebody.
@@ -7187,6 +7192,8 @@ const NonceResponse = z.object({
7187
7192
  */
7188
7193
  const PATCH_GROUPS_CACHE_MS = 1000;
7189
7194
  class ValOpsHttp extends ValOps {
7195
+ authHeaders;
7196
+ root;
7190
7197
  constructor(contentUrl, project, commitSha,
7191
7198
  // TODO: CommitSha
7192
7199
  branch,
@@ -13542,6 +13549,8 @@ const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
13542
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
13543
13550
 
13544
13551
  class ValModuleLoader {
13552
+ cache;
13553
+ cacheSize;
13545
13554
  constructor(projectRoot, compilerOptions,
13546
13555
  // TODO: remove this?
13547
13556
  sourceFileHandler, host = {
@@ -14921,7 +14930,7 @@ async function handleRemoteFileUpload(ctx) {
14921
14930
  return uploadRemoteFileCore(ctx, fileRefProp, fileSourceMetadata, resolveRemoteFileSchema);
14922
14931
  }
14923
14932
 
14924
- // Gallery (s.images({ ... }).remote() / s.files({ ... }).remote()) upload.
14933
+ // Gallery (s.imageset({ ... }).remote() / s.fileset({ ... }).remote()) upload.
14925
14934
  // Unlike a single image/file field, a gallery entry is keyed by its local file
14926
14935
  // path and the value is bare metadata (no FileSource), so we derive the file
14927
14936
  // ref from the key and synthesize the image/file schema from the record's
@@ -14962,10 +14971,10 @@ async function handleRemoteGalleryFileUpload(ctx) {
14962
14971
  // synthesize the serialized image/file schema (matching how single fields
14963
14972
  // serialize) for the remote ref's validation hash.
14964
14973
  //
14965
- // `accept`/`directory` come from the RECORD that holds the entry, resolved
14974
+ // `accept`/`dir` come from the RECORD that holds the entry, resolved
14966
14975
  // from the entry's PARENT path - not from the module root, which is only the
14967
14976
  // record when the gallery is the whole module. A nested gallery
14968
- // (s.object({ gallery: s.images(...) })) would otherwise synthesize a schema
14977
+ // (s.object({ gallery: s.imageset(...) })) would otherwise synthesize a schema
14969
14978
  // with no options and bake a validation hash into the remote ref that can
14970
14979
  // never validate, so a mismatch fails fast instead of uploading.
14971
14980
  const [, parentModulePath] = Internal.splitModuleFilePathAndModulePath(Internal.parentOfSourcePath(ctx.sourcePath));
@@ -14978,7 +14987,7 @@ async function handleRemoteGalleryFileUpload(ctx) {
14978
14987
  }
14979
14988
  const {
14980
14989
  accept,
14981
- directory
14990
+ dir
14982
14991
  } = recordSchema;
14983
14992
  const schema = mediaType === "image" ? {
14984
14993
  type: "image",
@@ -14987,8 +14996,8 @@ async function handleRemoteGalleryFileUpload(ctx) {
14987
14996
  ...(accept ? {
14988
14997
  accept
14989
14998
  } : {}),
14990
- ...(directory ? {
14991
- directory
14999
+ ...(dir ? {
15000
+ dir
14992
15001
  } : {})
14993
15002
  }
14994
15003
  } : {
@@ -15028,14 +15037,14 @@ async function handleRemoteFileCheck() {
15028
15037
  }
15029
15038
  async function handleUniqueFolderCheck(ctx) {
15030
15039
  const value = ctx.validationError.value;
15031
- if (!value || typeof value.directory !== "string") {
15040
+ if (!value || typeof value.dir !== "string") {
15032
15041
  return {
15033
15042
  success: false,
15034
15043
  errorMessage: `Unexpected value in unique folder check for ${ctx.sourcePath}`
15035
15044
  };
15036
15045
  }
15037
15046
  const {
15038
- directory
15047
+ dir
15039
15048
  } = value;
15040
15049
  const conflicts = [];
15041
15050
  for (const file of ctx.valFiles) {
@@ -15045,14 +15054,14 @@ async function handleUniqueFolderCheck(ctx) {
15045
15054
  validate: false
15046
15055
  });
15047
15056
  const schema = otherModule.schema;
15048
- if ((schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.directory === directory && schema.mediaType) {
15057
+ if ((schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.dir === dir && schema.mediaType) {
15049
15058
  conflicts.push(otherModuleFilePath);
15050
15059
  }
15051
15060
  }
15052
15061
  if (conflicts.length > 0) {
15053
15062
  return {
15054
15063
  success: false,
15055
- errorMessage: `Gallery directory '${directory}' in ${ctx.moduleFilePath} is also used by: ${conflicts.join(", ")}. Each gallery must use a unique directory.`
15064
+ errorMessage: `Gallery directory '${dir}' in ${ctx.moduleFilePath} is also used by: ${conflicts.join(", ")}. Each gallery must use a unique directory.`
15056
15065
  };
15057
15066
  }
15058
15067
  return {
@@ -15082,7 +15091,7 @@ async function handleUniqueFolderCheck(ctx) {
15082
15091
  */
15083
15092
  function checkGalleryFiles(input) {
15084
15093
  const {
15085
- directory,
15094
+ dir,
15086
15095
  projectRoot,
15087
15096
  fs
15088
15097
  } = input;
@@ -15091,7 +15100,7 @@ function checkGalleryFiles(input) {
15091
15100
  const missingTrackedFiles = entries.filter(entry => !entry.remote && !fs.fileExists(path__default.join(projectRoot, entry.localPath))).map(entry => entry.localPath);
15092
15101
  const filesInDir = [];
15093
15102
  try {
15094
- const found = fs.readDirectory(path__default.join(projectRoot, directory), undefined, undefined, ["**/*"]);
15103
+ const found = fs.readDirectory(path__default.join(projectRoot, dir), undefined, undefined, ["**/*"]);
15095
15104
  for (const entry of found) {
15096
15105
  filesInDir.push("/" + path__default.relative(projectRoot, entry).split(path__default.sep).join("/"));
15097
15106
  }
@@ -15105,14 +15114,14 @@ function checkGalleryFiles(input) {
15105
15114
  }
15106
15115
  async function handleCheckAllFiles(ctx) {
15107
15116
  const value = ctx.validationError.value;
15108
- if (!value || typeof value.directory !== "string") {
15117
+ if (!value || typeof value.dir !== "string") {
15109
15118
  return {
15110
15119
  success: false,
15111
15120
  errorMessage: `Unexpected value in check-all-files for ${ctx.sourcePath}`
15112
15121
  };
15113
15122
  }
15114
15123
  const {
15115
- directory
15124
+ dir
15116
15125
  } = value;
15117
15126
  const source = ctx.valModule.source;
15118
15127
  if (!source || typeof source !== "object" || Array.isArray(source)) {
@@ -15126,7 +15135,7 @@ async function handleCheckAllFiles(ctx) {
15126
15135
  untrackedFiles
15127
15136
  } = checkGalleryFiles({
15128
15137
  entryKeys: Object.keys(source),
15129
- directory,
15138
+ dir,
15130
15139
  projectRoot: ctx.projectRoot,
15131
15140
  fs: ctx.fs
15132
15141
  });
@@ -15221,9 +15230,10 @@ async function handleExternalUpload() {
15221
15230
  };
15222
15231
  }
15223
15232
 
15224
- // Fix handler registry. `keyof:check-keys` and `router:check-route` are
15225
- // resolved upfront by the shared resolveSchemaSourceFixes — they never reach
15226
- // 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.
15227
15237
  const currentFixHandlers = {
15228
15238
  "image:check-metadata": handleFileMetadata,
15229
15239
  "image:add-metadata": handleFileMetadata,
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.126.0",
19
+ "version": "0.128.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.126.0",
33
- "@valbuild/shared": "0.126.0",
34
- "@valbuild/ui": "0.126.0"
32
+ "@valbuild/ui": "0.127.0",
33
+ "@valbuild/shared": "0.128.0",
34
+ "@valbuild/core": "0.128.0"
35
35
  },
36
36
  "engines": {
37
37
  "node": "^20.19.0 || >=22"