@valbuild/server 0.124.0 → 0.126.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,139 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.126.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#664](https://github.com/valbuild/val/pull/664) [`5bfd630`](https://github.com/valbuild/val/commit/5bfd630b63dee2189e238f20fe72ecc5537160f7) Thanks [@freekh](https://github.com/freekh)! - Show the git message on deployments Val did not publish
8
+
9
+ The deploy feed could only name a publish when Val itself had made the commit:
10
+ the message came off Val's own `ValCommit`, and every other deployment — a
11
+ developer's push, a merged pull request, a revert — showed a seven-character
12
+ sha. On most projects those are the majority, so "what went out at 14:02?" had
13
+ no answer in the Studio.
14
+
15
+ A deployment can now carry its own `commitMessage`, which the Studio uses
16
+ wherever there is no Val commit to prefer. It is optional and nullable, so a
17
+ content service that does not report messages is unaffected — those publishes
18
+ keep showing the short sha, exactly as before.
19
+
20
+ Deployment rows also show only the subject line of a message now. A git message
21
+ is a subject, a blank line and a body, and the rows are one truncated line — so
22
+ a real push arrived as "Subject The body went on like this…". The classic
23
+ Draft changes view still has the whole message in its tooltip.
24
+
25
+ - [#652](https://github.com/valbuild/val/pull/652) [`f2fe70d`](https://github.com/valbuild/val/commit/f2fe70dab2b65000dfaf289f09c70b4a8291467a) Thanks [@freekh](https://github.com/freekh)! - `s.union` is now `s.discriminatedUnion` and `s.enum`.
26
+
27
+ `s.union` did two unrelated jobs and worked out which one you meant from its
28
+ first argument: a string key meant a tagged union of objects, literal schemas
29
+ meant a set of allowed strings. Those are now two schemas with two names.
30
+
31
+ ```ts
32
+ // A fixed set of strings — presents as a dropdown
33
+ s.enum("primary", "secondary", "ghost"); // Schema<"primary" | "secondary" | "ghost">
34
+
35
+ // One of several object shapes, told apart by a tag field
36
+ s.discriminatedUnion(
37
+ "type",
38
+ s.object({ type: s.literal("hero"), heading: s.string() }),
39
+ s.object({ type: s.literal("quote"), text: s.string() }),
40
+ );
41
+ ```
42
+
43
+ `s.enum` takes the strings directly, so the `s.literal(...)` wrapper is gone.
44
+
45
+ **`s.union` still works** — it is deprecated, and it builds exactly the schema
46
+ above, so nothing has to change today:
47
+
48
+ ```ts
49
+ s.union(s.literal("one"), s.literal("two")); // → s.enum("one", "two")
50
+ s.union("type", pageA, pageB); // → s.discriminatedUnion("type", pageA, pageB)
51
+ ```
52
+
53
+ The two are different kinds of node, and that is the reason for the split. A
54
+ discriminated union is a container: the selected variant's fields are the fields
55
+ being edited, and everything that walks a schema descends through it. An enum is
56
+ a leaf — a string with a closed domain — so nothing recurses into it. Told apart
57
+ only by the shape of `key`, every consumer had to re-derive which one it was
58
+ holding; each now has its own serialized type (`"discriminated-union"` and
59
+ `"enum"`) and Val Studio has a field per kind rather than one field that
60
+ branches.
61
+
62
+ Two behaviour changes fall out of the split, both of them fixes:
63
+
64
+ - A value that is not a string at all now fails an enum's validation with a
65
+ type error. `s.union` of literals only ever checked the value against its
66
+ literals when the value WAS a string, so a number or an object where an enum
67
+ was declared validated clean.
68
+ - An enum field now shows its validation errors in Val Studio where the field
69
+ is opened on its own — the module editor and the canvas's fields column — and
70
+ gets the compact error layout inside an inline list row. It is a leaf now, so
71
+ it goes through the same error rendering as every other leaf field; the string
72
+ union bypassed it and showed nothing in those places.
73
+
74
+ Several latent crashes in the old `s.union` are fixed on the way past, all of
75
+ them cases where it threw a `TypeError` instead of reporting:
76
+
77
+ - A required discriminated union holding `null` now reports a type error rather
78
+ than throwing, and resolving a path underneath a nullable one that is `null`
79
+ gives the error the API promises instead of a crash.
80
+ - `s.literal("")` is a legal discriminator tag, and `s.enum("")` a legal value.
81
+ Both used to be treated as absent by a truthiness check — in path resolution,
82
+ in stega encoding, and in the message that lists a union's valid tags. The
83
+ editor's dropdowns handle them too: an empty value is reserved by the select
84
+ component and had to be mapped around.
85
+ - A variant that omits the discriminator entirely is now reported as the schema
86
+ error it is, instead of throwing while the check looked for it.
87
+ - An enum's value is now indexed for search, like every other string leaf. The
88
+ old string union was never indexed at all, so searching for one of its values
89
+ could not find the field.
90
+ - A nullable discriminated union set to `null` no longer renders a spinner that
91
+ never resolves.
92
+
93
+ `s.discriminatedUnion` also requires at least one variant, as `s.enum` requires
94
+ at least one value: a union with nothing to select is not a thing to write, and
95
+ everything downstream reads the first variant where it needs any.
96
+
97
+ If you read serialized schemas yourself, that is the breaking part: `type` is no
98
+ longer `"union"`, an enum carries `values: string[]` instead of a `key` plus
99
+ `items` of literal schemas, and `UnionSchema` is no longer a class.
100
+ `SerializedUnionSchema`, `SerializedStringUnionSchema`,
101
+ `SerializedObjectUnionSchema` and `UnionSchema` remain as deprecated type
102
+ aliases.
103
+
104
+ - [#666](https://github.com/valbuild/val/pull/666) [`5c18c99`](https://github.com/valbuild/val/commit/5c18c99ecc84651f82123481fc042063db953833) Thanks [@freekh](https://github.com/freekh)! - `next build` no longer warns `module.createRequire failed parsing argument.`
105
+
106
+ Every Next app that bundles `@valbuild/server` into its Val API route got this
107
+ on every build, twice, with an import trace that led from `route.ts` down into
108
+ `valbuild-server.esm.js` and stopped there:
109
+
110
+ ```
111
+ ⚠ ./node_modules/.../@valbuild/server/dist/valbuild-server.esm.js
112
+ module.createRequire failed parsing argument.
113
+ ```
114
+
115
+ Nothing was wrong. webpack special-cases a `createRequire` binding imported from
116
+ `node:module` and tries to resolve the call's argument at build time; the two
117
+ calls in this package take a path inside the user's project, known only at
118
+ runtime, so there was nothing to resolve and nothing the warning could tell
119
+ anyone. Both now go through a helper that reaches the same function through the
120
+ `Module` class, which that analysis does not tag. Runtime behaviour is
121
+ unchanged, and a lint rule keeps the direct import from coming back.
122
+
123
+ - Updated dependencies [[`719ad6b`](https://github.com/valbuild/val/commit/719ad6b607bcf136d0dbde9e90bf4b8a843561a4), [`9830277`](https://github.com/valbuild/val/commit/9830277e9aaca8da3030f629c2656ec58da47e45), [`64bfd0a`](https://github.com/valbuild/val/commit/64bfd0a6c85832ea5169b53e47087f22e193df36), [`7782979`](https://github.com/valbuild/val/commit/7782979e9b52f2015a6e72dc981e630d4f8c78e2), [`5bfd630`](https://github.com/valbuild/val/commit/5bfd630b63dee2189e238f20fe72ecc5537160f7), [`ccbcda6`](https://github.com/valbuild/val/commit/ccbcda60b3e3c465071229ae1ba28ac735483e63), [`f2fe70d`](https://github.com/valbuild/val/commit/f2fe70dab2b65000dfaf289f09c70b4a8291467a), [`755e1a3`](https://github.com/valbuild/val/commit/755e1a3953775cb8d2c2dce87d6810d3dc329640), [`c6b1ec8`](https://github.com/valbuild/val/commit/c6b1ec84f1883750a4cfe5f70470b177621e971f), [`656f680`](https://github.com/valbuild/val/commit/656f680043c640f678625a64e690389ab23a0a69), [`610a041`](https://github.com/valbuild/val/commit/610a0414b120b521f38a2eb1182b3778bf778b2b), [`171208a`](https://github.com/valbuild/val/commit/171208a20177e68ed5a8b1a6fdaabfe893a6aa5f)]:
124
+ - @valbuild/ui@0.126.0
125
+ - @valbuild/shared@0.126.0
126
+ - @valbuild/core@0.126.0
127
+
128
+ ## 0.125.0
129
+
130
+ ### Patch Changes
131
+
132
+ - Updated dependencies [[`c390397`](https://github.com/valbuild/val/commit/c390397cb5e203eb1bedae3a6fab15726e850b90)]:
133
+ - @valbuild/core@0.125.0
134
+ - @valbuild/shared@0.125.0
135
+ - @valbuild/ui@0.125.0
136
+
3
137
  ## 0.124.0
4
138
 
5
139
  ### Minor Changes
@@ -696,6 +696,32 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
696
696
  return parsedConfigFile.options;
697
697
  };
698
698
 
699
+ /**
700
+ * Node's `require`, resolving as if from `filename`.
701
+ *
702
+ * Use this instead of importing `createRequire` from `node:module` directly.
703
+ * webpack special-cases a `createRequire` binding imported from `module` /
704
+ * `node:module`: it tries to resolve the call's argument at build time, and
705
+ * when the argument is not a literal it gives up with
706
+ *
707
+ * module.createRequire failed parsing argument.
708
+ *
709
+ * Our argument is never a literal — it is a path inside the user's project,
710
+ * known only at runtime — so there is nothing for that analysis to find, and
711
+ * nothing the warning can tell anyone. It is emitted on every build of every
712
+ * Next app that bundles `@valbuild/server` into its Val API route, which is
713
+ * all of them. Reaching `createRequire` through the `Module` class is the same
714
+ * function and is not what webpack tags, so the call is invisible to it.
715
+ *
716
+ * It has to stay a real `node:module` import: jest's module registry hands out
717
+ * its own `node:module`, and a `require` obtained around it (via
718
+ * `process.getBuiltinModule`, say) would resolve against the real filesystem
719
+ * rather than the registry the tests run in.
720
+ */
721
+ function createNodeRequire(filename) {
722
+ return node_module.Module.createRequire(filename);
723
+ }
724
+
699
725
  /**
700
726
  * The filesystem seam `loadValModules` reads through.
701
727
  *
@@ -829,7 +855,7 @@ function loadModule(absPath, cache, compilerOptions, host) {
829
855
  // Insert into the cache before evaluating so cyclic imports resolve.
830
856
  cache[absPath] = moduleObj;
831
857
  const dirName = path__namespace["default"].dirname(absPath);
832
- const realRequire = node_module.Module.createRequire(absPath);
858
+ const realRequire = createNodeRequire(absPath);
833
859
  const customRequire = spec => {
834
860
  var _ts$resolveModuleName;
835
861
  if (isStubbedSpecifier(spec)) {
@@ -1136,7 +1162,7 @@ function findNestedJsonValuesRecords(schema, path = []) {
1136
1162
  case "array":
1137
1163
  rec(current.item, currentPath.concat("*"));
1138
1164
  return;
1139
- case "union":
1165
+ case "discriminated-union":
1140
1166
  for (let i = 0; i < current.items.length; i++) {
1141
1167
  rec(current.items[i], currentPath.concat(`union[${i}]`));
1142
1168
  }
@@ -7015,7 +7041,11 @@ const GetApplicablePatches = z.z.object({
7015
7041
  commitSha: z.z.string(),
7016
7042
  deploymentState: z.z.string(),
7017
7043
  createdAt: z.z.string(),
7018
- updatedAt: z.z.string()
7044
+ updatedAt: z.z.string(),
7045
+ // The git message, where the content service knows it. See
7046
+ // `ValDeployment`: this is the only source for a deployment that Val
7047
+ // did not publish, and zod would otherwise strip it here.
7048
+ commitMessage: z.z.string().nullable().optional()
7019
7049
  })).optional()
7020
7050
  });
7021
7051
  const FilesResponse = z.z.object({
@@ -7616,7 +7646,8 @@ class ValOpsHttp extends ValOps {
7616
7646
  deploymentId: deployment.deploymentId,
7617
7647
  deploymentState: deployment.deploymentState,
7618
7648
  createdAt: deployment.createdAt,
7619
- updatedAt: deployment.updatedAt
7649
+ updatedAt: deployment.updatedAt,
7650
+ commitMessage: deployment.commitMessage
7620
7651
  });
7621
7652
  }
7622
7653
  }
@@ -15386,7 +15417,7 @@ async function evalValConfigFile(projectRoot, configFileName) {
15386
15417
  },
15387
15418
  fileName: valConfigPath
15388
15419
  });
15389
- const projectRootRequire = node_module.createRequire(valConfigPath);
15420
+ const projectRootRequire = createNodeRequire(valConfigPath);
15390
15421
  const exportsObj = {};
15391
15422
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15392
15423
  const sandbox = {
@@ -696,6 +696,32 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
696
696
  return parsedConfigFile.options;
697
697
  };
698
698
 
699
+ /**
700
+ * Node's `require`, resolving as if from `filename`.
701
+ *
702
+ * Use this instead of importing `createRequire` from `node:module` directly.
703
+ * webpack special-cases a `createRequire` binding imported from `module` /
704
+ * `node:module`: it tries to resolve the call's argument at build time, and
705
+ * when the argument is not a literal it gives up with
706
+ *
707
+ * module.createRequire failed parsing argument.
708
+ *
709
+ * Our argument is never a literal — it is a path inside the user's project,
710
+ * known only at runtime — so there is nothing for that analysis to find, and
711
+ * nothing the warning can tell anyone. It is emitted on every build of every
712
+ * Next app that bundles `@valbuild/server` into its Val API route, which is
713
+ * all of them. Reaching `createRequire` through the `Module` class is the same
714
+ * function and is not what webpack tags, so the call is invisible to it.
715
+ *
716
+ * It has to stay a real `node:module` import: jest's module registry hands out
717
+ * its own `node:module`, and a `require` obtained around it (via
718
+ * `process.getBuiltinModule`, say) would resolve against the real filesystem
719
+ * rather than the registry the tests run in.
720
+ */
721
+ function createNodeRequire(filename) {
722
+ return node_module.Module.createRequire(filename);
723
+ }
724
+
699
725
  /**
700
726
  * The filesystem seam `loadValModules` reads through.
701
727
  *
@@ -829,7 +855,7 @@ function loadModule(absPath, cache, compilerOptions, host) {
829
855
  // Insert into the cache before evaluating so cyclic imports resolve.
830
856
  cache[absPath] = moduleObj;
831
857
  const dirName = path__namespace["default"].dirname(absPath);
832
- const realRequire = node_module.Module.createRequire(absPath);
858
+ const realRequire = createNodeRequire(absPath);
833
859
  const customRequire = spec => {
834
860
  var _ts$resolveModuleName;
835
861
  if (isStubbedSpecifier(spec)) {
@@ -1136,7 +1162,7 @@ function findNestedJsonValuesRecords(schema, path = []) {
1136
1162
  case "array":
1137
1163
  rec(current.item, currentPath.concat("*"));
1138
1164
  return;
1139
- case "union":
1165
+ case "discriminated-union":
1140
1166
  for (let i = 0; i < current.items.length; i++) {
1141
1167
  rec(current.items[i], currentPath.concat(`union[${i}]`));
1142
1168
  }
@@ -7015,7 +7041,11 @@ const GetApplicablePatches = z.z.object({
7015
7041
  commitSha: z.z.string(),
7016
7042
  deploymentState: z.z.string(),
7017
7043
  createdAt: z.z.string(),
7018
- updatedAt: z.z.string()
7044
+ updatedAt: z.z.string(),
7045
+ // The git message, where the content service knows it. See
7046
+ // `ValDeployment`: this is the only source for a deployment that Val
7047
+ // did not publish, and zod would otherwise strip it here.
7048
+ commitMessage: z.z.string().nullable().optional()
7019
7049
  })).optional()
7020
7050
  });
7021
7051
  const FilesResponse = z.z.object({
@@ -7616,7 +7646,8 @@ class ValOpsHttp extends ValOps {
7616
7646
  deploymentId: deployment.deploymentId,
7617
7647
  deploymentState: deployment.deploymentState,
7618
7648
  createdAt: deployment.createdAt,
7619
- updatedAt: deployment.updatedAt
7649
+ updatedAt: deployment.updatedAt,
7650
+ commitMessage: deployment.commitMessage
7620
7651
  });
7621
7652
  }
7622
7653
  }
@@ -15386,7 +15417,7 @@ async function evalValConfigFile(projectRoot, configFileName) {
15386
15417
  },
15387
15418
  fileName: valConfigPath
15388
15419
  });
15389
- const projectRootRequire = node_module.createRequire(valConfigPath);
15420
+ const projectRootRequire = createNodeRequire(valConfigPath);
15390
15421
  const exportsObj = {};
15391
15422
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15392
15423
  const sandbox = {
@@ -7,7 +7,7 @@ import * as path from 'path';
7
7
  import path__default from 'path';
8
8
  import fs, { promises } from 'fs';
9
9
  import vm from 'node:vm';
10
- import { Module, createRequire } from 'node:module';
10
+ import { Module } from 'node:module';
11
11
  import { resolveSchemaSourceFixForError, Patch, getErrorMessageFromUnknownJson, JSONValue, PatchGroup, newestCommitSha, SerializedSchema, VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE, VAL_SESSION_COOKIE, Api } from '@valbuild/shared/internal';
12
12
  import { createUIRequestHandler } from '@valbuild/ui/server';
13
13
  import crypto$1 from 'crypto';
@@ -662,6 +662,32 @@ const getCompilerOptions = (rootDir, parseConfigHost) => {
662
662
  return parsedConfigFile.options;
663
663
  };
664
664
 
665
+ /**
666
+ * Node's `require`, resolving as if from `filename`.
667
+ *
668
+ * Use this instead of importing `createRequire` from `node:module` directly.
669
+ * webpack special-cases a `createRequire` binding imported from `module` /
670
+ * `node:module`: it tries to resolve the call's argument at build time, and
671
+ * when the argument is not a literal it gives up with
672
+ *
673
+ * module.createRequire failed parsing argument.
674
+ *
675
+ * Our argument is never a literal — it is a path inside the user's project,
676
+ * known only at runtime — so there is nothing for that analysis to find, and
677
+ * nothing the warning can tell anyone. It is emitted on every build of every
678
+ * Next app that bundles `@valbuild/server` into its Val API route, which is
679
+ * all of them. Reaching `createRequire` through the `Module` class is the same
680
+ * function and is not what webpack tags, so the call is invisible to it.
681
+ *
682
+ * It has to stay a real `node:module` import: jest's module registry hands out
683
+ * its own `node:module`, and a `require` obtained around it (via
684
+ * `process.getBuiltinModule`, say) would resolve against the real filesystem
685
+ * rather than the registry the tests run in.
686
+ */
687
+ function createNodeRequire(filename) {
688
+ return Module.createRequire(filename);
689
+ }
690
+
665
691
  /**
666
692
  * The filesystem seam `loadValModules` reads through.
667
693
  *
@@ -795,7 +821,7 @@ function loadModule(absPath, cache, compilerOptions, host) {
795
821
  // Insert into the cache before evaluating so cyclic imports resolve.
796
822
  cache[absPath] = moduleObj;
797
823
  const dirName = path__default.dirname(absPath);
798
- const realRequire = Module.createRequire(absPath);
824
+ const realRequire = createNodeRequire(absPath);
799
825
  const customRequire = spec => {
800
826
  var _ts$resolveModuleName;
801
827
  if (isStubbedSpecifier(spec)) {
@@ -1102,7 +1128,7 @@ function findNestedJsonValuesRecords(schema, path = []) {
1102
1128
  case "array":
1103
1129
  rec(current.item, currentPath.concat("*"));
1104
1130
  return;
1105
- case "union":
1131
+ case "discriminated-union":
1106
1132
  for (let i = 0; i < current.items.length; i++) {
1107
1133
  rec(current.items[i], currentPath.concat(`union[${i}]`));
1108
1134
  }
@@ -6981,7 +7007,11 @@ const GetApplicablePatches = z.object({
6981
7007
  commitSha: z.string(),
6982
7008
  deploymentState: z.string(),
6983
7009
  createdAt: z.string(),
6984
- updatedAt: z.string()
7010
+ updatedAt: z.string(),
7011
+ // The git message, where the content service knows it. See
7012
+ // `ValDeployment`: this is the only source for a deployment that Val
7013
+ // did not publish, and zod would otherwise strip it here.
7014
+ commitMessage: z.string().nullable().optional()
6985
7015
  })).optional()
6986
7016
  });
6987
7017
  const FilesResponse = z.object({
@@ -7582,7 +7612,8 @@ class ValOpsHttp extends ValOps {
7582
7612
  deploymentId: deployment.deploymentId,
7583
7613
  deploymentState: deployment.deploymentState,
7584
7614
  createdAt: deployment.createdAt,
7585
- updatedAt: deployment.updatedAt
7615
+ updatedAt: deployment.updatedAt,
7616
+ commitMessage: deployment.commitMessage
7586
7617
  });
7587
7618
  }
7588
7619
  }
@@ -15352,7 +15383,7 @@ async function evalValConfigFile(projectRoot, configFileName) {
15352
15383
  },
15353
15384
  fileName: valConfigPath
15354
15385
  });
15355
- const projectRootRequire = createRequire(valConfigPath);
15386
+ const projectRootRequire = createNodeRequire(valConfigPath);
15356
15387
  const exportsObj = {};
15357
15388
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15358
15389
  const sandbox = {
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "types": "dist/valbuild-server.cjs.d.ts",
19
- "version": "0.124.0",
19
+ "version": "0.126.0",
20
20
  "devDependencies": {
21
21
  "@prettier/sync": "^0.6.1",
22
22
  "@types/jest": "^30.0.0"
@@ -29,9 +29,9 @@
29
29
  "typescript": "^6.0.3",
30
30
  "zod": "^4.4.3",
31
31
  "zod-validation-error": "^5.0.0",
32
- "@valbuild/core": "0.124.0",
33
- "@valbuild/ui": "0.124.0",
34
- "@valbuild/shared": "0.124.0"
32
+ "@valbuild/core": "0.126.0",
33
+ "@valbuild/shared": "0.126.0",
34
+ "@valbuild/ui": "0.126.0"
35
35
  },
36
36
  "engines": {
37
37
  "node": "^20.19.0 || >=22"