@valbuild/server 0.130.0 → 0.131.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,76 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.131.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#686](https://github.com/valbuild/val/pull/686) [`0d5857b`](https://github.com/valbuild/val/commit/0d5857b731e11f7e6a011f79297df6485908c31f) Thanks [@freekh](https://github.com/freekh)! - Say `VAL_MODE=memory` where there is no disk, and get a sentence instead of an `EPERM`
8
+
9
+ Memory mode — the one for a host that holds the project's source itself — is
10
+ selected by passing `sourceFiles`, and it has to be: nothing in an environment
11
+ can supply a project's source, so a mode that an env var could switch on would
12
+ be a server with no content in it.
13
+
14
+ The cost was the failure when a host forgot. Val inferred `fs` mode, `fs` mode
15
+ went looking for a working tree, and in a Worker isolate the first thing to
16
+ touch the disk failed:
17
+
18
+ ```
19
+ patch-error /bundle/.val/patches.lock: EPERM
20
+ ```
21
+
22
+ That names a path two layers below the decision that caused it, and nobody
23
+ reading it would guess "your server was configured for the wrong mode".
24
+
25
+ So an environment can now DECLARE that it has no disk:
26
+
27
+ ```
28
+ VAL_MODE=memory
29
+ ```
30
+
31
+ It does not turn memory mode on. It says the host is supposed to be supplying
32
+ `sourceFiles`, so if none arrive, Val refuses at configuration time and says
33
+ where to pass them. `VAL_MODE=` counts as unset, the way a shell means it; any
34
+ other value is refused rather than ignored, since leaving you in `fs` mode is
35
+ the exact failure this is meant to catch.
36
+
37
+ **`initValContent` takes the same options, and this is the release that
38
+ noticed.** It builds a Val server of its own — these readers resolve content by
39
+ asking it, not by calling the API over HTTP — so configuring `initValServer`
40
+ alone left them inferring `fs` mode. On a host with no filesystem that is a
41
+ reader looking for a working tree that is not there; it went unnoticed because
42
+ published reads still worked.
43
+
44
+ ```ts
45
+ const patchStore = new InMemoryPatchStore(); // now exported from this package
46
+
47
+ const { valApiHandler, draftMode } = initValServer(valModules, config, {
48
+ sourceFiles: FILES,
49
+ patchStore,
50
+ unsafelyAllowUnauthenticated: true,
51
+ });
52
+
53
+ const { fetchValStega } = initValContent(config, valModules, {
54
+ draftMode,
55
+ // The same three. Two patch stores are two sets of pending edits, and a
56
+ // reader that checks a session the host never issues answers itself 401 and
57
+ // falls back to published content — a draft render showing the live site.
58
+ sourceFiles: FILES,
59
+ patchStore,
60
+ unsafelyAllowUnauthenticated: true,
61
+ });
62
+ ```
63
+
64
+ All three are optional. Left out, this reader gets its own store and its own
65
+ answer about authentication, which is right for published content.
66
+
67
+ `@valbuild/next` has no memory mode: its `initValServer` takes neither option,
68
+ so for a Next app `VAL_MODE=memory` names an environment Val cannot serve from,
69
+ and the error says so.
70
+
71
+ Nothing changes for an app that sets none of this: `http` when `VAL_API_KEY`
72
+ and `VAL_SECRET` are both present, `fs` otherwise, as before.
73
+
3
74
  ## 0.130.0
4
75
 
5
76
  ### Minor Changes
@@ -61,6 +61,11 @@ type ValServerOverrides = Partial<{
61
61
  * and "proxy" this one cannot be inferred from the environment -- there is
62
62
  * nothing to infer it FROM, and a mode that can be turned on without
63
63
  * supplying the source would be a server with no content in it.
64
+ *
65
+ * An environment that has no disk can still say it EXPECTS this, by setting
66
+ * `VAL_MODE=memory`. That does not select the mode; it makes forgetting to
67
+ * pass the source an error here rather than an `EPERM` from `fs` mode two
68
+ * layers down.
64
69
  */
65
70
  sourceFiles: Record<string, string>;
66
71
  /**
@@ -10103,6 +10103,31 @@ async function initHandlerOptions(route, opts, config) {
10103
10103
  config
10104
10104
  };
10105
10105
  }
10106
+ /*
10107
+ * `VAL_MODE=memory` says the host MEANT to hold the source, and did not.
10108
+ *
10109
+ * It cannot SELECT memory mode -- nothing in the environment can supply
10110
+ * `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.
10112
+ *
10113
+ * Without it, a host that forgot to pass its source got `fs` mode, and `fs`
10114
+ * mode in a Worker isolate reaches for a working tree that is not there: the
10115
+ * failure is an `EPERM` on `.val/patches.lock`, several layers below the
10116
+ * mistake, naming a path rather than the decision that led to it. Every
10117
+ * environment that runs Val without a disk can set this once and get a
10118
+ * sentence instead.
10119
+ */
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).");
10123
+ }
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.");
10130
+ }
10106
10131
  const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
10107
10132
  const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
10108
10133
  const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
@@ -10103,6 +10103,31 @@ async function initHandlerOptions(route, opts, config) {
10103
10103
  config
10104
10104
  };
10105
10105
  }
10106
+ /*
10107
+ * `VAL_MODE=memory` says the host MEANT to hold the source, and did not.
10108
+ *
10109
+ * It cannot SELECT memory mode -- nothing in the environment can supply
10110
+ * `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.
10112
+ *
10113
+ * Without it, a host that forgot to pass its source got `fs` mode, and `fs`
10114
+ * mode in a Worker isolate reaches for a working tree that is not there: the
10115
+ * failure is an `EPERM` on `.val/patches.lock`, several layers below the
10116
+ * mistake, naming a path rather than the decision that led to it. Every
10117
+ * environment that runs Val without a disk can set this once and get a
10118
+ * sentence instead.
10119
+ */
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).");
10123
+ }
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.");
10130
+ }
10106
10131
  const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
10107
10132
  const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
10108
10133
  const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
@@ -10069,6 +10069,31 @@ async function initHandlerOptions(route, opts, config) {
10069
10069
  config
10070
10070
  };
10071
10071
  }
10072
+ /*
10073
+ * `VAL_MODE=memory` says the host MEANT to hold the source, and did not.
10074
+ *
10075
+ * It cannot SELECT memory mode -- nothing in the environment can supply
10076
+ * `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.
10078
+ *
10079
+ * Without it, a host that forgot to pass its source got `fs` mode, and `fs`
10080
+ * mode in a Worker isolate reaches for a working tree that is not there: the
10081
+ * failure is an `EPERM` on `.val/patches.lock`, several layers below the
10082
+ * mistake, naming a path rather than the decision that led to it. Every
10083
+ * environment that runs Val without a disk can set this once and get a
10084
+ * sentence instead.
10085
+ */
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).");
10089
+ }
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.");
10096
+ }
10072
10097
  const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
10073
10098
  const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
10074
10099
  const isProxyMode = opts.mode === "proxy" || opts.mode === undefined && (maybeApiKey || maybeValSecret);
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.130.0",
19
+ "version": "0.131.0",
20
20
  "devDependencies": {
21
21
  "@prettier/sync": "^0.6.1",
22
22
  "@types/jest": "^30.0.0"