@valbuild/server 0.131.0 → 0.132.1

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,90 @@
1
1
  # @valbuild/server
2
2
 
3
+ ## 0.132.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#695](https://github.com/valbuild/val/pull/695) [`c07b1ab`](https://github.com/valbuild/val/commit/c07b1abe30e226c80ef7ec4b4f0f5ccdae061c11) Thanks [@freekh](https://github.com/freekh)! - A publish result can now say which commit it was built on, and what tree it points at
8
+
9
+ `CommitResult` gains two optional fields, `parent` and `tree`, filled in from
10
+ the content service's commit response when it reports them.
11
+
12
+ Both matter to a host that keeps its own record of what each commit changed —
13
+ a build cache, or an incremental publisher that rebuilds from the last thing it
14
+ built rather than from scratch. Such a host can only tell whether its record is
15
+ COMPLETE by chaining the commits it holds back to the one it last built. With
16
+ just a sha per commit the record is a set of snapshots with no way to notice a
17
+ gap, so a commit somebody else made in between is silently absent instead of
18
+ detected — and the host rebuilds from a source tree that is missing a change it
19
+ never knew about. `parent` is what closes that.
20
+
21
+ `tree` identifies the CONTENT of a commit rather than the commit itself, so two
22
+ commits carrying the same tree are the same source. A host that has already
23
+ built one of them can recognise the other and skip the work.
24
+
25
+ Both are optional, and absent means NOT REPORTED rather than absent-in-git: a
26
+ content service that predates these fields sends neither, and a caller must not
27
+ read a missing `parent` as "this is a root commit". They are plain strings
28
+ rather than branded shas for the same reason — they are passed through as what
29
+ a separately versioned service said, not as something this end has checked.
30
+
31
+ Nothing changes for a host that does not look at them. The publish route's own
32
+ response is unchanged.
33
+
34
+ - [#678](https://github.com/valbuild/val/pull/678) [`cbfa2b8`](https://github.com/valbuild/val/commit/cbfa2b884898f1603bde8e5aa5cd9da78778101f) Thanks [@freekh](https://github.com/freekh)! - Putting a whole module back works for `.jsonValues()` records
35
+
36
+ A `.jsonValues()` record's entries are not in the module's source: the `.val.ts`
37
+ holds `c.json(() => import("./entry.val.json"))` per entry and the content lives
38
+ in those files. Val already routed a patch that named an entry key into the
39
+ right file, but a patch that replaced the **whole record** named no key — so it
40
+ was applied as an ordinary source edit, writing over the imports that make the
41
+ entries load at all. "Put everything back" in the history pane left such a module
42
+ out for exactly that reason.
43
+
44
+ A whole-record write is now expanded into per-entry ops before anything acts on
45
+ it: an entry added, one removed, one changed, and nothing at all for an entry
46
+ that already holds what the write says — so putting a module back does not
47
+ rewrite every file in it. The same expansion produces the draft the Studio shows
48
+ and the files a publish writes, so a draft cannot show one thing and publish
49
+ another.
50
+
51
+ Nothing that writes a patch has to know a record is `.jsonValues()`: write the
52
+ module as if it were ordinary content, and it lands in the right files.
53
+
54
+ "Put everything back" and "Restore this whole module" now cover `.jsonValues()`
55
+ modules. They read each entry as it was at the commit and put the content back —
56
+ never the recorded source, which is markers rather than content, and is now
57
+ refused rather than written.
58
+
59
+ - Updated dependencies [[`c9a5cd3`](https://github.com/valbuild/val/commit/c9a5cd3a4ab5908ecb9757b7a95db17a77e3b173), [`f413c5c`](https://github.com/valbuild/val/commit/f413c5cebca27ba82052825abc8c632b6177747e), [`1ddf245`](https://github.com/valbuild/val/commit/1ddf245ec73ad5af8099c3d18d4d33c6a1cc1254), [`a19997a`](https://github.com/valbuild/val/commit/a19997a542e65cc1375837b1bdb11c8af9e10160), [`76c5d41`](https://github.com/valbuild/val/commit/76c5d41c3afa7cc8180d156c9e19fb082af7fba4), [`cbfa2b8`](https://github.com/valbuild/val/commit/cbfa2b884898f1603bde8e5aa5cd9da78778101f), [`003419a`](https://github.com/valbuild/val/commit/003419ab72f3069d92e67dfea931d5accc63e730)]:
60
+ - @valbuild/ui@0.132.1
61
+
62
+ ## 0.132.0
63
+
64
+ ### Minor Changes
65
+
66
+ - [#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.
67
+
68
+ A host knows WHERE it is running; which Val mode that implies is Val's to
69
+ derive. `VAL_ENV=app` says "this is the Val app" — a project built in a browser
70
+ and served from a Worker isolate — and Val reads that as http mode: there is no
71
+ disk, so `fs` is never the right fall-through, and the content is Val's own,
72
+ read over HTTP at a commit like any other deployed app.
73
+
74
+ Unlike `VAL_MODE=memory`, this **selects** the mode rather than only refusing a
75
+ fall-through, because everything http mode needs is an environment variable. The
76
+ point is what happens when one is missing: inference reads an absent
77
+ `VAL_API_KEY` as "not a proxy" and resolves `fs` mode, which in an isolate fails
78
+ on `.val/patches.lock` — a path, two layers below the actual mistake. Now each
79
+ of `VAL_API_KEY`, `VAL_SECRET`, `VAL_PROJECT`, `VAL_GIT_COMMIT` and
80
+ `VAL_GIT_BRANCH` is named when it is the one that is not set, and the message
81
+ says which variable put the app in http mode.
82
+
83
+ An explicit `VAL_MODE` still wins, including when it is a typo that has to be
84
+ refused, and `http` is still not a value `VAL_MODE` accepts. A host that passes
85
+ `sourceFiles` still gets memory mode: that is checked before the environment is
86
+ consulted at all, so a build published by an older platform keeps working.
87
+
3
88
  ## 0.131.0
4
89
 
5
90
  ### Minor Changes
@@ -282,6 +282,10 @@ export declare class ValOpsHttp extends ValOps {
282
282
  isNotFastForward?: boolean;
283
283
  updatedFiles: string[];
284
284
  commit: CommitSha;
285
+ /** See `CommitResult.parent`: absent means not reported. */
286
+ parent?: string;
287
+ /** See `CommitResult.tree`: absent means not reported. */
288
+ tree?: string;
285
289
  branch: string;
286
290
  error?: undefined;
287
291
  } | {
@@ -76,6 +76,37 @@ export type CommitResult = {
76
76
  isNotFastForward?: boolean;
77
77
  updatedFiles: string[];
78
78
  commit: CommitSha;
79
+ /**
80
+ * The commit this one was built on.
81
+ *
82
+ * Optional because it is only as available as the content service is
83
+ * willing to say: a service that predates this field sends nothing, and
84
+ * `undefined` there means "not reported", never "this commit has no
85
+ * parent". A host that needs it has to handle its absence rather than
86
+ * treat it as a root commit.
87
+ *
88
+ * What it is FOR: a host that keeps its own record of what each commit
89
+ * changed -- a build cache, an incremental publisher -- can only tell
90
+ * whether its record is complete by chaining the commits it holds back to
91
+ * the one it last built. Without a parent the record is a set of
92
+ * snapshots with no way to notice a gap, and a commit made by somebody
93
+ * else in between is silently absent rather than detected.
94
+ *
95
+ * Not `CommitSha`-typed for the same reason it is optional: it is
96
+ * reported by a remote service and validated on arrival, and a branded
97
+ * type here would suggest this end had checked it.
98
+ */
99
+ parent?: string;
100
+ /**
101
+ * The git tree this commit points at.
102
+ *
103
+ * Optional for the same reason as {@link parent}. A tree hash identifies
104
+ * the CONTENT of a commit rather than the commit itself, so two commits
105
+ * with the same tree are the same source -- which is what lets a host
106
+ * recognise that a commit it is being asked to build is one it has built
107
+ * already, under a different sha, and skip the work.
108
+ */
109
+ tree?: string;
79
110
  branch: string;
80
111
  error?: undefined;
81
112
  } | {
@@ -1,4 +1,4 @@
1
- import type { PatchId, SerializedSchema } from "@valbuild/core";
1
+ import { type PatchId, type SerializedSchema } from "@valbuild/core";
2
2
  import type { PatchSourceError } from "../ValOps.js";
3
3
  import { result } from "@valbuild/core/fp";
4
4
  import { JSONValue, Operation, Patch, PatchError } from "@valbuild/core/patch";
@@ -30,6 +30,74 @@ export type JsonValuesOpClass = {
30
30
  * `.jsonValues()` record.
31
31
  */
32
32
  export declare function classifyJsonValuesOp(schema: SerializedSchema, opPath: string[]): JsonValuesOpClass;
33
+ /**
34
+ * Is this op a write of the WHOLE record at the root of a `.jsonValues()`
35
+ * module?
36
+ *
37
+ * The one op {@link classifyJsonValuesOp} cannot classify: it finds the entry
38
+ * key by walking the op path, and the root path has no segments to walk, so a
39
+ * root write reads as `normal` and gets applied to the `.val.ts` - writing
40
+ * markers over the `c.json(() => import(...))` calls that make the entries load
41
+ * at all. {@link expandJsonValuesRootOp} turns it into ops that DO name a key,
42
+ * which everything downstream already handles.
43
+ *
44
+ * `add` counts as well as `replace`: at the root both mean "the document is now
45
+ * this" (see `JSONOps`), so both have to be expanded or the unexpanded one is
46
+ * the same bug again.
47
+ *
48
+ * The value has to BE a record, and that is part of the question rather than a
49
+ * check inside the expansion: a `.jsonValues()` record can be nullable, and
50
+ * `null` at the root means the module no longer has a record at all - there are
51
+ * no entries to write, and it is an ordinary `.val.ts` write, exactly as it was
52
+ * before any of this existed. Same for any other non-record value: not a
53
+ * whole-record write, so not this conversion's to route.
54
+ */
55
+ export declare function isJsonValuesRootOp(schema: SerializedSchema, op: Operation): boolean;
56
+ /**
57
+ * One entry as it stands right now, for {@link expandJsonValuesRootOp} to
58
+ * expand against.
59
+ *
60
+ * The key set decides add-vs-replace-vs-remove. The content, where the caller
61
+ * has it, decides whether an op is emitted at all: an entry whose content is
62
+ * already what the root write says is left alone rather than rewritten, so
63
+ * putting a module back does not touch every `*.val.json` it did not change.
64
+ * `undefined` means "this entry exists, content unknown" - which is what the
65
+ * Studio's draft source has, because the source holds markers - and yields a
66
+ * `replace`, the safe answer.
67
+ */
68
+ export type CurrentJsonEntries = ReadonlyMap<string, JSONValue | undefined>;
69
+ /**
70
+ * Fans a whole-record write at a `.jsonValues()` module's root out into ops
71
+ * that each name an entry key.
72
+ *
73
+ * This is THE conversion, and it has exactly one implementation on purpose: the
74
+ * commit flow (`ValOps.prepare`) and the read side that builds draft content
75
+ * ({@link applyJsonValuesEntryPatches}) both expand through here, so a draft
76
+ * cannot show something other than what publishing writes. A second
77
+ * implementation of the same rule would differ silently, which is the whole
78
+ * failure mode this exists to prevent.
79
+ *
80
+ * The value must be the entries' CONTENT. A module's Source is markers, not
81
+ * content, so a Source handed over here is refused rather than written: that is
82
+ * the shape of the original bug (a revert replaying the archived Source), and
83
+ * it is not recoverable afterwards - the markers replace the entries' content
84
+ * on disk.
85
+ */
86
+ export declare function expandJsonValuesRootOp(op: Operation, currentEntries: CurrentJsonEntries): result.Result<Operation[], PatchError>;
87
+ /**
88
+ * What a whole-record write says about ONE entry: the same rule
89
+ * {@link expandJsonValuesRootOp} applies to all of them.
90
+ *
91
+ * For a reader that is about one entry. Expanding the whole record to find the
92
+ * one op that names its key costs an op per entry per entry read - the record
93
+ * squared, on exactly the large records this feature is for. The rule itself is
94
+ * `writeForEntry`, shared with the expansion above, so the two cannot come to
95
+ * disagree; only the loop around it differs.
96
+ *
97
+ * `null` means the write says nothing about this entry: it holds that content
98
+ * already, or the record does not name it and it does not exist.
99
+ */
100
+ export declare function expandJsonValuesRootOpForKey(op: Operation, entryKey: string, currentEntries: CurrentJsonEntries): result.Result<Operation | null, PatchError>;
33
101
  /**
34
102
  * Finds every `.jsonValues()` record in a module's schema that is NOT the
35
103
  * module's root, returning the path to each within the module source.