@valbuild/server 0.150.0 → 0.150.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.150.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`c674aae`](https://github.com/valbuild/val/commit/c674aae361be3c75b5232acb41ea6d8da973c678), [`c044e55`](https://github.com/valbuild/val/commit/c044e55452ba34ec5b7b38c935c9c25491d980be), [`36412b2`](https://github.com/valbuild/val/commit/36412b28bb009083f652b26462b76a74c80ff595), [`155a079`](https://github.com/valbuild/val/commit/155a0792c2a437a1cbcdd6342131999be70e0d70), [`8f598b6`](https://github.com/valbuild/val/commit/8f598b618b794e59ef5b9c6ef94650be4ff006e7), [`95d17e6`](https://github.com/valbuild/val/commit/95d17e6bbecceb2f09d6a6226808e9c60b2d4795), [`520d22c`](https://github.com/valbuild/val/commit/520d22cc721ff23b83e4bae0b4ba7f52a6a0fa0c)]:
8
+ - @valbuild/ui@0.150.2
9
+ - @valbuild/shared@0.150.2
10
+ - @valbuild/core@0.150.2
11
+
12
+ ## 0.150.1
13
+
14
+ ### Patch Changes
15
+
16
+ - [#894](https://github.com/valbuild/val/pull/894) [`9171baa`](https://github.com/valbuild/val/commit/9171baad7beb1b2a355c70724d2339467b126e02) Thanks [@freekh](https://github.com/freekh)! - `val validate` no longer fails a project just because some of its files are
17
+ local while the project stores files remotely (`files: { remote: true }` or
18
+ `.remote()`). Every project made from a template starts like that, and the
19
+ site serves those files from `public/` until they are uploaded. They are now
20
+ reported as **warnings**, grouped into one block per file with the command
21
+ that uploads them:
22
+
23
+ ```
24
+ content/images.val.ts ⚠ 7 warnings (12ms)
25
+ │
26
+ │ ⚠ Local files in a project with remote files. Your site serves them from public/ for now; they move to remote storage when uploaded.
27
+ │
28
+ │ content/images.val.ts:7:5 /public/val/images/image-0_00000.png
29
+ │ content/images.val.ts:8:5 /public/val/images/image-1_00001.png
30
+ │ content/images.val.ts:9:5 /public/val/images/image-2_00002.png
31
+ │ content/images.val.ts:10:5 /public/val/images/image-3_00003.png
32
+ │ content/images.val.ts:11:5 /public/val/images/image-4_00004.png
33
+ │ … 2 more
34
+ │
35
+ │ → val validate --fix uploads them (needs val login)
36
+ │
37
+
38
+ ✔ No validation errors · 7 warnings in 1 file
39
+ ```
40
+
41
+ - `--strict` on `val validate` and `val publish` makes warnings fail too.
42
+ - `val validate --fix` still fails when it cannot upload them, for example
43
+ when you are not logged in.
44
+ - A local file that does not exist is still an error.
45
+ - The language server shows these as warnings.
46
+ - In the Studio, **Check everything** on the Validate page lists them with a
47
+ **Fix** button that uploads them, so a hosted project, which
48
+ has no command line, can move its template's images and files to remote
49
+ storage. That covers images, files and videos (a video's poster, captions
50
+ and HLS stream go up with it), images inside rich text included, and the
51
+ fields that picked a gallery image or a video from a set are updated with
52
+ it.
53
+ - On a hosted project, **Check everything** no longer reports the template's
54
+ images and videos as "The file is gone", and no longer offers a Fix that
55
+ would remove them from their gallery or video set.
56
+
57
+ Fixable errors are now printed with `✘`, like every other error, instead of
58
+ the `⚠` warnings use. They were always errors and still make `val validate`
59
+ fail.
60
+
61
+ - Updated dependencies [[`9171baa`](https://github.com/valbuild/val/commit/9171baad7beb1b2a355c70724d2339467b126e02), [`fd9ee2a`](https://github.com/valbuild/val/commit/fd9ee2ad76ef45bc6c4801aaccaae6e183960f11)]:
62
+ - @valbuild/core@0.150.1
63
+ - @valbuild/shared@0.150.1
64
+ - @valbuild/ui@0.150.1
65
+
3
66
  ## 0.150.0
4
67
 
5
68
  ### Patch Changes
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@valbuild/core');
6
- var ValRouterCore = require('../../dist/ValRouterCore-fec2cbca.cjs.dev.js');
6
+ var ValRouterCore = require('../../dist/ValRouterCore-f9016ae0.cjs.dev.js');
7
7
  require('@valbuild/shared/internal');
8
8
  require('zod');
9
9
  require('zod-validation-error');
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@valbuild/core');
6
- var ValRouterCore = require('../../dist/ValRouterCore-dfb7b578.cjs.prod.js');
6
+ var ValRouterCore = require('../../dist/ValRouterCore-2e82ae18.cjs.prod.js');
7
7
  require('@valbuild/shared/internal');
8
8
  require('zod');
9
9
  require('zod-validation-error');
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_CONTENT_HOST } from '@valbuild/core';
2
- import { j as ValOpsMemory, E as bytesToBase64, y as ValServer, z as createValRequestHandler } from '../../dist/ValRouterCore-2579e663.esm.js';
2
+ import { j as ValOpsMemory, E as bytesToBase64, y as ValServer, z as createValRequestHandler } from '../../dist/ValRouterCore-b3c6a562.esm.js';
3
3
  import '@valbuild/shared/internal';
4
4
  import 'zod';
5
5
  import 'zod-validation-error';
@@ -4391,6 +4391,20 @@ class ValOps {
4391
4391
  return null;
4392
4392
  }
4393
4393
 
4394
+ /**
4395
+ * Whether {@link getBinaryFile} can read a PUBLISHED file at all, so that
4396
+ * `null` from it means the file is gone rather than out of reach.
4397
+ *
4398
+ * Not in http mode without a repository -- a hosted project. Its published
4399
+ * binaries are kept in the patch store, and a template's files were never in
4400
+ * a patch: they are in the built site's `public/`, which the content service
4401
+ * never had. A check that read `null` there as "gone" reported every image
4402
+ * and video a template ships with, and offered to remove them.
4403
+ */
4404
+ canReadPublishedFiles() {
4405
+ return true;
4406
+ }
4407
+
4394
4408
  /**
4395
4409
  * Does a press of Publish run as a publish job (valbuild/home,
4396
4410
  * docs/app-mode.md, "Publishing is a queued job")? Never for a project
@@ -9102,7 +9116,7 @@ const ValServer = (valModules, options, callbacks, platform) => {
9102
9116
  // TypeScript compiler. See `patch/ts/publishKit.ts`.
9103
9117
  const {
9104
9118
  getJsonEntryAtCommit
9105
- } = await Promise.resolve().then(function () { return require('./getJsonEntryAtCommit-6bbe1c9d.cjs.prod.js'); });
9119
+ } = await Promise.resolve().then(function () { return require('./getJsonEntryAtCommit-5552ac46.cjs.prod.js'); });
9106
9120
  const res = await getJsonEntryAtCommit(serverOps, req.query.commit_sha, req.query.path, req.query.key);
9107
9121
  if (fp.result.isErr(res)) {
9108
9122
  return historyErrorResponse(res.error);
@@ -10408,6 +10422,11 @@ class ValOpsMemory extends ValOps {
10408
10422
  metadata: file.metadata
10409
10423
  };
10410
10424
  }
10425
+
10426
+ /** See {@link ValOps.canReadPublishedFiles}: `getBinaryFile` never can. */
10427
+ canReadPublishedFiles() {
10428
+ return false;
10429
+ }
10411
10430
  async getBinaryFile(_filePathOrRef) {
10412
10431
  // Null rather than a throw: callers treat this as "no such file", and a
10413
10432
  // read of a local file in a remote-files project is a miss, not a fault.
@@ -4385,6 +4385,20 @@ class ValOps {
4385
4385
  return null;
4386
4386
  }
4387
4387
 
4388
+ /**
4389
+ * Whether {@link getBinaryFile} can read a PUBLISHED file at all, so that
4390
+ * `null` from it means the file is gone rather than out of reach.
4391
+ *
4392
+ * Not in http mode without a repository -- a hosted project. Its published
4393
+ * binaries are kept in the patch store, and a template's files were never in
4394
+ * a patch: they are in the built site's `public/`, which the content service
4395
+ * never had. A check that read `null` there as "gone" reported every image
4396
+ * and video a template ships with, and offered to remove them.
4397
+ */
4398
+ canReadPublishedFiles() {
4399
+ return true;
4400
+ }
4401
+
4388
4402
  /**
4389
4403
  * Does a press of Publish run as a publish job (valbuild/home,
4390
4404
  * docs/app-mode.md, "Publishing is a queued job")? Never for a project
@@ -9096,7 +9110,7 @@ const ValServer = (valModules, options, callbacks, platform) => {
9096
9110
  // TypeScript compiler. See `patch/ts/publishKit.ts`.
9097
9111
  const {
9098
9112
  getJsonEntryAtCommit
9099
- } = await import('./getJsonEntryAtCommit-66544899.esm.js');
9113
+ } = await import('./getJsonEntryAtCommit-ad6deabd.esm.js');
9100
9114
  const res = await getJsonEntryAtCommit(serverOps, req.query.commit_sha, req.query.path, req.query.key);
9101
9115
  if (result.isErr(res)) {
9102
9116
  return historyErrorResponse(res.error);
@@ -10402,6 +10416,11 @@ class ValOpsMemory extends ValOps {
10402
10416
  metadata: file.metadata
10403
10417
  };
10404
10418
  }
10419
+
10420
+ /** See {@link ValOps.canReadPublishedFiles}: `getBinaryFile` never can. */
10421
+ canReadPublishedFiles() {
10422
+ return false;
10423
+ }
10405
10424
  async getBinaryFile(_filePathOrRef) {
10406
10425
  // Null rather than a throw: callers treat this as "no such file", and a
10407
10426
  // read of a local file in a remote-files project is a miss, not a fault.
@@ -4391,6 +4391,20 @@ class ValOps {
4391
4391
  return null;
4392
4392
  }
4393
4393
 
4394
+ /**
4395
+ * Whether {@link getBinaryFile} can read a PUBLISHED file at all, so that
4396
+ * `null` from it means the file is gone rather than out of reach.
4397
+ *
4398
+ * Not in http mode without a repository -- a hosted project. Its published
4399
+ * binaries are kept in the patch store, and a template's files were never in
4400
+ * a patch: they are in the built site's `public/`, which the content service
4401
+ * never had. A check that read `null` there as "gone" reported every image
4402
+ * and video a template ships with, and offered to remove them.
4403
+ */
4404
+ canReadPublishedFiles() {
4405
+ return true;
4406
+ }
4407
+
4394
4408
  /**
4395
4409
  * Does a press of Publish run as a publish job (valbuild/home,
4396
4410
  * docs/app-mode.md, "Publishing is a queued job")? Never for a project
@@ -9102,7 +9116,7 @@ const ValServer = (valModules, options, callbacks, platform) => {
9102
9116
  // TypeScript compiler. See `patch/ts/publishKit.ts`.
9103
9117
  const {
9104
9118
  getJsonEntryAtCommit
9105
- } = await Promise.resolve().then(function () { return require('./getJsonEntryAtCommit-80bf8f02.cjs.dev.js'); });
9119
+ } = await Promise.resolve().then(function () { return require('./getJsonEntryAtCommit-b8469ec5.cjs.dev.js'); });
9106
9120
  const res = await getJsonEntryAtCommit(serverOps, req.query.commit_sha, req.query.path, req.query.key);
9107
9121
  if (fp.result.isErr(res)) {
9108
9122
  return historyErrorResponse(res.error);
@@ -10408,6 +10422,11 @@ class ValOpsMemory extends ValOps {
10408
10422
  metadata: file.metadata
10409
10423
  };
10410
10424
  }
10425
+
10426
+ /** See {@link ValOps.canReadPublishedFiles}: `getBinaryFile` never can. */
10427
+ canReadPublishedFiles() {
10428
+ return false;
10429
+ }
10411
10430
  async getBinaryFile(_filePathOrRef) {
10412
10431
  // Null rather than a throw: callers treat this as "no such file", and a
10413
10432
  // read of a local file in a remote-files project is a miss, not a fault.
@@ -483,6 +483,17 @@ export declare abstract class ValOps {
483
483
  * an older service.
484
484
  */
485
485
  sourceMode(): "managed" | "connected" | null;
486
+ /**
487
+ * Whether {@link getBinaryFile} can read a PUBLISHED file at all, so that
488
+ * `null` from it means the file is gone rather than out of reach.
489
+ *
490
+ * Not in http mode without a repository -- a hosted project. Its published
491
+ * binaries are kept in the patch store, and a template's files were never in
492
+ * a patch: they are in the built site's `public/`, which the content service
493
+ * never had. A check that read `null` there as "gone" reported every image
494
+ * and video a template ships with, and offered to remove them.
495
+ */
496
+ canReadPublishedFiles(): boolean;
486
497
  /**
487
498
  * Does a press of Publish run as a publish job (valbuild/home,
488
499
  * docs/app-mode.md, "Publishing is a queued job")? Never for a project
@@ -186,6 +186,8 @@ export declare class ValOpsHttp extends ValOps {
186
186
  * has just arrived.
187
187
  */
188
188
  sourceMode(): "managed" | "connected" | null;
189
+ /** See {@link ValOps.canReadPublishedFiles}: not without a repository. */
190
+ canReadPublishedFiles(): boolean;
189
191
  /** Remembered with {@link sourceMode}, from the same response. */
190
192
  projectBranch(): string | null;
191
193
  /**
@@ -296,6 +296,8 @@ export declare class ValOpsMemory extends ValOps {
296
296
  }>>;
297
297
  getBase64EncodedBinaryFileFromPatch(filePath: string, patchId: PatchId, _remote?: boolean): Promise<Uint8Array | null>;
298
298
  protected getBase64EncodedBinaryFileMetadataFromPatch<T extends "file" | "image">(filePath: string, type: T, patchId: PatchId, _remote?: boolean): Promise<OpsMetadata<T>>;
299
+ /** See {@link ValOps.canReadPublishedFiles}: `getBinaryFile` never can. */
300
+ canReadPublishedFiles(): boolean;
299
301
  getBinaryFile(_filePathOrRef: string): Promise<Uint8Array | null>;
300
302
  protected getBinaryFileMetadata<T extends "file" | "image">(_filePath: string, _type: T): Promise<OpsMetadata<T>>;
301
303
  listCommits(): Promise<result.Result<CommitPage, HistoryError>>;
@@ -101,6 +101,7 @@ export type FixHandlerResult = {
101
101
  shouldApplyPatch?: boolean;
102
102
  appliedFix?: boolean;
103
103
  fixableErrorMessage?: string;
104
+ warning?: ValidationWarning;
104
105
  /**
105
106
  * Patches to OTHER modules than the one the fix is about, for the caller to
106
107
  * apply alongside the fix's own patch (and only when it applies that one).
@@ -116,6 +117,11 @@ export type FixHandlerResult = {
116
117
  remoteFilesCounter?: number;
117
118
  events?: ValidationEvent[];
118
119
  };
120
+ export type ValidationWarning = {
121
+ message: string;
122
+ files: string[];
123
+ hint?: string;
124
+ };
119
125
  /** A patch, and the module it is for. */
120
126
  export type ModulePatch = {
121
127
  moduleFilePath: ModuleFilePath;
@@ -146,7 +152,18 @@ export type ValidationEvent = {
146
152
  keyError?: boolean;
147
153
  /** See `isRepairedOnSave`: the Studio would not hold a publish for it. */
148
154
  repairedOnSave?: boolean;
149
- } | {
155
+ }
156
+ /**
157
+ * Reported, and not counted as an error: `val validate` exits 0 on warnings
158
+ * alone unless `--strict`. So a module with warnings and no errors is still
159
+ * `file-valid`, which means "no errors"; the CLI shows it with its warnings
160
+ * rather than in the list of valid modules.
161
+ */
162
+ | ({
163
+ type: "validation-warning";
164
+ sourcePath: string;
165
+ keyError?: boolean;
166
+ } & ValidationWarning) | {
150
167
  type: "unknown-fix";
151
168
  sourcePath: string;
152
169
  fixes: string[];
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var ts = require('typescript');
4
- var ValRouterCore = require('./ValRouterCore-dfb7b578.cjs.prod.js');
4
+ var ValRouterCore = require('./ValRouterCore-2e82ae18.cjs.prod.js');
5
5
  var fp = require('@valbuild/core/fp');
6
6
  var jsonValuesModule = require('./jsonValuesModule-d1bb8d8f.cjs.prod.js');
7
7
  require('@valbuild/core');
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { F as bytesToUtf8, G as posixPath } from './ValRouterCore-2579e663.esm.js';
2
+ import { F as bytesToUtf8, G as posixPath } from './ValRouterCore-b3c6a562.esm.js';
3
3
  import { result } from '@valbuild/core/fp';
4
4
  import { a as analyzeValModule, c as analyzeJsonValuesEntries } from './jsonValuesModule-427d68c8.esm.js';
5
5
  import '@valbuild/core';
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var ts = require('typescript');
4
- var ValRouterCore = require('./ValRouterCore-fec2cbca.cjs.dev.js');
4
+ var ValRouterCore = require('./ValRouterCore-f9016ae0.cjs.dev.js');
5
5
  var fp = require('@valbuild/core/fp');
6
6
  var jsonValuesModule = require('./jsonValuesModule-8ae503a8.cjs.dev.js');
7
7
  require('@valbuild/core');
@@ -12,7 +12,7 @@ var path = require('path');
12
12
  var fs = require('fs');
13
13
  var vm = require('node:vm');
14
14
  var node_module = require('node:module');
15
- var ValRouterCore = require('./ValRouterCore-fec2cbca.cjs.dev.js');
15
+ var ValRouterCore = require('./ValRouterCore-f9016ae0.cjs.dev.js');
16
16
  var server = require('@valbuild/ui/server');
17
17
  var z = require('zod');
18
18
  var zodValidationError = require('zod-validation-error');
@@ -5587,6 +5587,11 @@ class ValOpsHttp extends ValRouterCore.ValOps {
5587
5587
  return ((_this$projectExpectat2 = this.projectExpectation) === null || _this$projectExpectat2 === void 0 ? void 0 : _this$projectExpectat2.sourceMode) ?? null;
5588
5588
  }
5589
5589
 
5590
+ /** See {@link ValOps.canReadPublishedFiles}: not without a repository. */
5591
+ canReadPublishedFiles() {
5592
+ return this.git !== null;
5593
+ }
5594
+
5590
5595
  /** Remembered with {@link sourceMode}, from the same response. */
5591
5596
  projectBranch() {
5592
5597
  var _this$projectExpectat3;
@@ -10546,6 +10551,23 @@ function diskFixFiles(projectRoot) {
10546
10551
 
10547
10552
  const textEncoder = new TextEncoder();
10548
10553
 
10554
+ /**
10555
+ * What an upload fix answers when `--fix` is off and the files it would upload
10556
+ * are all there: a warning, not an error (`WARNING_FIXES` in
10557
+ * `@valbuild/shared`). Asked only after the files were found, so a missing one
10558
+ * is still reported as the error it is.
10559
+ */
10560
+ function localFilesWarning(files) {
10561
+ return {
10562
+ success: true,
10563
+ warning: {
10564
+ message: internal.LOCAL_FILE_IN_REMOTE_PROJECT_WARNING,
10565
+ files,
10566
+ hint: internal.LOCAL_FILE_IN_REMOTE_PROJECT_HINT
10567
+ }
10568
+ };
10569
+ }
10570
+
10549
10571
  /**
10550
10572
  * Everything an upload to Val Remote needs before it has any bytes: the
10551
10573
  * developer's token, the project's public id and a bucket. Opened once per
@@ -11002,12 +11024,6 @@ function movablePathsOf(video, schema) {
11002
11024
  }) => !setBacked || at[0] !== "path");
11003
11025
  }
11004
11026
  async function handleVideoUploadRemote(ctx) {
11005
- if (!ctx.fix) {
11006
- return {
11007
- success: false,
11008
- errorMessage: `Video needs to be uploaded to Val Remote (use --fix to upload)`
11009
- };
11010
- }
11011
11027
  const resolved = resolveVideo(ctx);
11012
11028
  if (!resolved.success) {
11013
11029
  return resolved.result;
@@ -11046,6 +11062,11 @@ async function handleVideoUploadRemote(ctx) {
11046
11062
  errorMessage: `File ${missing} does not exist`
11047
11063
  };
11048
11064
  }
11065
+ if (!ctx.fix) {
11066
+ return localFilesWarning(toUpload.map(({
11067
+ path: ref
11068
+ }) => ref));
11069
+ }
11049
11070
  const opened = await openRemoteUploadSession(ctx);
11050
11071
  if (!opened.success) {
11051
11072
  return opened.result;
@@ -11816,13 +11837,6 @@ async function handleVideosetCheckRemote(ctx) {
11816
11837
  * stays, and the entry is written back with their refs.
11817
11838
  */
11818
11839
  async function handleVideosetUploadRemote(ctx) {
11819
- if (!ctx.fix) {
11820
- return {
11821
- success: false,
11822
- // No sourcePath in the message: the reported location already points at it.
11823
- errorMessage: `Video needs to be uploaded to Val Remote (use --fix to upload)`
11824
- };
11825
- }
11826
11840
  const key = ctx.validationError.value;
11827
11841
  if (typeof key !== "string") {
11828
11842
  return {
@@ -11869,6 +11883,9 @@ async function handleVideosetUploadRemote(ctx) {
11869
11883
  errorMessage: `File ${missing} does not exist`
11870
11884
  };
11871
11885
  }
11886
+ if (!ctx.fix) {
11887
+ return localFilesWarning([...(keyIsLocal ? [key] : []), ...entryFiles.map(file => file.path)]);
11888
+ }
11872
11889
  const opened = await openRemoteUploadSession(ctx);
11873
11890
  if (!opened.success) {
11874
11891
  return opened.result;
@@ -13047,7 +13064,12 @@ async function checkModuleForStudio(ops, input) {
13047
13064
  // not judge without the bytes — which is every image that has metadata at
13048
13065
  // all. Reading them is what turns "has metadata" into "has the RIGHT
13049
13066
  // metadata".
13050
- const fileErrors = await ops.validateFiles(schemas, sources, validation.files, input.fileLastUpdatedByPatchId);
13067
+ // A published file this server cannot read is not judged at all: in a
13068
+ // hosted project a template's files are in the built site, not the content
13069
+ // service (`ValOps.canReadPublishedFiles`), and reading them reported every
13070
+ // one as unreadable.
13071
+ const readableFiles = ops.canReadPublishedFiles() ? validation.files : Object.fromEntries(Object.entries(validation.files).filter(([, file]) => input.fileLastUpdatedByPatchId[file.path] !== undefined));
13072
+ const fileErrors = await ops.validateFiles(schemas, sources, readableFiles, input.fileLastUpdatedByPatchId);
13051
13073
  for (const [sourcePath, found] of Object.entries(fileErrors)) {
13052
13074
  add(sourcePath, found);
13053
13075
  }
@@ -13105,6 +13127,10 @@ function isEntries(source) {
13105
13127
  * is never compared with the file (`videosetCheckAllFilesPatch` says why).
13106
13128
  * - **A remote entry** is asked of its host, as any remote file is, but only
13107
13129
  * whether the host has it (`existenceOnly`).
13130
+ * - **A local entry this server cannot read** is skipped: in a hosted project
13131
+ * a template's files are in the built site, not the content service
13132
+ * (`ValOps.canReadPublishedFiles`), and "gone" would be a lie whose fix
13133
+ * deletes the entry. Its `*:upload-remote` row is what moves it.
13108
13134
  */
13109
13135
  async function checkMediaDirEntries(ops, input, onlyKey) {
13110
13136
  const errors = {};
@@ -13131,9 +13157,15 @@ async function checkMediaDirEntries(ops, input, onlyKey) {
13131
13157
  };
13132
13158
  continue;
13133
13159
  }
13160
+ const fromPatch = input.fileLastUpdatedByPatchId[entry.localPath] ?? null;
13161
+ if (fromPatch === null && !ops.canReadPublishedFiles()) {
13162
+ // Nowhere to read it from -- a hosted project's template files are in
13163
+ // the built site, not the content service -- so nothing to say about
13164
+ // it. Never "gone": that row's fix removes the entry.
13165
+ continue;
13166
+ }
13134
13167
  if (type === "videos") {
13135
13168
  // A video: there or not, and nothing else. See above.
13136
- const fromPatch = input.fileLastUpdatedByPatchId[entry.localPath] ?? null;
13137
13169
  const reader = await ops.openBinaryFile(entry.localPath, fromPatch);
13138
13170
  if (reader === null) {
13139
13171
  errors[entryPath] = [{
@@ -14034,6 +14066,9 @@ async function uploadRemoteFileCore(ctx, fileRef, metadata, schema) {
14034
14066
  }]
14035
14067
  };
14036
14068
  }
14069
+ if (!ctx.fix) {
14070
+ return localFilesWarning([fileRef]);
14071
+ }
14037
14072
  const opened = await openRemoteUploadSession(ctx);
14038
14073
  if (!opened.success) {
14039
14074
  return opened.result;
@@ -14080,13 +14115,6 @@ async function uploadRemoteFileCore(ctx, fileRef, metadata, schema) {
14080
14115
  }
14081
14116
  async function handleRemoteFileUpload(ctx) {
14082
14117
  var _resolvedRemoteFileAt;
14083
- if (!ctx.fix) {
14084
- return {
14085
- success: false,
14086
- // No sourcePath in the message: the reported location already points at it.
14087
- errorMessage: `Remote file needs to be uploaded (use --fix to upload)`
14088
- };
14089
- }
14090
14118
  const [, modulePath] = core.Internal.splitModuleFilePathAndModulePath(ctx.sourcePath);
14091
14119
  if (!ctx.valModule.source || !ctx.valModule.schema) {
14092
14120
  return {
@@ -14104,7 +14132,11 @@ async function handleRemoteFileUpload(ctx) {
14104
14132
  errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`
14105
14133
  };
14106
14134
  }
14107
- const resolveRemoteFileSchema = resolvedRemoteFileAtSourcePath.schema;
14135
+
14136
+ // A rich text image's `src` resolves to the RICH TEXT schema when the schema
14137
+ // is serialized, as it is here: the image schema is its `img` option.
14138
+ const resolvedSchema = resolvedRemoteFileAtSourcePath.schema;
14139
+ const resolveRemoteFileSchema = (resolvedSchema === null || resolvedSchema === void 0 ? void 0 : resolvedSchema.type) === "richtext" ? internal.richTextImageSchema(resolvedSchema) : resolvedSchema;
14108
14140
  if (!resolveRemoteFileSchema) {
14109
14141
  return {
14110
14142
  success: false,
@@ -14138,13 +14170,6 @@ async function handleRemoteFileUpload(ctx) {
14138
14170
  // media options.
14139
14171
  async function handleRemoteGalleryFileUpload(ctx) {
14140
14172
  var _ctx$validationError$, _ctx$remoteFiles$ctx$;
14141
- if (!ctx.fix) {
14142
- return {
14143
- success: false,
14144
- // No sourcePath in the message: the reported location already points at it.
14145
- errorMessage: `Remote file needs to be uploaded (use --fix to upload)`
14146
- };
14147
- }
14148
14173
  const fix = (_ctx$validationError$ = ctx.validationError.fixes) === null || _ctx$validationError$ === void 0 ? void 0 : _ctx$validationError$[0];
14149
14174
  const mediaType = fix === "files:upload-remote" ? "file" : "image";
14150
14175
 
@@ -14186,30 +14211,7 @@ async function handleRemoteGalleryFileUpload(ctx) {
14186
14211
  errorMessage: `Expected a gallery record at the parent of ${ctx.sourcePath}, got ${(recordSchema === null || recordSchema === void 0 ? void 0 : recordSchema.type) ?? "nothing"}`
14187
14212
  };
14188
14213
  }
14189
- const {
14190
- accept,
14191
- dir
14192
- } = recordSchema;
14193
- const schema = mediaType === "image" ? {
14194
- type: "image",
14195
- opt: false,
14196
- options: {
14197
- ...(accept ? {
14198
- accept
14199
- } : {}),
14200
- ...(dir ? {
14201
- dir
14202
- } : {})
14203
- }
14204
- } : {
14205
- type: "file",
14206
- opt: false,
14207
- options: {
14208
- ...(accept ? {
14209
- accept
14210
- } : {})
14211
- }
14212
- };
14214
+ const schema = core.Internal.galleryEntryMediaSchema(mediaType, recordSchema);
14213
14215
  const uploaded = await uploadRemoteFileCore(ctx, fileRef, metadata, schema);
14214
14216
  const ref = (_ctx$remoteFiles$ctx$ = ctx.remoteFiles[ctx.sourcePath]) === null || _ctx$remoteFiles$ctx$ === void 0 ? void 0 : _ctx$remoteFiles$ctx$.ref;
14215
14217
  if (!uploaded.success || !uploaded.shouldApplyPatch || ref === undefined) {
@@ -12,7 +12,7 @@ var path = require('path');
12
12
  var fs = require('fs');
13
13
  var vm = require('node:vm');
14
14
  var node_module = require('node:module');
15
- var ValRouterCore = require('./ValRouterCore-dfb7b578.cjs.prod.js');
15
+ var ValRouterCore = require('./ValRouterCore-2e82ae18.cjs.prod.js');
16
16
  var server = require('@valbuild/ui/server');
17
17
  var z = require('zod');
18
18
  var zodValidationError = require('zod-validation-error');
@@ -5587,6 +5587,11 @@ class ValOpsHttp extends ValRouterCore.ValOps {
5587
5587
  return ((_this$projectExpectat2 = this.projectExpectation) === null || _this$projectExpectat2 === void 0 ? void 0 : _this$projectExpectat2.sourceMode) ?? null;
5588
5588
  }
5589
5589
 
5590
+ /** See {@link ValOps.canReadPublishedFiles}: not without a repository. */
5591
+ canReadPublishedFiles() {
5592
+ return this.git !== null;
5593
+ }
5594
+
5590
5595
  /** Remembered with {@link sourceMode}, from the same response. */
5591
5596
  projectBranch() {
5592
5597
  var _this$projectExpectat3;
@@ -10546,6 +10551,23 @@ function diskFixFiles(projectRoot) {
10546
10551
 
10547
10552
  const textEncoder = new TextEncoder();
10548
10553
 
10554
+ /**
10555
+ * What an upload fix answers when `--fix` is off and the files it would upload
10556
+ * are all there: a warning, not an error (`WARNING_FIXES` in
10557
+ * `@valbuild/shared`). Asked only after the files were found, so a missing one
10558
+ * is still reported as the error it is.
10559
+ */
10560
+ function localFilesWarning(files) {
10561
+ return {
10562
+ success: true,
10563
+ warning: {
10564
+ message: internal.LOCAL_FILE_IN_REMOTE_PROJECT_WARNING,
10565
+ files,
10566
+ hint: internal.LOCAL_FILE_IN_REMOTE_PROJECT_HINT
10567
+ }
10568
+ };
10569
+ }
10570
+
10549
10571
  /**
10550
10572
  * Everything an upload to Val Remote needs before it has any bytes: the
10551
10573
  * developer's token, the project's public id and a bucket. Opened once per
@@ -11002,12 +11024,6 @@ function movablePathsOf(video, schema) {
11002
11024
  }) => !setBacked || at[0] !== "path");
11003
11025
  }
11004
11026
  async function handleVideoUploadRemote(ctx) {
11005
- if (!ctx.fix) {
11006
- return {
11007
- success: false,
11008
- errorMessage: `Video needs to be uploaded to Val Remote (use --fix to upload)`
11009
- };
11010
- }
11011
11027
  const resolved = resolveVideo(ctx);
11012
11028
  if (!resolved.success) {
11013
11029
  return resolved.result;
@@ -11046,6 +11062,11 @@ async function handleVideoUploadRemote(ctx) {
11046
11062
  errorMessage: `File ${missing} does not exist`
11047
11063
  };
11048
11064
  }
11065
+ if (!ctx.fix) {
11066
+ return localFilesWarning(toUpload.map(({
11067
+ path: ref
11068
+ }) => ref));
11069
+ }
11049
11070
  const opened = await openRemoteUploadSession(ctx);
11050
11071
  if (!opened.success) {
11051
11072
  return opened.result;
@@ -11816,13 +11837,6 @@ async function handleVideosetCheckRemote(ctx) {
11816
11837
  * stays, and the entry is written back with their refs.
11817
11838
  */
11818
11839
  async function handleVideosetUploadRemote(ctx) {
11819
- if (!ctx.fix) {
11820
- return {
11821
- success: false,
11822
- // No sourcePath in the message: the reported location already points at it.
11823
- errorMessage: `Video needs to be uploaded to Val Remote (use --fix to upload)`
11824
- };
11825
- }
11826
11840
  const key = ctx.validationError.value;
11827
11841
  if (typeof key !== "string") {
11828
11842
  return {
@@ -11869,6 +11883,9 @@ async function handleVideosetUploadRemote(ctx) {
11869
11883
  errorMessage: `File ${missing} does not exist`
11870
11884
  };
11871
11885
  }
11886
+ if (!ctx.fix) {
11887
+ return localFilesWarning([...(keyIsLocal ? [key] : []), ...entryFiles.map(file => file.path)]);
11888
+ }
11872
11889
  const opened = await openRemoteUploadSession(ctx);
11873
11890
  if (!opened.success) {
11874
11891
  return opened.result;
@@ -13047,7 +13064,12 @@ async function checkModuleForStudio(ops, input) {
13047
13064
  // not judge without the bytes — which is every image that has metadata at
13048
13065
  // all. Reading them is what turns "has metadata" into "has the RIGHT
13049
13066
  // metadata".
13050
- const fileErrors = await ops.validateFiles(schemas, sources, validation.files, input.fileLastUpdatedByPatchId);
13067
+ // A published file this server cannot read is not judged at all: in a
13068
+ // hosted project a template's files are in the built site, not the content
13069
+ // service (`ValOps.canReadPublishedFiles`), and reading them reported every
13070
+ // one as unreadable.
13071
+ const readableFiles = ops.canReadPublishedFiles() ? validation.files : Object.fromEntries(Object.entries(validation.files).filter(([, file]) => input.fileLastUpdatedByPatchId[file.path] !== undefined));
13072
+ const fileErrors = await ops.validateFiles(schemas, sources, readableFiles, input.fileLastUpdatedByPatchId);
13051
13073
  for (const [sourcePath, found] of Object.entries(fileErrors)) {
13052
13074
  add(sourcePath, found);
13053
13075
  }
@@ -13105,6 +13127,10 @@ function isEntries(source) {
13105
13127
  * is never compared with the file (`videosetCheckAllFilesPatch` says why).
13106
13128
  * - **A remote entry** is asked of its host, as any remote file is, but only
13107
13129
  * whether the host has it (`existenceOnly`).
13130
+ * - **A local entry this server cannot read** is skipped: in a hosted project
13131
+ * a template's files are in the built site, not the content service
13132
+ * (`ValOps.canReadPublishedFiles`), and "gone" would be a lie whose fix
13133
+ * deletes the entry. Its `*:upload-remote` row is what moves it.
13108
13134
  */
13109
13135
  async function checkMediaDirEntries(ops, input, onlyKey) {
13110
13136
  const errors = {};
@@ -13131,9 +13157,15 @@ async function checkMediaDirEntries(ops, input, onlyKey) {
13131
13157
  };
13132
13158
  continue;
13133
13159
  }
13160
+ const fromPatch = input.fileLastUpdatedByPatchId[entry.localPath] ?? null;
13161
+ if (fromPatch === null && !ops.canReadPublishedFiles()) {
13162
+ // Nowhere to read it from -- a hosted project's template files are in
13163
+ // the built site, not the content service -- so nothing to say about
13164
+ // it. Never "gone": that row's fix removes the entry.
13165
+ continue;
13166
+ }
13134
13167
  if (type === "videos") {
13135
13168
  // A video: there or not, and nothing else. See above.
13136
- const fromPatch = input.fileLastUpdatedByPatchId[entry.localPath] ?? null;
13137
13169
  const reader = await ops.openBinaryFile(entry.localPath, fromPatch);
13138
13170
  if (reader === null) {
13139
13171
  errors[entryPath] = [{
@@ -14034,6 +14066,9 @@ async function uploadRemoteFileCore(ctx, fileRef, metadata, schema) {
14034
14066
  }]
14035
14067
  };
14036
14068
  }
14069
+ if (!ctx.fix) {
14070
+ return localFilesWarning([fileRef]);
14071
+ }
14037
14072
  const opened = await openRemoteUploadSession(ctx);
14038
14073
  if (!opened.success) {
14039
14074
  return opened.result;
@@ -14080,13 +14115,6 @@ async function uploadRemoteFileCore(ctx, fileRef, metadata, schema) {
14080
14115
  }
14081
14116
  async function handleRemoteFileUpload(ctx) {
14082
14117
  var _resolvedRemoteFileAt;
14083
- if (!ctx.fix) {
14084
- return {
14085
- success: false,
14086
- // No sourcePath in the message: the reported location already points at it.
14087
- errorMessage: `Remote file needs to be uploaded (use --fix to upload)`
14088
- };
14089
- }
14090
14118
  const [, modulePath] = core.Internal.splitModuleFilePathAndModulePath(ctx.sourcePath);
14091
14119
  if (!ctx.valModule.source || !ctx.valModule.schema) {
14092
14120
  return {
@@ -14104,7 +14132,11 @@ async function handleRemoteFileUpload(ctx) {
14104
14132
  errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`
14105
14133
  };
14106
14134
  }
14107
- const resolveRemoteFileSchema = resolvedRemoteFileAtSourcePath.schema;
14135
+
14136
+ // A rich text image's `src` resolves to the RICH TEXT schema when the schema
14137
+ // is serialized, as it is here: the image schema is its `img` option.
14138
+ const resolvedSchema = resolvedRemoteFileAtSourcePath.schema;
14139
+ const resolveRemoteFileSchema = (resolvedSchema === null || resolvedSchema === void 0 ? void 0 : resolvedSchema.type) === "richtext" ? internal.richTextImageSchema(resolvedSchema) : resolvedSchema;
14108
14140
  if (!resolveRemoteFileSchema) {
14109
14141
  return {
14110
14142
  success: false,
@@ -14138,13 +14170,6 @@ async function handleRemoteFileUpload(ctx) {
14138
14170
  // media options.
14139
14171
  async function handleRemoteGalleryFileUpload(ctx) {
14140
14172
  var _ctx$validationError$, _ctx$remoteFiles$ctx$;
14141
- if (!ctx.fix) {
14142
- return {
14143
- success: false,
14144
- // No sourcePath in the message: the reported location already points at it.
14145
- errorMessage: `Remote file needs to be uploaded (use --fix to upload)`
14146
- };
14147
- }
14148
14173
  const fix = (_ctx$validationError$ = ctx.validationError.fixes) === null || _ctx$validationError$ === void 0 ? void 0 : _ctx$validationError$[0];
14149
14174
  const mediaType = fix === "files:upload-remote" ? "file" : "image";
14150
14175
 
@@ -14186,30 +14211,7 @@ async function handleRemoteGalleryFileUpload(ctx) {
14186
14211
  errorMessage: `Expected a gallery record at the parent of ${ctx.sourcePath}, got ${(recordSchema === null || recordSchema === void 0 ? void 0 : recordSchema.type) ?? "nothing"}`
14187
14212
  };
14188
14213
  }
14189
- const {
14190
- accept,
14191
- dir
14192
- } = recordSchema;
14193
- const schema = mediaType === "image" ? {
14194
- type: "image",
14195
- opt: false,
14196
- options: {
14197
- ...(accept ? {
14198
- accept
14199
- } : {}),
14200
- ...(dir ? {
14201
- dir
14202
- } : {})
14203
- }
14204
- } : {
14205
- type: "file",
14206
- opt: false,
14207
- options: {
14208
- ...(accept ? {
14209
- accept
14210
- } : {})
14211
- }
14212
- };
14214
+ const schema = core.Internal.galleryEntryMediaSchema(mediaType, recordSchema);
14213
14215
  const uploaded = await uploadRemoteFileCore(ctx, fileRef, metadata, schema);
14214
14216
  const ref = (_ctx$remoteFiles$ctx$ = ctx.remoteFiles[ctx.sourcePath]) === null || _ctx$remoteFiles$ctx$ === void 0 ? void 0 : _ctx$remoteFiles$ctx$.ref;
14215
14217
  if (!uploaded.success || !uploaded.shouldApplyPatch || ref === undefined) {
@@ -10,12 +10,12 @@ import path from 'path';
10
10
  import fs from 'fs';
11
11
  import vm from 'node:vm';
12
12
  import { Module } from 'node:module';
13
- import { r as resolveExistingJsonPath, f as findNestedJsonValuesRecords, v as validateJsonValuesEntries, i as isJsonValuesRootOp, a as rebaseContentOp, c as classifyJsonValuesOp, V as ValOps, b as bufferFromDataUrl, g as getFieldsForType, u as uploadRemoteFile, d as getFileExt, e as guessMimeTypeFromPath, h as createMetadataFromBuffer, j as ValOpsMemory, k as isHlsMasterPlaylist, s as sumHlsSegmentDurations, p as parseHlsMasterVariants, l as largestHlsVariant, m as fallbackRefOf, n as mapHlsUris, o as movedRemoteHost, q as needsHostCheck, t as rewriteRefHost, w as fallbackRemoteHost, x as checkRemoteFiles, y as ValServer, z as createValRequestHandler, A as getNewJsonEntryPaths, B as formatPatchSourceError } from './ValRouterCore-2579e663.esm.js';
14
- export { I as InMemoryPatchStore, j as ValOpsMemory, C as askOncePerHost, c as classifyJsonValuesOp, B as formatPatchSourceError, d as getFileExt, D as getSettings, a as rebaseContentOp, u as uploadRemoteFile } from './ValRouterCore-2579e663.esm.js';
13
+ import { r as resolveExistingJsonPath, f as findNestedJsonValuesRecords, v as validateJsonValuesEntries, i as isJsonValuesRootOp, a as rebaseContentOp, c as classifyJsonValuesOp, V as ValOps, b as bufferFromDataUrl, g as getFieldsForType, u as uploadRemoteFile, d as getFileExt, e as guessMimeTypeFromPath, h as createMetadataFromBuffer, j as ValOpsMemory, k as isHlsMasterPlaylist, s as sumHlsSegmentDurations, p as parseHlsMasterVariants, l as largestHlsVariant, m as fallbackRefOf, n as mapHlsUris, o as movedRemoteHost, q as needsHostCheck, t as rewriteRefHost, w as fallbackRemoteHost, x as checkRemoteFiles, y as ValServer, z as createValRequestHandler, A as getNewJsonEntryPaths, B as formatPatchSourceError } from './ValRouterCore-b3c6a562.esm.js';
14
+ export { I as InMemoryPatchStore, j as ValOpsMemory, C as askOncePerHost, c as classifyJsonValuesOp, B as formatPatchSourceError, d as getFileExt, D as getSettings, a as rebaseContentOp, u as uploadRemoteFile } from './ValRouterCore-b3c6a562.esm.js';
15
15
  import { createUIRequestHandler } from '@valbuild/ui/server';
16
16
  import z$1, { z } from 'zod';
17
17
  import { fromError } from 'zod-validation-error';
18
- import { Patch, schemaErrorsOf, PatchSets, joinSharedChanges, schemaTypesOfPath, sideFiles, sideSource, JSONValue, PlanRefusalError, PatchId as PatchId$1, SerializedSchema, getErrorMessageFromUnknownJson, newestCommitSha, traverseSchemaSource, richTextImageSchema, forEachRichTextImage, isStudioFix } from '@valbuild/shared/internal';
18
+ import { Patch, schemaErrorsOf, PatchSets, joinSharedChanges, schemaTypesOfPath, sideFiles, sideSource, JSONValue, PlanRefusalError, PatchId as PatchId$1, SerializedSchema, getErrorMessageFromUnknownJson, newestCommitSha, LOCAL_FILE_IN_REMOTE_PROJECT_WARNING, LOCAL_FILE_IN_REMOTE_PROJECT_HINT, traverseSchemaSource, richTextImageSchema, forEachRichTextImage, isStudioFix } from '@valbuild/shared/internal';
19
19
  import os from 'os';
20
20
  import crypto$1, { createHash } from 'crypto';
21
21
  import { Buffer as Buffer$1 } from 'buffer';
@@ -5551,6 +5551,11 @@ class ValOpsHttp extends ValOps {
5551
5551
  return ((_this$projectExpectat2 = this.projectExpectation) === null || _this$projectExpectat2 === void 0 ? void 0 : _this$projectExpectat2.sourceMode) ?? null;
5552
5552
  }
5553
5553
 
5554
+ /** See {@link ValOps.canReadPublishedFiles}: not without a repository. */
5555
+ canReadPublishedFiles() {
5556
+ return this.git !== null;
5557
+ }
5558
+
5554
5559
  /** Remembered with {@link sourceMode}, from the same response. */
5555
5560
  projectBranch() {
5556
5561
  var _this$projectExpectat3;
@@ -10510,6 +10515,23 @@ function diskFixFiles(projectRoot) {
10510
10515
 
10511
10516
  const textEncoder = new TextEncoder();
10512
10517
 
10518
+ /**
10519
+ * What an upload fix answers when `--fix` is off and the files it would upload
10520
+ * are all there: a warning, not an error (`WARNING_FIXES` in
10521
+ * `@valbuild/shared`). Asked only after the files were found, so a missing one
10522
+ * is still reported as the error it is.
10523
+ */
10524
+ function localFilesWarning(files) {
10525
+ return {
10526
+ success: true,
10527
+ warning: {
10528
+ message: LOCAL_FILE_IN_REMOTE_PROJECT_WARNING,
10529
+ files,
10530
+ hint: LOCAL_FILE_IN_REMOTE_PROJECT_HINT
10531
+ }
10532
+ };
10533
+ }
10534
+
10513
10535
  /**
10514
10536
  * Everything an upload to Val Remote needs before it has any bytes: the
10515
10537
  * developer's token, the project's public id and a bucket. Opened once per
@@ -10966,12 +10988,6 @@ function movablePathsOf(video, schema) {
10966
10988
  }) => !setBacked || at[0] !== "path");
10967
10989
  }
10968
10990
  async function handleVideoUploadRemote(ctx) {
10969
- if (!ctx.fix) {
10970
- return {
10971
- success: false,
10972
- errorMessage: `Video needs to be uploaded to Val Remote (use --fix to upload)`
10973
- };
10974
- }
10975
10991
  const resolved = resolveVideo(ctx);
10976
10992
  if (!resolved.success) {
10977
10993
  return resolved.result;
@@ -11010,6 +11026,11 @@ async function handleVideoUploadRemote(ctx) {
11010
11026
  errorMessage: `File ${missing} does not exist`
11011
11027
  };
11012
11028
  }
11029
+ if (!ctx.fix) {
11030
+ return localFilesWarning(toUpload.map(({
11031
+ path: ref
11032
+ }) => ref));
11033
+ }
11013
11034
  const opened = await openRemoteUploadSession(ctx);
11014
11035
  if (!opened.success) {
11015
11036
  return opened.result;
@@ -11780,13 +11801,6 @@ async function handleVideosetCheckRemote(ctx) {
11780
11801
  * stays, and the entry is written back with their refs.
11781
11802
  */
11782
11803
  async function handleVideosetUploadRemote(ctx) {
11783
- if (!ctx.fix) {
11784
- return {
11785
- success: false,
11786
- // No sourcePath in the message: the reported location already points at it.
11787
- errorMessage: `Video needs to be uploaded to Val Remote (use --fix to upload)`
11788
- };
11789
- }
11790
11804
  const key = ctx.validationError.value;
11791
11805
  if (typeof key !== "string") {
11792
11806
  return {
@@ -11833,6 +11847,9 @@ async function handleVideosetUploadRemote(ctx) {
11833
11847
  errorMessage: `File ${missing} does not exist`
11834
11848
  };
11835
11849
  }
11850
+ if (!ctx.fix) {
11851
+ return localFilesWarning([...(keyIsLocal ? [key] : []), ...entryFiles.map(file => file.path)]);
11852
+ }
11836
11853
  const opened = await openRemoteUploadSession(ctx);
11837
11854
  if (!opened.success) {
11838
11855
  return opened.result;
@@ -13011,7 +13028,12 @@ async function checkModuleForStudio(ops, input) {
13011
13028
  // not judge without the bytes — which is every image that has metadata at
13012
13029
  // all. Reading them is what turns "has metadata" into "has the RIGHT
13013
13030
  // metadata".
13014
- const fileErrors = await ops.validateFiles(schemas, sources, validation.files, input.fileLastUpdatedByPatchId);
13031
+ // A published file this server cannot read is not judged at all: in a
13032
+ // hosted project a template's files are in the built site, not the content
13033
+ // service (`ValOps.canReadPublishedFiles`), and reading them reported every
13034
+ // one as unreadable.
13035
+ const readableFiles = ops.canReadPublishedFiles() ? validation.files : Object.fromEntries(Object.entries(validation.files).filter(([, file]) => input.fileLastUpdatedByPatchId[file.path] !== undefined));
13036
+ const fileErrors = await ops.validateFiles(schemas, sources, readableFiles, input.fileLastUpdatedByPatchId);
13015
13037
  for (const [sourcePath, found] of Object.entries(fileErrors)) {
13016
13038
  add(sourcePath, found);
13017
13039
  }
@@ -13069,6 +13091,10 @@ function isEntries(source) {
13069
13091
  * is never compared with the file (`videosetCheckAllFilesPatch` says why).
13070
13092
  * - **A remote entry** is asked of its host, as any remote file is, but only
13071
13093
  * whether the host has it (`existenceOnly`).
13094
+ * - **A local entry this server cannot read** is skipped: in a hosted project
13095
+ * a template's files are in the built site, not the content service
13096
+ * (`ValOps.canReadPublishedFiles`), and "gone" would be a lie whose fix
13097
+ * deletes the entry. Its `*:upload-remote` row is what moves it.
13072
13098
  */
13073
13099
  async function checkMediaDirEntries(ops, input, onlyKey) {
13074
13100
  const errors = {};
@@ -13095,9 +13121,15 @@ async function checkMediaDirEntries(ops, input, onlyKey) {
13095
13121
  };
13096
13122
  continue;
13097
13123
  }
13124
+ const fromPatch = input.fileLastUpdatedByPatchId[entry.localPath] ?? null;
13125
+ if (fromPatch === null && !ops.canReadPublishedFiles()) {
13126
+ // Nowhere to read it from -- a hosted project's template files are in
13127
+ // the built site, not the content service -- so nothing to say about
13128
+ // it. Never "gone": that row's fix removes the entry.
13129
+ continue;
13130
+ }
13098
13131
  if (type === "videos") {
13099
13132
  // A video: there or not, and nothing else. See above.
13100
- const fromPatch = input.fileLastUpdatedByPatchId[entry.localPath] ?? null;
13101
13133
  const reader = await ops.openBinaryFile(entry.localPath, fromPatch);
13102
13134
  if (reader === null) {
13103
13135
  errors[entryPath] = [{
@@ -13998,6 +14030,9 @@ async function uploadRemoteFileCore(ctx, fileRef, metadata, schema) {
13998
14030
  }]
13999
14031
  };
14000
14032
  }
14033
+ if (!ctx.fix) {
14034
+ return localFilesWarning([fileRef]);
14035
+ }
14001
14036
  const opened = await openRemoteUploadSession(ctx);
14002
14037
  if (!opened.success) {
14003
14038
  return opened.result;
@@ -14044,13 +14079,6 @@ async function uploadRemoteFileCore(ctx, fileRef, metadata, schema) {
14044
14079
  }
14045
14080
  async function handleRemoteFileUpload(ctx) {
14046
14081
  var _resolvedRemoteFileAt;
14047
- if (!ctx.fix) {
14048
- return {
14049
- success: false,
14050
- // No sourcePath in the message: the reported location already points at it.
14051
- errorMessage: `Remote file needs to be uploaded (use --fix to upload)`
14052
- };
14053
- }
14054
14082
  const [, modulePath] = Internal.splitModuleFilePathAndModulePath(ctx.sourcePath);
14055
14083
  if (!ctx.valModule.source || !ctx.valModule.schema) {
14056
14084
  return {
@@ -14068,7 +14096,11 @@ async function handleRemoteFileUpload(ctx) {
14068
14096
  errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`
14069
14097
  };
14070
14098
  }
14071
- const resolveRemoteFileSchema = resolvedRemoteFileAtSourcePath.schema;
14099
+
14100
+ // A rich text image's `src` resolves to the RICH TEXT schema when the schema
14101
+ // is serialized, as it is here: the image schema is its `img` option.
14102
+ const resolvedSchema = resolvedRemoteFileAtSourcePath.schema;
14103
+ const resolveRemoteFileSchema = (resolvedSchema === null || resolvedSchema === void 0 ? void 0 : resolvedSchema.type) === "richtext" ? richTextImageSchema(resolvedSchema) : resolvedSchema;
14072
14104
  if (!resolveRemoteFileSchema) {
14073
14105
  return {
14074
14106
  success: false,
@@ -14102,13 +14134,6 @@ async function handleRemoteFileUpload(ctx) {
14102
14134
  // media options.
14103
14135
  async function handleRemoteGalleryFileUpload(ctx) {
14104
14136
  var _ctx$validationError$, _ctx$remoteFiles$ctx$;
14105
- if (!ctx.fix) {
14106
- return {
14107
- success: false,
14108
- // No sourcePath in the message: the reported location already points at it.
14109
- errorMessage: `Remote file needs to be uploaded (use --fix to upload)`
14110
- };
14111
- }
14112
14137
  const fix = (_ctx$validationError$ = ctx.validationError.fixes) === null || _ctx$validationError$ === void 0 ? void 0 : _ctx$validationError$[0];
14113
14138
  const mediaType = fix === "files:upload-remote" ? "file" : "image";
14114
14139
 
@@ -14150,30 +14175,7 @@ async function handleRemoteGalleryFileUpload(ctx) {
14150
14175
  errorMessage: `Expected a gallery record at the parent of ${ctx.sourcePath}, got ${(recordSchema === null || recordSchema === void 0 ? void 0 : recordSchema.type) ?? "nothing"}`
14151
14176
  };
14152
14177
  }
14153
- const {
14154
- accept,
14155
- dir
14156
- } = recordSchema;
14157
- const schema = mediaType === "image" ? {
14158
- type: "image",
14159
- opt: false,
14160
- options: {
14161
- ...(accept ? {
14162
- accept
14163
- } : {}),
14164
- ...(dir ? {
14165
- dir
14166
- } : {})
14167
- }
14168
- } : {
14169
- type: "file",
14170
- opt: false,
14171
- options: {
14172
- ...(accept ? {
14173
- accept
14174
- } : {})
14175
- }
14176
- };
14178
+ const schema = Internal.galleryEntryMediaSchema(mediaType, recordSchema);
14177
14179
  const uploaded = await uploadRemoteFileCore(ctx, fileRef, metadata, schema);
14178
14180
  const ref = (_ctx$remoteFiles$ctx$ = ctx.remoteFiles[ctx.sourcePath]) === null || _ctx$remoteFiles$ctx$ === void 0 ? void 0 : _ctx$remoteFiles$ctx$.ref;
14179
14181
  if (!uploaded.success || !uploaded.shouldApplyPatch || ref === undefined) {
package/package.json CHANGED
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "exports": true
29
29
  },
30
- "version": "0.150.0",
30
+ "version": "0.150.2",
31
31
  "devDependencies": {
32
32
  "@prettier/sync": "^0.6.1",
33
33
  "@types/jest": "^30.0.0",
@@ -41,9 +41,9 @@
41
41
  "typescript": "^6.0.3",
42
42
  "zod": "^4.4.3",
43
43
  "zod-validation-error": "^5.0.0",
44
- "@valbuild/core": "0.148.7",
45
- "@valbuild/shared": "0.149.7",
46
- "@valbuild/ui": "0.150.0"
44
+ "@valbuild/shared": "0.150.2",
45
+ "@valbuild/ui": "0.150.2",
46
+ "@valbuild/core": "0.150.2"
47
47
  },
48
48
  "engines": {
49
49
  "node": "^20.19.0 || >=22"