@valbuild/server 0.131.0 → 0.132.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,31 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.132.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#689](https://github.com/valbuild/val/pull/689) [`72cc676`](https://github.com/valbuild/val/commit/72cc6765e92a6e72b5c09ddd9eed8efa7ce899f2) Thanks [@freekh](https://github.com/freekh)! - `VAL_ENV=app` selects `http` mode.
8
+
9
+ A host knows WHERE it is running; which Val mode that implies is Val's to
10
+ derive. `VAL_ENV=app` says "this is the Val app" — a project built in a browser
11
+ and served from a Worker isolate — and Val reads that as http mode: there is no
12
+ disk, so `fs` is never the right fall-through, and the content is Val's own,
13
+ read over HTTP at a commit like any other deployed app.
14
+
15
+ Unlike `VAL_MODE=memory`, this **selects** the mode rather than only refusing a
16
+ fall-through, because everything http mode needs is an environment variable. The
17
+ point is what happens when one is missing: inference reads an absent
18
+ `VAL_API_KEY` as "not a proxy" and resolves `fs` mode, which in an isolate fails
19
+ on `.val/patches.lock` — a path, two layers below the actual mistake. Now each
20
+ of `VAL_API_KEY`, `VAL_SECRET`, `VAL_PROJECT`, `VAL_GIT_COMMIT` and
21
+ `VAL_GIT_BRANCH` is named when it is the one that is not set, and the message
22
+ says which variable put the app in http mode.
23
+
24
+ An explicit `VAL_MODE` still wins, including when it is a typo that has to be
25
+ refused, and `http` is still not a value `VAL_MODE` accepts. A host that passes
26
+ `sourceFiles` still gets memory mode: that is checked before the environment is
27
+ consulted at all, so a build published by an older platform keeps working.
28
+
3
29
  ## 0.131.0
4
30
 
5
31
  ### Minor Changes
@@ -10051,6 +10051,67 @@ function parsePersonalAccessTokenFile(content) {
10051
10051
 
10052
10052
  const DEFAULT_VAL_BUILD_URL = "https://admin.val.build";
10053
10053
 
10054
+ /**
10055
+ * The value of `VAL_ENV` that means "this is the Val app".
10056
+ *
10057
+ * The Val app builds a project in a browser and runs it in a Worker isolate.
10058
+ * There is no disk there and there never will be, so `fs` mode is never the
10059
+ * right fall-through -- and the content is Val's own, reached over HTTP at a
10060
+ * commit, exactly as it is for any other deployed app. So this names `http`.
10061
+ *
10062
+ * What makes the app unusual is not where its content comes from but what
10063
+ * publishing means: the browser rebuilds the site and the new build is served
10064
+ * immediately, instead of a host noticing a commit and redeploying. That is a
10065
+ * difference in what happens AFTER the commit, and `publishOverride` is where
10066
+ * a host says so -- not a difference in where patches, files or sources live.
10067
+ *
10068
+ * A host says WHERE it runs, which is a fact it knows. Which Val mode that
10069
+ * implies is Val's to derive, and that is the whole reason this exists next to
10070
+ * `VAL_MODE` rather than the platform naming a mode itself: one is a
10071
+ * description of an environment, the other an assertion about Val's internals,
10072
+ * and only the first stays true when the internals move. They have already
10073
+ * moved once -- this meant `memory` while the app kept its own patch store --
10074
+ * and no platform had to be changed to follow.
10075
+ */
10076
+ const VAL_APP_ENV = "app";
10077
+
10078
+ /** Which mode the environment SAYS this is, and which variable said so. */
10079
+
10080
+ /**
10081
+ * `null` is "the environment did not say", which is the normal case.
10082
+ *
10083
+ * The two variables differ in what can be DONE with an answer, and the
10084
+ * difference is whether the environment holds everything the mode needs.
10085
+ * `http` does -- an api key, a secret, a project, a commit and a branch are all
10086
+ * env vars -- so `VAL_ENV=app` SELECTS it, and the checks in
10087
+ * {@link initHandlerOptions} name whichever one is missing. `memory` does not:
10088
+ * it needs the host's own source files, which nothing in an environment can
10089
+ * supply, so `VAL_MODE=memory` can only ever turn a fall-through into an error.
10090
+ */
10091
+ function namedMode() {
10092
+ const declared = process.env.VAL_MODE;
10093
+ /*
10094
+ * An empty value counts as unset, which is what `VAL_MODE=` in a shell or a
10095
+ * CI settings page means. An explicit `VAL_MODE` otherwise wins over
10096
+ * `VAL_ENV`: naming a mode outright says something more specific than naming
10097
+ * an environment does, including when what it names is wrong and has to be
10098
+ * refused.
10099
+ */
10100
+ if (declared !== undefined && declared !== "") {
10101
+ return {
10102
+ mode: declared,
10103
+ from: "VAL_MODE"
10104
+ };
10105
+ }
10106
+ if (process.env.VAL_ENV === VAL_APP_ENV) {
10107
+ return {
10108
+ mode: "http",
10109
+ from: "VAL_ENV"
10110
+ };
10111
+ }
10112
+ return null;
10113
+ }
10114
+
10054
10115
  /**
10055
10116
  * Resolve options plus environment into a concrete {@link ValServerConfig}.
10056
10117
  *
@@ -10104,11 +10165,13 @@ async function initHandlerOptions(route, opts, config) {
10104
10165
  };
10105
10166
  }
10106
10167
  /*
10107
- * `VAL_MODE=memory` says the host MEANT to hold the source, and did not.
10168
+ * The environment saying 'memory' means the host MEANT to hold the source,
10169
+ * and did not. Either variable can say it: `VAL_MODE=memory` outright, or
10170
+ * `VAL_ENV=app`, which names an environment that has no disk.
10108
10171
  *
10109
- * It cannot SELECT memory mode -- nothing in the environment can supply
10172
+ * Neither can SELECT memory mode -- nothing in the environment can supply
10110
10173
  * `sourceFiles`, and a mode turned on without them is a server with no
10111
- * content in it. What it does is turn the fall-through into an error.
10174
+ * content in it. What they do is turn the fall-through into an error.
10112
10175
  *
10113
10176
  * Without it, a host that forgot to pass its source got `fs` mode, and `fs`
10114
10177
  * mode in a Worker isolate reaches for a working tree that is not there: the
@@ -10117,20 +10180,37 @@ async function initHandlerOptions(route, opts, config) {
10117
10180
  * environment that runs Val without a disk can set this once and get a
10118
10181
  * sentence instead.
10119
10182
  */
10120
- const declaredMode = process.env.VAL_MODE;
10121
- if (declaredMode === "memory") {
10122
- throw new Error("VAL_MODE is 'memory', but no `sourceFiles` were given here, so there " + "is no source to serve. Memory mode cannot be turned on by the " + "environment: it needs the project's own source, and only the host " + "that holds it can hand it over. On TanStack Start that is the " + "`sourceFiles` option, passed to `initValServer` AND to " + "`initValContent`, which has a Val server of its own and is " + "configured separately. @valbuild/next has no memory mode yet, so " + "for a Next app this variable is set on an environment Val cannot " + "serve from. Unset " + "VAL_MODE to go back to the inferred mode instead ('http' when " + "VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise).");
10183
+ const declared = namedMode();
10184
+ if ((declared === null || declared === void 0 ? void 0 : declared.mode) === "memory") {
10185
+ throw new Error("VAL_MODE is 'memory', but no `sourceFiles` were given here, so there " + "is no source to serve. Memory mode cannot be turned on by the " + "environment: it needs the project's own source, and only the host " + "that holds it can hand it over. On TanStack Start that is the " + "`sourceFiles` option, passed to `initValServer` AND to " + "`initValContent`, which has a Val server of its own and is " + "configured separately. @valbuild/next has no memory mode yet, so " + "for a Next app this variable is set on an environment Val cannot " + "serve from. Unset VAL_MODE to go back to the inferred mode instead " + "('http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' " + "otherwise).");
10123
10186
  }
10124
- // An empty value counts as unset, which is what `VAL_MODE=` in a shell or a
10125
- // CI settings page means. Every other value is refused rather than ignored:
10126
- // ignoring `VAL_MODE=memry` would leave the app in `fs` mode, which is the
10127
- // exact failure this variable exists to catch.
10128
- if (declaredMode !== undefined && declaredMode !== "") {
10129
- throw new Error(`VAL_MODE is '${declaredMode}', which is not a mode Val knows. The only ` + "value it accepts is 'memory', which asserts that the host supplies " + "`sourceFiles`. 'fs' and 'http' are inferred rather than named: " + "'http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise.");
10187
+ /*
10188
+ * Every other value is refused rather than ignored: ignoring `VAL_MODE=memry`
10189
+ * would leave the app in `fs` mode, which is the exact failure this variable
10190
+ * exists to catch.
10191
+ *
10192
+ * `VAL_ENV` is excluded by name rather than by its value happening to pass:
10193
+ * it names 'http', which is selected below, and a reader who sees only
10194
+ * `declared !== null` here would reasonably conclude that 'http' is a
10195
+ * `VAL_MODE` value -- it is not, and the message below says so.
10196
+ */
10197
+ if (declared !== null && declared.from === "VAL_MODE") {
10198
+ throw new Error(`VAL_MODE is '${declared.mode}', which is not a mode Val knows. The only ` + "value it accepts is 'memory', which asserts that the host supplies " + "`sourceFiles`. 'fs' and 'http' are inferred rather than named: " + "'http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise.");
10130
10199
  }
10131
10200
  const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
10132
10201
  const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
10133
- const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
10202
+ /*
10203
+ * The app's environment selects http mode, rather than leaving it to be
10204
+ * inferred from a credential being present.
10205
+ *
10206
+ * The difference shows when something is MISSING. Inference reads an absent
10207
+ * api key as "not a proxy" and falls through to `fs`, which in an isolate
10208
+ * reaches for a working tree that is not there -- an `EPERM` on
10209
+ * `.val/patches.lock`, several layers below the mistake. Selecting the mode
10210
+ * means the checks below run instead, and each one names what it wanted.
10211
+ */
10212
+ const isAppEnv = (declared === null || declared === void 0 ? void 0 : declared.from) === "VAL_ENV";
10213
+ const isProxyMode = opts.mode === "proxy" || isAppEnv || opts.mode === undefined && (maybeApiKey || maybeValSecret);
10134
10214
  const valEnableRedirectUrl = opts.valEnableRedirectUrl || process.env.VAL_ENABLE_REDIRECT_URL;
10135
10215
  const valDisableRedirectUrl = opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL;
10136
10216
  const maybeValProject = opts.project || process.env.VAL_PROJECT;
@@ -10142,19 +10222,28 @@ async function initHandlerOptions(route, opts, config) {
10142
10222
  });
10143
10223
  if (isProxyMode) {
10144
10224
  var _opts$versions, _opts$versions2;
10225
+ /*
10226
+ * Why this app is in http mode, in the message that says what is missing.
10227
+ *
10228
+ * "must be set in proxy mode" is a fine sentence for a developer who wrote
10229
+ * `mode: "proxy"` and a poor one for an app that never mentioned a mode:
10230
+ * there, the answer to "why am I in proxy mode?" is a variable set by the
10231
+ * platform, in a file the reader of this error is not looking at.
10232
+ */
10233
+ const because = isAppEnv ? " (VAL_ENV is 'app', which is the Val app: its content is Val's own " + "and is read over HTTP at a commit, so http mode is the mode and " + "these are what it needs)" : "";
10145
10234
  if (!maybeApiKey || !maybeValSecret) {
10146
- throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
10235
+ throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode" + because);
10147
10236
  }
10148
10237
  const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
10149
10238
  if (!maybeGitCommit) {
10150
- throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
10239
+ throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode" + because);
10151
10240
  }
10152
10241
  const maybeGitBranch = opts.gitBranch || process.env.VAL_GIT_BRANCH;
10153
10242
  if (!maybeGitBranch) {
10154
- throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode");
10243
+ throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode" + because);
10155
10244
  }
10156
10245
  if (!maybeValProject) {
10157
- throw new Error("Proxy mode does not work unless the 'project' option in val.config is defined or the VAL_PROJECT env var is set.");
10246
+ throw new Error("Proxy mode does not work unless the 'project' option in val.config is defined or the VAL_PROJECT env var is set." + because);
10158
10247
  }
10159
10248
  const coreVersion = (_opts$versions = opts.versions) === null || _opts$versions === void 0 ? void 0 : _opts$versions.core;
10160
10249
  if (!coreVersion) {
@@ -10051,6 +10051,67 @@ function parsePersonalAccessTokenFile(content) {
10051
10051
 
10052
10052
  const DEFAULT_VAL_BUILD_URL = "https://admin.val.build";
10053
10053
 
10054
+ /**
10055
+ * The value of `VAL_ENV` that means "this is the Val app".
10056
+ *
10057
+ * The Val app builds a project in a browser and runs it in a Worker isolate.
10058
+ * There is no disk there and there never will be, so `fs` mode is never the
10059
+ * right fall-through -- and the content is Val's own, reached over HTTP at a
10060
+ * commit, exactly as it is for any other deployed app. So this names `http`.
10061
+ *
10062
+ * What makes the app unusual is not where its content comes from but what
10063
+ * publishing means: the browser rebuilds the site and the new build is served
10064
+ * immediately, instead of a host noticing a commit and redeploying. That is a
10065
+ * difference in what happens AFTER the commit, and `publishOverride` is where
10066
+ * a host says so -- not a difference in where patches, files or sources live.
10067
+ *
10068
+ * A host says WHERE it runs, which is a fact it knows. Which Val mode that
10069
+ * implies is Val's to derive, and that is the whole reason this exists next to
10070
+ * `VAL_MODE` rather than the platform naming a mode itself: one is a
10071
+ * description of an environment, the other an assertion about Val's internals,
10072
+ * and only the first stays true when the internals move. They have already
10073
+ * moved once -- this meant `memory` while the app kept its own patch store --
10074
+ * and no platform had to be changed to follow.
10075
+ */
10076
+ const VAL_APP_ENV = "app";
10077
+
10078
+ /** Which mode the environment SAYS this is, and which variable said so. */
10079
+
10080
+ /**
10081
+ * `null` is "the environment did not say", which is the normal case.
10082
+ *
10083
+ * The two variables differ in what can be DONE with an answer, and the
10084
+ * difference is whether the environment holds everything the mode needs.
10085
+ * `http` does -- an api key, a secret, a project, a commit and a branch are all
10086
+ * env vars -- so `VAL_ENV=app` SELECTS it, and the checks in
10087
+ * {@link initHandlerOptions} name whichever one is missing. `memory` does not:
10088
+ * it needs the host's own source files, which nothing in an environment can
10089
+ * supply, so `VAL_MODE=memory` can only ever turn a fall-through into an error.
10090
+ */
10091
+ function namedMode() {
10092
+ const declared = process.env.VAL_MODE;
10093
+ /*
10094
+ * An empty value counts as unset, which is what `VAL_MODE=` in a shell or a
10095
+ * CI settings page means. An explicit `VAL_MODE` otherwise wins over
10096
+ * `VAL_ENV`: naming a mode outright says something more specific than naming
10097
+ * an environment does, including when what it names is wrong and has to be
10098
+ * refused.
10099
+ */
10100
+ if (declared !== undefined && declared !== "") {
10101
+ return {
10102
+ mode: declared,
10103
+ from: "VAL_MODE"
10104
+ };
10105
+ }
10106
+ if (process.env.VAL_ENV === VAL_APP_ENV) {
10107
+ return {
10108
+ mode: "http",
10109
+ from: "VAL_ENV"
10110
+ };
10111
+ }
10112
+ return null;
10113
+ }
10114
+
10054
10115
  /**
10055
10116
  * Resolve options plus environment into a concrete {@link ValServerConfig}.
10056
10117
  *
@@ -10104,11 +10165,13 @@ async function initHandlerOptions(route, opts, config) {
10104
10165
  };
10105
10166
  }
10106
10167
  /*
10107
- * `VAL_MODE=memory` says the host MEANT to hold the source, and did not.
10168
+ * The environment saying 'memory' means the host MEANT to hold the source,
10169
+ * and did not. Either variable can say it: `VAL_MODE=memory` outright, or
10170
+ * `VAL_ENV=app`, which names an environment that has no disk.
10108
10171
  *
10109
- * It cannot SELECT memory mode -- nothing in the environment can supply
10172
+ * Neither can SELECT memory mode -- nothing in the environment can supply
10110
10173
  * `sourceFiles`, and a mode turned on without them is a server with no
10111
- * content in it. What it does is turn the fall-through into an error.
10174
+ * content in it. What they do is turn the fall-through into an error.
10112
10175
  *
10113
10176
  * Without it, a host that forgot to pass its source got `fs` mode, and `fs`
10114
10177
  * mode in a Worker isolate reaches for a working tree that is not there: the
@@ -10117,20 +10180,37 @@ async function initHandlerOptions(route, opts, config) {
10117
10180
  * environment that runs Val without a disk can set this once and get a
10118
10181
  * sentence instead.
10119
10182
  */
10120
- const declaredMode = process.env.VAL_MODE;
10121
- if (declaredMode === "memory") {
10122
- throw new Error("VAL_MODE is 'memory', but no `sourceFiles` were given here, so there " + "is no source to serve. Memory mode cannot be turned on by the " + "environment: it needs the project's own source, and only the host " + "that holds it can hand it over. On TanStack Start that is the " + "`sourceFiles` option, passed to `initValServer` AND to " + "`initValContent`, which has a Val server of its own and is " + "configured separately. @valbuild/next has no memory mode yet, so " + "for a Next app this variable is set on an environment Val cannot " + "serve from. Unset " + "VAL_MODE to go back to the inferred mode instead ('http' when " + "VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise).");
10183
+ const declared = namedMode();
10184
+ if ((declared === null || declared === void 0 ? void 0 : declared.mode) === "memory") {
10185
+ throw new Error("VAL_MODE is 'memory', but no `sourceFiles` were given here, so there " + "is no source to serve. Memory mode cannot be turned on by the " + "environment: it needs the project's own source, and only the host " + "that holds it can hand it over. On TanStack Start that is the " + "`sourceFiles` option, passed to `initValServer` AND to " + "`initValContent`, which has a Val server of its own and is " + "configured separately. @valbuild/next has no memory mode yet, so " + "for a Next app this variable is set on an environment Val cannot " + "serve from. Unset VAL_MODE to go back to the inferred mode instead " + "('http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' " + "otherwise).");
10123
10186
  }
10124
- // An empty value counts as unset, which is what `VAL_MODE=` in a shell or a
10125
- // CI settings page means. Every other value is refused rather than ignored:
10126
- // ignoring `VAL_MODE=memry` would leave the app in `fs` mode, which is the
10127
- // exact failure this variable exists to catch.
10128
- if (declaredMode !== undefined && declaredMode !== "") {
10129
- throw new Error(`VAL_MODE is '${declaredMode}', which is not a mode Val knows. The only ` + "value it accepts is 'memory', which asserts that the host supplies " + "`sourceFiles`. 'fs' and 'http' are inferred rather than named: " + "'http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise.");
10187
+ /*
10188
+ * Every other value is refused rather than ignored: ignoring `VAL_MODE=memry`
10189
+ * would leave the app in `fs` mode, which is the exact failure this variable
10190
+ * exists to catch.
10191
+ *
10192
+ * `VAL_ENV` is excluded by name rather than by its value happening to pass:
10193
+ * it names 'http', which is selected below, and a reader who sees only
10194
+ * `declared !== null` here would reasonably conclude that 'http' is a
10195
+ * `VAL_MODE` value -- it is not, and the message below says so.
10196
+ */
10197
+ if (declared !== null && declared.from === "VAL_MODE") {
10198
+ throw new Error(`VAL_MODE is '${declared.mode}', which is not a mode Val knows. The only ` + "value it accepts is 'memory', which asserts that the host supplies " + "`sourceFiles`. 'fs' and 'http' are inferred rather than named: " + "'http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise.");
10130
10199
  }
10131
10200
  const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
10132
10201
  const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
10133
- const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
10202
+ /*
10203
+ * The app's environment selects http mode, rather than leaving it to be
10204
+ * inferred from a credential being present.
10205
+ *
10206
+ * The difference shows when something is MISSING. Inference reads an absent
10207
+ * api key as "not a proxy" and falls through to `fs`, which in an isolate
10208
+ * reaches for a working tree that is not there -- an `EPERM` on
10209
+ * `.val/patches.lock`, several layers below the mistake. Selecting the mode
10210
+ * means the checks below run instead, and each one names what it wanted.
10211
+ */
10212
+ const isAppEnv = (declared === null || declared === void 0 ? void 0 : declared.from) === "VAL_ENV";
10213
+ const isProxyMode = opts.mode === "proxy" || isAppEnv || opts.mode === undefined && (maybeApiKey || maybeValSecret);
10134
10214
  const valEnableRedirectUrl = opts.valEnableRedirectUrl || process.env.VAL_ENABLE_REDIRECT_URL;
10135
10215
  const valDisableRedirectUrl = opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL;
10136
10216
  const maybeValProject = opts.project || process.env.VAL_PROJECT;
@@ -10142,19 +10222,28 @@ async function initHandlerOptions(route, opts, config) {
10142
10222
  });
10143
10223
  if (isProxyMode) {
10144
10224
  var _opts$versions, _opts$versions2;
10225
+ /*
10226
+ * Why this app is in http mode, in the message that says what is missing.
10227
+ *
10228
+ * "must be set in proxy mode" is a fine sentence for a developer who wrote
10229
+ * `mode: "proxy"` and a poor one for an app that never mentioned a mode:
10230
+ * there, the answer to "why am I in proxy mode?" is a variable set by the
10231
+ * platform, in a file the reader of this error is not looking at.
10232
+ */
10233
+ const because = isAppEnv ? " (VAL_ENV is 'app', which is the Val app: its content is Val's own " + "and is read over HTTP at a commit, so http mode is the mode and " + "these are what it needs)" : "";
10145
10234
  if (!maybeApiKey || !maybeValSecret) {
10146
- throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
10235
+ throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode" + because);
10147
10236
  }
10148
10237
  const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
10149
10238
  if (!maybeGitCommit) {
10150
- throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
10239
+ throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode" + because);
10151
10240
  }
10152
10241
  const maybeGitBranch = opts.gitBranch || process.env.VAL_GIT_BRANCH;
10153
10242
  if (!maybeGitBranch) {
10154
- throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode");
10243
+ throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode" + because);
10155
10244
  }
10156
10245
  if (!maybeValProject) {
10157
- throw new Error("Proxy mode does not work unless the 'project' option in val.config is defined or the VAL_PROJECT env var is set.");
10246
+ throw new Error("Proxy mode does not work unless the 'project' option in val.config is defined or the VAL_PROJECT env var is set." + because);
10158
10247
  }
10159
10248
  const coreVersion = (_opts$versions = opts.versions) === null || _opts$versions === void 0 ? void 0 : _opts$versions.core;
10160
10249
  if (!coreVersion) {
@@ -10017,6 +10017,67 @@ function parsePersonalAccessTokenFile(content) {
10017
10017
 
10018
10018
  const DEFAULT_VAL_BUILD_URL = "https://admin.val.build";
10019
10019
 
10020
+ /**
10021
+ * The value of `VAL_ENV` that means "this is the Val app".
10022
+ *
10023
+ * The Val app builds a project in a browser and runs it in a Worker isolate.
10024
+ * There is no disk there and there never will be, so `fs` mode is never the
10025
+ * right fall-through -- and the content is Val's own, reached over HTTP at a
10026
+ * commit, exactly as it is for any other deployed app. So this names `http`.
10027
+ *
10028
+ * What makes the app unusual is not where its content comes from but what
10029
+ * publishing means: the browser rebuilds the site and the new build is served
10030
+ * immediately, instead of a host noticing a commit and redeploying. That is a
10031
+ * difference in what happens AFTER the commit, and `publishOverride` is where
10032
+ * a host says so -- not a difference in where patches, files or sources live.
10033
+ *
10034
+ * A host says WHERE it runs, which is a fact it knows. Which Val mode that
10035
+ * implies is Val's to derive, and that is the whole reason this exists next to
10036
+ * `VAL_MODE` rather than the platform naming a mode itself: one is a
10037
+ * description of an environment, the other an assertion about Val's internals,
10038
+ * and only the first stays true when the internals move. They have already
10039
+ * moved once -- this meant `memory` while the app kept its own patch store --
10040
+ * and no platform had to be changed to follow.
10041
+ */
10042
+ const VAL_APP_ENV = "app";
10043
+
10044
+ /** Which mode the environment SAYS this is, and which variable said so. */
10045
+
10046
+ /**
10047
+ * `null` is "the environment did not say", which is the normal case.
10048
+ *
10049
+ * The two variables differ in what can be DONE with an answer, and the
10050
+ * difference is whether the environment holds everything the mode needs.
10051
+ * `http` does -- an api key, a secret, a project, a commit and a branch are all
10052
+ * env vars -- so `VAL_ENV=app` SELECTS it, and the checks in
10053
+ * {@link initHandlerOptions} name whichever one is missing. `memory` does not:
10054
+ * it needs the host's own source files, which nothing in an environment can
10055
+ * supply, so `VAL_MODE=memory` can only ever turn a fall-through into an error.
10056
+ */
10057
+ function namedMode() {
10058
+ const declared = process.env.VAL_MODE;
10059
+ /*
10060
+ * An empty value counts as unset, which is what `VAL_MODE=` in a shell or a
10061
+ * CI settings page means. An explicit `VAL_MODE` otherwise wins over
10062
+ * `VAL_ENV`: naming a mode outright says something more specific than naming
10063
+ * an environment does, including when what it names is wrong and has to be
10064
+ * refused.
10065
+ */
10066
+ if (declared !== undefined && declared !== "") {
10067
+ return {
10068
+ mode: declared,
10069
+ from: "VAL_MODE"
10070
+ };
10071
+ }
10072
+ if (process.env.VAL_ENV === VAL_APP_ENV) {
10073
+ return {
10074
+ mode: "http",
10075
+ from: "VAL_ENV"
10076
+ };
10077
+ }
10078
+ return null;
10079
+ }
10080
+
10020
10081
  /**
10021
10082
  * Resolve options plus environment into a concrete {@link ValServerConfig}.
10022
10083
  *
@@ -10070,11 +10131,13 @@ async function initHandlerOptions(route, opts, config) {
10070
10131
  };
10071
10132
  }
10072
10133
  /*
10073
- * `VAL_MODE=memory` says the host MEANT to hold the source, and did not.
10134
+ * The environment saying 'memory' means the host MEANT to hold the source,
10135
+ * and did not. Either variable can say it: `VAL_MODE=memory` outright, or
10136
+ * `VAL_ENV=app`, which names an environment that has no disk.
10074
10137
  *
10075
- * It cannot SELECT memory mode -- nothing in the environment can supply
10138
+ * Neither can SELECT memory mode -- nothing in the environment can supply
10076
10139
  * `sourceFiles`, and a mode turned on without them is a server with no
10077
- * content in it. What it does is turn the fall-through into an error.
10140
+ * content in it. What they do is turn the fall-through into an error.
10078
10141
  *
10079
10142
  * Without it, a host that forgot to pass its source got `fs` mode, and `fs`
10080
10143
  * mode in a Worker isolate reaches for a working tree that is not there: the
@@ -10083,20 +10146,37 @@ async function initHandlerOptions(route, opts, config) {
10083
10146
  * environment that runs Val without a disk can set this once and get a
10084
10147
  * sentence instead.
10085
10148
  */
10086
- const declaredMode = process.env.VAL_MODE;
10087
- if (declaredMode === "memory") {
10088
- throw new Error("VAL_MODE is 'memory', but no `sourceFiles` were given here, so there " + "is no source to serve. Memory mode cannot be turned on by the " + "environment: it needs the project's own source, and only the host " + "that holds it can hand it over. On TanStack Start that is the " + "`sourceFiles` option, passed to `initValServer` AND to " + "`initValContent`, which has a Val server of its own and is " + "configured separately. @valbuild/next has no memory mode yet, so " + "for a Next app this variable is set on an environment Val cannot " + "serve from. Unset " + "VAL_MODE to go back to the inferred mode instead ('http' when " + "VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise).");
10149
+ const declared = namedMode();
10150
+ if ((declared === null || declared === void 0 ? void 0 : declared.mode) === "memory") {
10151
+ throw new Error("VAL_MODE is 'memory', but no `sourceFiles` were given here, so there " + "is no source to serve. Memory mode cannot be turned on by the " + "environment: it needs the project's own source, and only the host " + "that holds it can hand it over. On TanStack Start that is the " + "`sourceFiles` option, passed to `initValServer` AND to " + "`initValContent`, which has a Val server of its own and is " + "configured separately. @valbuild/next has no memory mode yet, so " + "for a Next app this variable is set on an environment Val cannot " + "serve from. Unset VAL_MODE to go back to the inferred mode instead " + "('http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' " + "otherwise).");
10089
10152
  }
10090
- // An empty value counts as unset, which is what `VAL_MODE=` in a shell or a
10091
- // CI settings page means. Every other value is refused rather than ignored:
10092
- // ignoring `VAL_MODE=memry` would leave the app in `fs` mode, which is the
10093
- // exact failure this variable exists to catch.
10094
- if (declaredMode !== undefined && declaredMode !== "") {
10095
- throw new Error(`VAL_MODE is '${declaredMode}', which is not a mode Val knows. The only ` + "value it accepts is 'memory', which asserts that the host supplies " + "`sourceFiles`. 'fs' and 'http' are inferred rather than named: " + "'http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise.");
10153
+ /*
10154
+ * Every other value is refused rather than ignored: ignoring `VAL_MODE=memry`
10155
+ * would leave the app in `fs` mode, which is the exact failure this variable
10156
+ * exists to catch.
10157
+ *
10158
+ * `VAL_ENV` is excluded by name rather than by its value happening to pass:
10159
+ * it names 'http', which is selected below, and a reader who sees only
10160
+ * `declared !== null` here would reasonably conclude that 'http' is a
10161
+ * `VAL_MODE` value -- it is not, and the message below says so.
10162
+ */
10163
+ if (declared !== null && declared.from === "VAL_MODE") {
10164
+ throw new Error(`VAL_MODE is '${declared.mode}', which is not a mode Val knows. The only ` + "value it accepts is 'memory', which asserts that the host supplies " + "`sourceFiles`. 'fs' and 'http' are inferred rather than named: " + "'http' when VAL_API_KEY and VAL_SECRET are both set, 'fs' otherwise.");
10096
10165
  }
10097
10166
  const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
10098
10167
  const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
10099
- const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
10168
+ /*
10169
+ * The app's environment selects http mode, rather than leaving it to be
10170
+ * inferred from a credential being present.
10171
+ *
10172
+ * The difference shows when something is MISSING. Inference reads an absent
10173
+ * api key as "not a proxy" and falls through to `fs`, which in an isolate
10174
+ * reaches for a working tree that is not there -- an `EPERM` on
10175
+ * `.val/patches.lock`, several layers below the mistake. Selecting the mode
10176
+ * means the checks below run instead, and each one names what it wanted.
10177
+ */
10178
+ const isAppEnv = (declared === null || declared === void 0 ? void 0 : declared.from) === "VAL_ENV";
10179
+ const isProxyMode = opts.mode === "proxy" || isAppEnv || opts.mode === undefined && (maybeApiKey || maybeValSecret);
10100
10180
  const valEnableRedirectUrl = opts.valEnableRedirectUrl || process.env.VAL_ENABLE_REDIRECT_URL;
10101
10181
  const valDisableRedirectUrl = opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL;
10102
10182
  const maybeValProject = opts.project || process.env.VAL_PROJECT;
@@ -10108,19 +10188,28 @@ async function initHandlerOptions(route, opts, config) {
10108
10188
  });
10109
10189
  if (isProxyMode) {
10110
10190
  var _opts$versions, _opts$versions2;
10191
+ /*
10192
+ * Why this app is in http mode, in the message that says what is missing.
10193
+ *
10194
+ * "must be set in proxy mode" is a fine sentence for a developer who wrote
10195
+ * `mode: "proxy"` and a poor one for an app that never mentioned a mode:
10196
+ * there, the answer to "why am I in proxy mode?" is a variable set by the
10197
+ * platform, in a file the reader of this error is not looking at.
10198
+ */
10199
+ const because = isAppEnv ? " (VAL_ENV is 'app', which is the Val app: its content is Val's own " + "and is read over HTTP at a commit, so http mode is the mode and " + "these are what it needs)" : "";
10111
10200
  if (!maybeApiKey || !maybeValSecret) {
10112
- throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
10201
+ throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode" + because);
10113
10202
  }
10114
10203
  const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
10115
10204
  if (!maybeGitCommit) {
10116
- throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
10205
+ throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode" + because);
10117
10206
  }
10118
10207
  const maybeGitBranch = opts.gitBranch || process.env.VAL_GIT_BRANCH;
10119
10208
  if (!maybeGitBranch) {
10120
- throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode");
10209
+ throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode" + because);
10121
10210
  }
10122
10211
  if (!maybeValProject) {
10123
- throw new Error("Proxy mode does not work unless the 'project' option in val.config is defined or the VAL_PROJECT env var is set.");
10212
+ throw new Error("Proxy mode does not work unless the 'project' option in val.config is defined or the VAL_PROJECT env var is set." + because);
10124
10213
  }
10125
10214
  const coreVersion = (_opts$versions = opts.versions) === null || _opts$versions === void 0 ? void 0 : _opts$versions.core;
10126
10215
  if (!coreVersion) {
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.131.0",
19
+ "version": "0.132.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/shared": "0.130.0",
33
32
  "@valbuild/core": "0.130.0",
34
- "@valbuild/ui": "0.130.0"
33
+ "@valbuild/ui": "0.130.0",
34
+ "@valbuild/shared": "0.130.0"
35
35
  },
36
36
  "engines": {
37
37
  "node": "^20.19.0 || >=22"