@valbuild/shared 0.145.0 → 0.146.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,29 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.146.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#861](https://github.com/valbuild/val/pull/861) [`89b0fa8`](https://github.com/valbuild/val/commit/89b0fa82a3ae0d8ccd5d0282c1034f1f40cc4e26) Thanks [@freekh](https://github.com/freekh)! - Proposals can be brought up to date with the site, like a pull request.
8
+
9
+ - **Update from the site** merges what was published on the site since a proposal started into the proposal, field by field. Where the site and the proposal changed the same field, the Publish dialog shows both and asks which to keep.
10
+ - **With code:** a proposal that changes code, or a site whose code changed since the proposal started, is updated too. The Studio runs each version's schemas in the tab, and the merged code is built before it can be published.
11
+ - Publish offers **Update from the site** whenever the site has moved under the proposal.
12
+
13
+ - [#864](https://github.com/valbuild/val/pull/864) [`f0c7d27`](https://github.com/valbuild/val/commit/f0c7d27846c21f81ff6ce79c579185150bc0d063) Thanks [@freekh](https://github.com/freekh)! - Patch groups are removed. **Publish** now publishes every pending change, and
14
+ the Review list shows everyone's pending changes in one list. To keep changes
15
+ out of a publish, move them to a proposal.
16
+
17
+ Draft reads no longer send `own_patch_groups_only`, `patch_id` or
18
+ `include_patch_groups`, and the server no longer has the patch-group routes or
19
+ fields. A content host that still sends them is fine: they are ignored.
20
+
21
+ ## 0.145.1
22
+
23
+ ### Patch Changes
24
+
25
+ - [#860](https://github.com/valbuild/val/pull/860) [`a45f595`](https://github.com/valbuild/val/commit/a45f595a5e20b009d4110bbef8ebdc08b2d731d7) Thanks [@freekh](https://github.com/freekh)! - `val publish` and the Studio now tell Val Build which version of the platform's React and TanStack a build was made against. If the platform is updated while a build is running — a project's CI can take several minutes — that build is no longer published onto a platform it was never checked against: the publish stops with a message saying the platform was updated and to build again, and the next build is checked against what it will run on. A build published with `val publish --artifacts` was made elsewhere and does not say, and publishes as before.
26
+
3
27
  ## 0.145.0
4
28
 
5
29
  ### Minor Changes
@@ -21,21 +21,6 @@ export type PlanRefusalErrorT = z.infer<typeof PlanRefusalError>;
21
21
  * with the editor's cookie from another site.
22
22
  */
23
23
  export declare const VAL_STUDIO_HEADER = "x-val-studio";
24
- /**
25
- * A patch group: the set of patches one user has chosen to publish.
26
- *
27
- * Not a patch *set* — a patch set is computed from the schema and says which
28
- * patches must move together; a patch group is curated and says which ones this
29
- * user wants live. See `docs/independent-publish/PLAN.md`.
30
- */
31
- export declare const PatchGroup: z.ZodObject<{
32
- patchGroupId: z.ZodString;
33
- authorId: z.ZodNullable<z.ZodString>;
34
- createdAt: z.ZodString;
35
- publishedAt: z.ZodNullable<z.ZodString>;
36
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
37
- }, z.core.$strip>;
38
- export type PatchGroupT = z.infer<typeof PatchGroup>;
39
24
  declare const cookies: z.ZodUnion<readonly [z.ZodLiteral<"val_session">, z.ZodLiteral<"val_enable">, z.ZodLiteral<"val_state">]>;
40
25
  type Cookies = z.infer<typeof cookies>;
41
26
  declare const enableCookieValue: z.ZodObject<{
@@ -447,13 +432,6 @@ export declare const Api: {
447
432
  headCommitSha: z.ZodOptional<z.ZodString>;
448
433
  headPatchId: z.ZodOptional<z.ZodNullable<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>>;
449
434
  headVersion: z.ZodOptional<z.ZodNumber>;
450
- patchGroups: z.ZodOptional<z.ZodArray<z.ZodObject<{
451
- patchGroupId: z.ZodString;
452
- authorId: z.ZodNullable<z.ZodString>;
453
- createdAt: z.ZodString;
454
- publishedAt: z.ZodNullable<z.ZodString>;
455
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
456
- }, z.core.$strip>>>;
457
435
  commits: z.ZodArray<z.ZodObject<{
458
436
  commitSha: z.ZodString;
459
437
  clientCommitSha: z.ZodNullable<z.ZodString>;
@@ -730,7 +708,6 @@ export declare const Api: {
730
708
  id: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
731
709
  };
732
710
  body: z.ZodOptional<z.ZodObject<{
733
- unstagePatchIds: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>>;
734
711
  reportHeadVersion: z.ZodOptional<z.ZodLiteral<true>>;
735
712
  }, z.core.$strip>>;
736
713
  cookies: {
@@ -779,8 +756,6 @@ export declare const Api: {
779
756
  patchId: z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>;
780
757
  patch: z.ZodAny;
781
758
  }, z.core.$strip>>;
782
- patchGroupId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
783
- withPatchIds: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>>;
784
759
  }, z.core.$strip>;
785
760
  cookies: {
786
761
  val_session: z.ZodOptional<z.ZodString>;
@@ -827,7 +802,6 @@ export declare const Api: {
827
802
  type: z.ZodLiteral<"patch">;
828
803
  patchId: z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>;
829
804
  }, z.core.$strip>]>;
830
- patchGroupId: z.ZodOptional<z.ZodString>;
831
805
  headVersion: z.ZodOptional<z.ZodNumber>;
832
806
  }, z.core.$strip>;
833
807
  }, z.core.$strip>, z.ZodObject<{
@@ -841,7 +815,6 @@ export declare const Api: {
841
815
  type: z.ZodLiteral<"patch">;
842
816
  patchId: z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>;
843
817
  }, z.core.$strip>]>;
844
- patchGroupId: z.ZodOptional<z.ZodString>;
845
818
  headVersion: z.ZodOptional<z.ZodNumber>;
846
819
  }, z.core.$strip>;
847
820
  }, z.core.$strip>]>;
@@ -851,15 +824,6 @@ export declare const Api: {
851
824
  query: {
852
825
  patch_id: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>>;
853
826
  exclude_patch_ops: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
854
- /**
855
- * Include the patch group annotation in the answer.
856
- *
857
- * Off by default because the client CHUNKS this request — a long
858
- * chain is several round trips — and the groups are a property of the
859
- * branch, identical in every chunk. Asking on each one is N identical
860
- * lookups against the content API to learn the same thing.
861
- */
862
- include_patch_groups: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
863
827
  };
864
828
  cookies: {
865
829
  val_session: z.ZodOptional<z.ZodString>;
@@ -934,15 +898,7 @@ export declare const Api: {
934
898
  appliedAt: z.ZodNullable<z.ZodObject<{
935
899
  commitSha: z.ZodString;
936
900
  }, z.core.$strip>>;
937
- patchGroupIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
938
901
  }, z.core.$strip>>;
939
- patchGroups: z.ZodOptional<z.ZodArray<z.ZodObject<{
940
- patchGroupId: z.ZodString;
941
- authorId: z.ZodNullable<z.ZodString>;
942
- createdAt: z.ZodString;
943
- publishedAt: z.ZodNullable<z.ZodString>;
944
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
945
- }, z.core.$strip>>>;
946
902
  baseSha: z.ZodString;
947
903
  error: z.ZodOptional<z.ZodObject<{
948
904
  message: z.ZodString;
@@ -954,88 +910,6 @@ export declare const Api: {
954
910
  }, z.core.$strip>]>;
955
911
  };
956
912
  };
957
- "/patch-groups/~/patches": {
958
- PUT: {
959
- req: {
960
- body: z.ZodObject<{
961
- patchGroupId: z.ZodOptional<z.ZodString>;
962
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
963
- withPatchIds: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>>;
964
- }, z.core.$strip>;
965
- cookies: {
966
- val_session: z.ZodOptional<z.ZodString>;
967
- };
968
- };
969
- res: z.ZodUnion<readonly [z.ZodObject<{
970
- status: z.ZodLiteral<401>;
971
- json: z.ZodObject<{
972
- message: z.ZodString;
973
- }, z.core.$strip>;
974
- }, z.core.$strip>, z.ZodObject<{
975
- status: z.ZodLiteral<403>;
976
- json: z.ZodObject<{
977
- message: z.ZodString;
978
- }, z.core.$strip>;
979
- }, z.core.$strip>, z.ZodObject<{
980
- status: z.ZodLiteral<409>;
981
- json: z.ZodObject<{
982
- message: z.ZodString;
983
- }, z.core.$strip>;
984
- }, z.core.$strip>, z.ZodObject<{
985
- status: z.ZodLiteral<500>;
986
- json: z.ZodObject<{
987
- message: z.ZodString;
988
- }, z.core.$strip>;
989
- }, z.core.$strip>, z.ZodObject<{
990
- status: z.ZodLiteral<200>;
991
- json: z.ZodObject<{
992
- patchGroupId: z.ZodNullable<z.ZodString>;
993
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
994
- headVersion: z.ZodOptional<z.ZodNumber>;
995
- }, z.core.$strip>;
996
- }, z.core.$strip>]>;
997
- };
998
- DELETE: {
999
- req: {
1000
- body: z.ZodObject<{
1001
- patchGroupId: z.ZodOptional<z.ZodString>;
1002
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
1003
- withPatchIds: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>>;
1004
- }, z.core.$strip>;
1005
- cookies: {
1006
- val_session: z.ZodOptional<z.ZodString>;
1007
- };
1008
- };
1009
- res: z.ZodUnion<readonly [z.ZodObject<{
1010
- status: z.ZodLiteral<401>;
1011
- json: z.ZodObject<{
1012
- message: z.ZodString;
1013
- }, z.core.$strip>;
1014
- }, z.core.$strip>, z.ZodObject<{
1015
- status: z.ZodLiteral<403>;
1016
- json: z.ZodObject<{
1017
- message: z.ZodString;
1018
- }, z.core.$strip>;
1019
- }, z.core.$strip>, z.ZodObject<{
1020
- status: z.ZodLiteral<409>;
1021
- json: z.ZodObject<{
1022
- message: z.ZodString;
1023
- }, z.core.$strip>;
1024
- }, z.core.$strip>, z.ZodObject<{
1025
- status: z.ZodLiteral<500>;
1026
- json: z.ZodObject<{
1027
- message: z.ZodString;
1028
- }, z.core.$strip>;
1029
- }, z.core.$strip>, z.ZodObject<{
1030
- status: z.ZodLiteral<200>;
1031
- json: z.ZodObject<{
1032
- patchGroupId: z.ZodNullable<z.ZodString>;
1033
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
1034
- headVersion: z.ZodOptional<z.ZodNumber>;
1035
- }, z.core.$strip>;
1036
- }, z.core.$strip>]>;
1037
- };
1038
- };
1039
913
  "/schema": {
1040
914
  GET: {
1041
915
  req: {
@@ -1083,33 +957,6 @@ export declare const Api: {
1083
957
  validate_binary_files: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
1084
958
  exclude_patches: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
1085
959
  apply_patches: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
1086
- /**
1087
- * Apply ONLY these patches, rather than everything pending.
1088
- *
1089
- * This is what makes a draft-mode render match the caller's staged
1090
- * view. Without it `/sources/~` replays every pending patch on the
1091
- * branch, so a server-rendered preview shows other people's
1092
- * unpublished work — the one thing independent publish is for.
1093
- *
1094
- * Omitted means everything pending, which is the behaviour every
1095
- * existing caller has and must keep. An EMPTY array is a different
1096
- * answer and is honoured as one: a group holding nothing renders
1097
- * base.
1098
- */
1099
- patch_id: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>>;
1100
- /**
1101
- * Apply only the patches in the CALLER'S OWN patch groups.
1102
- *
1103
- * The server resolves them from the session, so a draft render does
1104
- * not have to know its own group ids — which it cannot, since it has
1105
- * no client state. This is what makes `fetchVal` in draft mode show
1106
- * the person their own staged work rather than everybody's.
1107
- *
1108
- * Ignored when `patch_id` is given: an explicit list is a caller that
1109
- * already knows what it wants, and silently intersecting the two
1110
- * would make the narrower one invisible.
1111
- */
1112
- own_patch_groups_only: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
1113
960
  };
1114
961
  cookies: {
1115
962
  val_session: z.ZodOptional<z.ZodString>;
@@ -1306,7 +1153,6 @@ export declare const Api: {
1306
1153
  body: z.ZodObject<{
1307
1154
  message: z.ZodOptional<z.ZodString>;
1308
1155
  patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
1309
- patchGroupId: z.ZodOptional<z.ZodString>;
1310
1156
  expectedHeadCommitSha: z.ZodOptional<z.ZodString>;
1311
1157
  }, z.core.$strip>;
1312
1158
  cookies: {
@@ -1333,11 +1179,6 @@ export declare const Api: {
1333
1179
  }, z.core.$strip>>>;
1334
1180
  headVersion: z.ZodOptional<z.ZodNumber>;
1335
1181
  }, z.core.$strip>;
1336
- }, z.core.$strip>, z.ZodObject<{
1337
- status: z.ZodLiteral<403>;
1338
- json: z.ZodObject<{
1339
- message: z.ZodString;
1340
- }, z.core.$strip>;
1341
1182
  }, z.core.$strip>, z.ZodObject<{
1342
1183
  status: z.ZodLiteral<500>;
1343
1184
  json: z.ZodObject<{
@@ -1359,9 +1200,6 @@ export declare const Api: {
1359
1200
  }, z.core.$strip>, z.ZodObject<{
1360
1201
  message: z.ZodString;
1361
1202
  headMoved: z.ZodLiteral<true>;
1362
- }, z.core.$strip>, z.ZodObject<{
1363
- message: z.ZodString;
1364
- patchGroupPublished: z.ZodLiteral<true>;
1365
1203
  }, z.core.$strip>]>;
1366
1204
  }, z.core.$strip>, z.ZodObject<{
1367
1205
  status: z.ZodLiteral<400>;
@@ -1814,15 +1652,6 @@ export declare const Api: {
1814
1652
  offset: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>>, z.ZodTransform<number, string>>>;
1815
1653
  limit: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>>, z.ZodTransform<number, string>>>;
1816
1654
  apply_patches: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
1817
- /**
1818
- * The caller's own staged work, and nobody else's.
1819
- *
1820
- * The same flag `/sources/~` takes, and for the same reason — a draft
1821
- * page renders both, so a scoped module beside an unscoped
1822
- * `jsonValues` entry showed one person's view and everybody's pending
1823
- * work on the same screen.
1824
- */
1825
- own_patch_groups_only: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>> & z.ZodType<"true" | "false", string[], z.core.$ZodTypeInternals<"true" | "false", string[]>>, z.ZodTransform<boolean, "true" | "false">>>;
1826
1655
  };
1827
1656
  cookies: {
1828
1657
  [VAL_SESSION_COOKIE]: z.ZodOptional<z.ZodString>;
@@ -5,13 +5,11 @@ import type { PatchId } from "@valbuild/core";
5
5
  *
6
6
  * The head the server REPORTS (`headPatchId`) when it reports one, and not the
7
7
  * last patch it listed. The list — `/applicable/patches`, and `/stat` on top of
8
- * it — leaves out patches the running deployment already contains, and since
9
- * patch groups a publish can ship later patches while an earlier one stays
10
- * pending (another author's change that nets to nothing can never be published
11
- * at all). The last listed id is then behind the head for good, and the content
12
- * service accepts a write only on the head, so every save naming it is refused
13
- * — and a re-sync returns the same list. What fixed it in the Studio before
14
- * this existed was discarding the other author's change.
8
+ * it — leaves out patches the running deployment already contains, and a
9
+ * publish can ship later patches while an earlier one stays pending (a failed
10
+ * publish holds its changes back). The last listed id is then behind the head
11
+ * for good, and the content service accepts a write only on the head, so every
12
+ * save naming it is refused — and a re-sync returns the same list.
15
13
  *
16
14
  * `null` is a REPORTED empty chain, not "unknown". `undefined` is a server
17
15
  * that reports no head (`fs` mode, which ignores the parent anyway, or a
@@ -37,3 +37,6 @@ export * from "./search/sourcePath.js";
37
37
  export * from "./theme/accentRamp.js";
38
38
  export * from "./publish/contentApi.js";
39
39
  export * from "./publish/protocol.js";
40
+ export * from "./patchSets/PatchSets.js";
41
+ export * from "./patchSets/schemaTypesOfPath.js";
42
+ export * from "./proposalMerge/trees.js";
@@ -0,0 +1,75 @@
1
+ import { ModuleFilePath, PatchId, SerializedSchema } from "@valbuild/core";
2
+ import { Operation } from "@valbuild/core/patch";
3
+ type AuthorId = string;
4
+ type IsoDateString = string;
5
+ export type PatchMetadata = {
6
+ patchId: PatchId;
7
+ patchPath: PatchPath;
8
+ opType: Operation["op"];
9
+ schemaTypes: Array<SerializedSchema["type"]>;
10
+ author: AuthorId | null;
11
+ createdAt: IsoDateString;
12
+ };
13
+ export type PatchSetMetadata = {
14
+ moduleFilePath: ModuleFilePath;
15
+ patchPath: PatchPath;
16
+ patches: PatchMetadata[];
17
+ authors: AuthorId[];
18
+ opTypes: Operation["op"][];
19
+ schemaTypes: Array<SerializedSchema["type"]>;
20
+ lastUpdated: IsoDateString;
21
+ lastUpdatedBy: AuthorId | null;
22
+ };
23
+ /**
24
+ * A patch set is a set of patches that are non-independent of each other. This is useful for example when we want to apply a set of patches atomically.
25
+ * A patch set is identified by a module file path and a json patch path delimited by a '?' character. We call this the patch set path. Example: /file/name.val.ts?/path/to/patch
26
+ *
27
+ *
28
+ * NOTE: the order of patch sets is always newest first. This is because the UX always shows the newest patches first.
29
+ */
30
+ export type SerializedPatchSet = PatchSetMetadata[];
31
+ type PatchPath = string[];
32
+ /**
33
+ * Is `inner` strictly nested inside the patch set `outer`?
34
+ *
35
+ * A plain `inner.startsWith(outer)` is not enough, and the difference is not
36
+ * cosmetic. The file part of a key is terminated by the '?' delimiter, but the
37
+ * segments of the patch path are not terminated by anything, so a raw prefix
38
+ * test also matches siblings whose *name* merely starts with the same
39
+ * characters: '/p.val.ts?foobar/title' starts with '/p.val.ts?foo', so removing
40
+ * record key "foo" and retitling record key "foobar" would be merged into one
41
+ * patch set.
42
+ *
43
+ * That used to only over-group two unrelated changes in the compare view. Now
44
+ * that patch sets decide what moves between branches as one (whole patch sets,
45
+ * as `changesToMove` moves them), it means moving one key silently moves its
46
+ * similarly-named sibling — so the boundary has to be
47
+ * checked explicitly: '?' when `outer` is a bare module file path, '/' when it
48
+ * already has a patch path.
49
+ */
50
+ export declare function isInsidePatchSetPath(inner: string, outer: string): boolean;
51
+ export declare class PatchSets {
52
+ private insertedPatches;
53
+ private patchSetMetadata;
54
+ private orderedInsertKeys;
55
+ constructor();
56
+ reset(): void;
57
+ getInsertedPatches(): Set<PatchId>;
58
+ private insertPath;
59
+ /**
60
+ * Insert a whole patch.
61
+ *
62
+ * Takes the patch rather than one op because the de-duplication below is
63
+ * per-*patch*: a patch is either known to this instance or it is not. When this
64
+ * took a single op, callers looped and every call after the first hit the
65
+ * `insertedPatches` guard and returned — so only a patch's first op ever reached
66
+ * a patch set. A patch that touched two places (a `move`, or a `file` op sitting
67
+ * before its source op) was therefore mis-grouped, which is invisible in the
68
+ * compare view but decides what moves as one.
69
+ */
70
+ insert(moduleFilePath: ModuleFilePath, schema: SerializedSchema | undefined, patch: Operation[], patchId: PatchId, createdAt: IsoDateString, author: AuthorId | null): void;
71
+ private insertOp;
72
+ isInserted(patchId: PatchId): boolean;
73
+ serialize(): SerializedPatchSet;
74
+ }
75
+ export {};
@@ -0,0 +1,5 @@
1
+ import { SerializedSchema } from "@valbuild/core";
2
+ /**
3
+ * This functions returns all the possible schema types of a given module path.
4
+ */
5
+ export declare function schemaTypesOfPath(schema: SerializedSchema, patchPath?: string[]): Set<SerializedSchema["type"]>;
@@ -0,0 +1,56 @@
1
+ import type { ModuleFilePath, Schema, SelectorSource, SerializedSchema } from "@valbuild/core";
2
+ import type { JSONValue } from "@valbuild/core/patch";
3
+ /**
4
+ * Update from the site, for a proposal with code (valbuild/home
5
+ * `docs/hosted-publishing.md` step 8.2, slice 1b): what content hands over,
6
+ * what the builder tab makes of it, and what Val's server reads -- one
7
+ * definition of each for the three.
8
+ */
9
+ /** Content a schema does not allow, by path in the module. */
10
+ export type SchemaError = {
11
+ path: string[];
12
+ message: string;
13
+ };
14
+ /** Content's `merge-trees` answer. */
15
+ export type MergeTrees = {
16
+ base: string | null;
17
+ siteHead: string | null;
18
+ proposalHead: string | null;
19
+ upToDate: boolean;
20
+ builds: {
21
+ base: string;
22
+ site: string;
23
+ };
24
+ trees: {
25
+ site: Record<string, string>;
26
+ base: Record<string, string | null>;
27
+ proposal: Record<string, string | null>;
28
+ };
29
+ modules: Record<string, {
30
+ base: string | null;
31
+ site: string | null;
32
+ proposal: string | null;
33
+ }>;
34
+ sources: Record<string, JSONValue>;
35
+ };
36
+ /** Which tree: where the proposal started, the site, or the proposal. */
37
+ export type Side = "base" | "site" | "proposal";
38
+ /** A module, as evaluating its tree in the tab gave it. */
39
+ export type EvaluatedModule = {
40
+ schema: SerializedSchema;
41
+ /** The content its file's text holds. */
42
+ written: JSONValue;
43
+ /** The errors of the module's Source under this schema. */
44
+ errors: SchemaError[];
45
+ };
46
+ export type Evaluated = Record<ModuleFilePath, EvaluatedModule>;
47
+ /** A side's files: the site's tree, the base's changes over it, the proposal's over that. */
48
+ export declare function sideFiles(trees: MergeTrees, side: Side): Record<string, string>;
49
+ /** A side's Source of a module, from content's index; `null` when it has none. */
50
+ export declare function sideSource(trees: MergeTrees, side: Side, moduleFilePath: ModuleFilePath): JSONValue | null;
51
+ /**
52
+ * What a schema reports about this content, without the errors the server
53
+ * fixes on its own (those with `fixes`): run on the schema instance that
54
+ * evaluating the module made, so custom rules take part.
55
+ */
56
+ export declare function schemaErrorsOf(schema: Schema<SelectorSource>, moduleFilePath: ModuleFilePath, source: JSONValue | undefined): SchemaError[];
@@ -50,9 +50,11 @@ export type ContentPublishApi = {
50
50
  *
51
51
  * ## What is deliberately not here
52
52
  *
53
- * No loader URL, no `vendorRev`, no `x-platform-project`. This service holds
54
- * the operator relationship with the build platform and calls it; a publisher
55
- * talks to this API and nothing else. The one exception is the presigned
53
+ * No loader URL, no `x-platform-project`. This service holds the operator
54
+ * relationship with the build platform and calls it; a publisher talks to
55
+ * this API and nothing else. A publisher does say which base layer it built
56
+ * against (`baseRev` below) -- a fact about its build, which this service
57
+ * forwards -- but never addresses the platform itself. The one exception is the presigned
56
58
  * upload URL, which points at object storage -- bytes do not travel through
57
59
  * here.
58
60
  *
@@ -73,6 +75,18 @@ export type ContentPublishApi = {
73
75
  branch: string | null;
74
76
  /** Which dependency layer this was built against, sent or not. */
75
77
  layerRev: string | null;
78
+ /**
79
+ * Which BASE layer -- the platform's React and TanStack -- this was
80
+ * built against: `base.rev` of the build target the build used.
81
+ *
82
+ * Content forwards it to the build platform as the build's own, and
83
+ * the platform refuses a new build whose base layer it no longer
84
+ * serves: one deployed while the build ran, which the build's layer
85
+ * was never checked against. Left out when it is not known -- a build
86
+ * made elsewhere and published with `--artifacts` -- which publishes
87
+ * as every build did before.
88
+ */
89
+ baseRev?: string | null;
76
90
  /**
77
91
  * Whether the app links its own CSS.
78
92
  *
@@ -369,10 +383,6 @@ export type ContentPublishApi = {
369
383
  };
370
384
  "/publish-jobs/:jobId/discard": {
371
385
  POST: {
372
- /** The forward closure, as `DELETE /patches` takes it. */
373
- body: {
374
- unstagePatchIds?: string[];
375
- };
376
386
  /** Changes it could not discard: another job in flight holds them. */
377
387
  res: {
378
388
  stillHeld: string[];
@@ -88,6 +88,11 @@ declare const buildTargetResponse: z.ZodObject<{
88
88
  flightServer: z.ZodString;
89
89
  serverFnBase: z.ZodString;
90
90
  }, z.core.$strip>;
91
+ pins: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
92
+ exports: z.ZodOptional<z.ZodObject<{
93
+ worker: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
94
+ browser: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
95
+ }, z.core.$strip>>;
91
96
  }, z.core.$strip>;
92
97
  project: z.ZodObject<{
93
98
  rev: z.ZodNullable<z.ZodString>;
@@ -177,6 +182,11 @@ declare const updateTargetResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
177
182
  flightServer: z.ZodString;
178
183
  serverFnBase: z.ZodString;
179
184
  }, z.core.$strip>;
185
+ pins: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
186
+ exports: z.ZodOptional<z.ZodObject<{
187
+ worker: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
188
+ browser: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
189
+ }, z.core.$strip>>;
180
190
  }, z.core.$strip>;
181
191
  project: z.ZodObject<{
182
192
  rev: z.ZodNullable<z.ZodString>;
@@ -6,14 +6,16 @@
6
6
  * byte-identical each time, because the query and the session are fixed for the
7
7
  * length of a request. This is what makes the second and third call free.
8
8
  *
9
- * ## Why the scope is the REQUEST and can never be wider
10
- *
11
- * The memoised response is `own_patch_groups_only: true` — it contains the
12
- * CALLER'S OWN unpublished work, resolved from their session cookie. A memo
13
- * that outlived the request would serve one author's staged edits to the next
14
- * visitor, which is the exact failure `own_patch_groups_only` exists to
15
- * prevent, and it would be invisible: the draft would simply render somebody
16
- * else's half-finished sentence.
9
+ * ## Why the scope is the REQUEST
10
+ *
11
+ * The memoised response is a DRAFT -- unpublished work, read with the caller's
12
+ * session cookie. Every caller allowed to read drafts gets the same draft (patch
13
+ * groups, which made it per author, are gone), but whether a caller is allowed
14
+ * at all is decided per session. A memo that outlived the request would hand
15
+ * the draft to the next visitor without asking, and it would be invisible: the
16
+ * page would simply render somebody's half-finished sentence. Sharing it across
17
+ * requests is possible now, keyed on the chain's head, and would be its own
18
+ * measured change.
17
19
  *
18
20
  * So this module holds NO state of its own. The caller supplies the box, and
19
21
  * the box has to come from something the framework already scopes to the