@valbuild/shared 0.129.0 → 0.133.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,69 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.133.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#700](https://github.com/valbuild/val/pull/700) [`802412b`](https://github.com/valbuild/val/commit/802412b92c06bc1abbca78c86885e39c8710dd83) Thanks [@freekh](https://github.com/freekh)! - http mode no longer needs a git repository
8
+
9
+ A Val app can now run in http mode with no commit and no branch — its content
10
+ service is the store of record, and git is an optional mirror of the code. This
11
+ is what `fs` mode has always done: it has never had git, and it works.
12
+
13
+ Before this, `VAL_API_KEY` and `VAL_SECRET` were not enough. `VAL_GIT_COMMIT`
14
+ and `VAL_GIT_BRANCH` were required too, so a deployment with no commit to name
15
+ either threw at boot or fell through to `fs` mode and reached for a working
16
+ tree that was not there.
17
+
18
+ **Breaking, if you pass `http` options in code.** `gitCommit` and `gitBranch`
19
+ are replaced by one optional `git`:
20
+
21
+ ```diff
22
+ initValServer(valModules, config, {
23
+ http: {
24
+ apiKey,
25
+ valSecret,
26
+ - gitCommit: process.env.VAL_GIT_COMMIT,
27
+ - gitBranch: "main",
28
+ + // Only for a project whose content is mirrored into a repository.
29
+ + // Omit it entirely otherwise.
30
+ + git: { commit: process.env.VAL_GIT_COMMIT, branch: "main" },
31
+ },
32
+ })
33
+ ```
34
+
35
+ `VAL_GIT_COMMIT` and `VAL_GIT_BRANCH` still work and are still read; they are
36
+ simply no longer required. Set both or neither — a commit without a branch, or
37
+ a branch without a commit, is refused at startup with a message naming the
38
+ missing half, rather than failing later at a publish.
39
+
40
+ **What a commit is for, where you have one.** Turning pending patches into new
41
+ `.val.ts` text means reading the current text first, and that read goes to the
42
+ content service at that commit. It is the publish path, not the serving path: a
43
+ committed render reads the source compiled into the build and asks the content
44
+ service nothing. With no repository there is nothing to write `.val.ts` into,
45
+ so a publish records the module's data and its schema and skips the file — and
46
+ that data is what history reads, so nothing is lost.
47
+
48
+ **A publish can now be refused by name, before it is attempted.** If a project
49
+ mirrors its commits into a repository but the running deployment was built
50
+ before that repository existed, it has no commit to write the mirror against.
51
+ Publishing anyway would save the content and silently leave the repository
52
+ behind. The Studio now disables Publish and shows why, and `/save` refuses with
53
+ a `no-base` code instead of failing partway.
54
+
55
+ **Also:** `ValCommit` and `HistoricalCommit` have nullable `parentCommitSha`
56
+ and `clientCommitSha`, and `/stat`'s `commitSha` is optional. A root commit has
57
+ no parent, and a publisher with no repository does not report where it was. If
58
+ you read these fields, handle `null`.
59
+
60
+ ## 0.130.0
61
+
62
+ ### Patch Changes
63
+
64
+ - Updated dependencies [[`8425378`](https://github.com/valbuild/val/commit/8425378c315ea46b5d822f1130b633e0449ff1b0), [`473a185`](https://github.com/valbuild/val/commit/473a185f70351b44388f3bc1852649e2c1dbe001), [`64f0de3`](https://github.com/valbuild/val/commit/64f0de339b8621cb5a6c422dfe55cae5b2bbe2a0)]:
65
+ - @valbuild/core@0.130.0
66
+
3
67
  ## 0.129.0
4
68
 
5
69
  ### Minor Changes
@@ -423,6 +423,10 @@ export declare const Api: {
423
423
  profileId: z.ZodNullable<z.ZodString>;
424
424
  mode: z.ZodUnion<readonly [z.ZodLiteral<"http">, z.ZodLiteral<"fs">]>;
425
425
  jsonEntriesSha: z.ZodOptional<z.ZodString>;
426
+ publishRefusal: z.ZodOptional<z.ZodObject<{
427
+ code: z.ZodLiteral<"no-base">;
428
+ message: z.ZodString;
429
+ }, z.core.$strip>>;
426
430
  }, z.core.$strip>, z.ZodObject<{
427
431
  type: z.ZodLiteral<"use-websocket">;
428
432
  url: z.ZodString;
@@ -430,14 +434,14 @@ export declare const Api: {
430
434
  baseSha: z.ZodString;
431
435
  schemaSha: z.ZodString;
432
436
  sourcesSha: z.ZodString;
433
- commitSha: z.ZodString;
437
+ commitSha: z.ZodOptional<z.ZodString>;
434
438
  patches: z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>;
435
439
  appliedPatches: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>>;
436
440
  headCommitSha: z.ZodOptional<z.ZodString>;
437
441
  commits: z.ZodArray<z.ZodObject<{
438
442
  commitSha: z.ZodString;
439
- clientCommitSha: z.ZodString;
440
- parentCommitSha: z.ZodString;
443
+ clientCommitSha: z.ZodNullable<z.ZodString>;
444
+ parentCommitSha: z.ZodNullable<z.ZodString>;
441
445
  branch: z.ZodString;
442
446
  creator: z.ZodString;
443
447
  createdAt: z.ZodString;
@@ -462,6 +466,10 @@ export declare const Api: {
462
466
  }, z.core.$strip>;
463
467
  profileId: z.ZodNullable<z.ZodString>;
464
468
  mode: z.ZodUnion<readonly [z.ZodLiteral<"http">, z.ZodLiteral<"fs">]>;
469
+ publishRefusal: z.ZodOptional<z.ZodObject<{
470
+ code: z.ZodLiteral<"no-base">;
471
+ message: z.ZodString;
472
+ }, z.core.$strip>>;
465
473
  }, z.core.$strip>]>;
466
474
  }, z.core.$strip>]>;
467
475
  };
@@ -1029,6 +1037,12 @@ export declare const Api: {
1029
1037
  status: z.ZodLiteral<409>;
1030
1038
  json: z.ZodUnion<readonly [z.ZodObject<{
1031
1039
  message: z.ZodString;
1040
+ publishRefusal: z.ZodObject<{
1041
+ code: z.ZodLiteral<"no-base">;
1042
+ message: z.ZodString;
1043
+ }, z.core.$strip>;
1044
+ }, z.core.$strip>, z.ZodObject<{
1045
+ message: z.ZodString;
1032
1046
  }, z.core.$strip>, z.ZodObject<{
1033
1047
  message: z.ZodString;
1034
1048
  isNotFastForward: z.ZodLiteral<true>;
@@ -1106,8 +1120,8 @@ export declare const Api: {
1106
1120
  json: z.ZodObject<{
1107
1121
  commits: z.ZodArray<z.ZodObject<{
1108
1122
  commitSha: z.ZodString;
1109
- parentCommitSha: z.ZodString;
1110
- clientCommitSha: z.ZodString;
1123
+ parentCommitSha: z.ZodNullable<z.ZodString>;
1124
+ clientCommitSha: z.ZodNullable<z.ZodString>;
1111
1125
  branch: z.ZodString;
1112
1126
  createdBranch: z.ZodNullable<z.ZodString>;
1113
1127
  creator: z.ZodNullable<z.ZodString>;
@@ -1164,8 +1178,8 @@ export declare const Api: {
1164
1178
  json: z.ZodObject<{
1165
1179
  commit: z.ZodObject<{
1166
1180
  commitSha: z.ZodString;
1167
- parentCommitSha: z.ZodString;
1168
- clientCommitSha: z.ZodString;
1181
+ parentCommitSha: z.ZodNullable<z.ZodString>;
1182
+ clientCommitSha: z.ZodNullable<z.ZodString>;
1169
1183
  branch: z.ZodString;
1170
1184
  createdBranch: z.ZodNullable<z.ZodString>;
1171
1185
  creator: z.ZodNullable<z.ZodString>;
@@ -1,4 +1,5 @@
1
- import { SerializedSchema, Json } from "@valbuild/core";
1
+ import { SerializedSchema } from "@valbuild/core";
2
+ import { JSONValue } from "@valbuild/core/patch";
2
3
  /**
3
4
  * What `emptyOf` cannot read off a serialized schema.
4
5
  *
@@ -22,4 +23,16 @@ export type EmptyOfContext = {
22
23
  */
23
24
  selectedLocale?: string;
24
25
  };
25
- export declare function emptyOf(schema: SerializedSchema, context?: EmptyOfContext): Json;
26
+ /**
27
+ * The empty value for a schema — the thing a "create this" affordance writes.
28
+ *
29
+ * Returns `JSONValue`, the MUTABLE shape, rather than `Json`. Every value here
30
+ * is built fresh on the way out — `Object.fromEntries`, a new array, a
31
+ * primitive — and none of it aliases anything a caller could then mutate out
32
+ * from under someone. Saying `Json` instead made the type readonly, which is
33
+ * a claim about sharing that is not true of anything this returns, and left
34
+ * every caller asserting `as JSONValue` at the point of writing a patch. A
35
+ * `JSONValue` is still assignable to `Json`, so a reader of the value loses
36
+ * nothing.
37
+ */
38
+ export declare function emptyOf(schema: SerializedSchema, context?: EmptyOfContext): JSONValue;
@@ -40,8 +40,8 @@ export declare const HistoryError: z.ZodDiscriminatedUnion<[z.ZodObject<{
40
40
  export type HistoryError = z.infer<typeof HistoryError>;
41
41
  export declare const HistoricalCommit: z.ZodObject<{
42
42
  commitSha: z.ZodString;
43
- parentCommitSha: z.ZodString;
44
- clientCommitSha: z.ZodString;
43
+ parentCommitSha: z.ZodNullable<z.ZodString>;
44
+ clientCommitSha: z.ZodNullable<z.ZodString>;
45
45
  branch: z.ZodString;
46
46
  createdBranch: z.ZodNullable<z.ZodString>;
47
47
  creator: z.ZodNullable<z.ZodString>;
@@ -97,8 +97,8 @@ export type HistoricalModule = z.infer<typeof HistoricalModule>;
97
97
  export declare const HistoricalPatchSet: z.ZodObject<{
98
98
  commit: z.ZodObject<{
99
99
  commitSha: z.ZodString;
100
- parentCommitSha: z.ZodString;
101
- clientCommitSha: z.ZodString;
100
+ parentCommitSha: z.ZodNullable<z.ZodString>;
101
+ clientCommitSha: z.ZodNullable<z.ZodString>;
102
102
  branch: z.ZodString;
103
103
  createdBranch: z.ZodNullable<z.ZodString>;
104
104
  creator: z.ZodNullable<z.ZodString>;
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod";
2
2
  export declare const ValCommit: z.ZodObject<{
3
3
  commitSha: z.ZodString;
4
- clientCommitSha: z.ZodString;
5
- parentCommitSha: z.ZodString;
4
+ clientCommitSha: z.ZodNullable<z.ZodString>;
5
+ parentCommitSha: z.ZodNullable<z.ZodString>;
6
6
  branch: z.ZodString;
7
7
  creator: z.ZodString;
8
8
  createdAt: z.ZodString;
@@ -1220,8 +1220,16 @@ var SerializedSchema = zod.z.union([SerializedStringSchema, SerializedLiteralSch
1220
1220
 
1221
1221
  var ValCommit = zod.z.object({
1222
1222
  commitSha: zod.z.string(),
1223
- clientCommitSha: zod.z.string(),
1224
- parentCommitSha: zod.z.string(),
1223
+ /**
1224
+ * Where the publisher said it was, or `null` when it did not say.
1225
+ *
1226
+ * Nullable since the content service mints its own commit shas rather than
1227
+ * taking git's: most publishers no longer bake a commit at all, and a root
1228
+ * commit -- the first publish of a project with no repository -- has no
1229
+ * parent for the same reason.
1230
+ */
1231
+ clientCommitSha: zod.z.string().nullable(),
1232
+ parentCommitSha: zod.z.string().nullable(),
1225
1233
  branch: zod.z.string(),
1226
1234
  creator: zod.z.string(),
1227
1235
  createdAt: zod.z.string(),
@@ -1293,8 +1301,10 @@ var HistoryError = zod.z.discriminatedUnion("kind", [zod.z.object({
1293
1301
  })]);
1294
1302
  var HistoricalCommit = zod.z.object({
1295
1303
  commitSha: zod.z.string(),
1296
- parentCommitSha: zod.z.string(),
1297
- clientCommitSha: zod.z.string(),
1304
+ /** `null` for a root commit. See `ValCommit`. */
1305
+ parentCommitSha: zod.z.string().nullable(),
1306
+ /** `null` when the publisher did not say where it was. See `ValCommit`. */
1307
+ clientCommitSha: zod.z.string().nullable(),
1298
1308
  branch: zod.z.string(),
1299
1309
  createdBranch: zod.z.string().nullable(),
1300
1310
  creator: zod.z.string().nullable(),
@@ -1415,6 +1425,27 @@ var GenericError = zod.z.object({
1415
1425
  message: zod.z.string()
1416
1426
  });
1417
1427
 
1428
+ /**
1429
+ * Why a publish cannot happen, in a form the Studio can show.
1430
+ *
1431
+ * Carried on `/stat` so the action is disabled with the reason beside it, and
1432
+ * on `/save`'s 409 so the same answer is given to a client that asked anyway --
1433
+ * a stat can be a poll out of date, and nothing may reach the commit path on a
1434
+ * project this deployment cannot publish.
1435
+ *
1436
+ * `no-base` is the only code so far: there is nowhere for this publish's
1437
+ * commit to be based. Note which way round that is. A project whose content
1438
+ * service is the store of record always HAS a base -- the service's own chain,
1439
+ * which mints its own shas -- so this is not about a missing git repository.
1440
+ * It is about a deployment that cannot do what its project requires: one built
1441
+ * before the project attached a repository, now asked to mirror commits into
1442
+ * one it knows no commit for.
1443
+ */
1444
+ var PublishRefusal = zod.z.object({
1445
+ code: zod.z.literal("no-base"),
1446
+ message: zod.z.string()
1447
+ });
1448
+
1418
1449
  /**
1419
1450
  * A patch group: the set of patches one user has chosen to publish.
1420
1451
  *
@@ -1810,7 +1841,8 @@ var Api = {
1810
1841
  config: ValConfig,
1811
1842
  profileId: zod.z.string().nullable(),
1812
1843
  mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")]),
1813
- jsonEntriesSha: zod.z.string().optional()
1844
+ jsonEntriesSha: zod.z.string().optional(),
1845
+ publishRefusal: PublishRefusal.optional()
1814
1846
  }), zod.z.object({
1815
1847
  type: zod.z.literal("use-websocket"),
1816
1848
  url: zod.z.string(),
@@ -1818,7 +1850,16 @@ var Api = {
1818
1850
  baseSha: zod.z.string(),
1819
1851
  schemaSha: zod.z.string(),
1820
1852
  sourcesSha: zod.z.string(),
1821
- commitSha: zod.z.string(),
1853
+ /**
1854
+ * The commit this deployment was built from.
1855
+ *
1856
+ * Optional: a project whose content service is the store of
1857
+ * record has no repository and no commit baked into its build.
1858
+ * The Studio has always parsed this as optional -- see
1859
+ * `useStatus` -- and shows a branch and a commit only when there
1860
+ * is one.
1861
+ */
1862
+ commitSha: zod.z.string().optional(),
1822
1863
  patches: zod.z.array(PatchId),
1823
1864
  /**
1824
1865
  * Of `patches`, the ones that have already SHIPPED.
@@ -1862,7 +1903,8 @@ var Api = {
1862
1903
  deployments: zod.z.array(ValDeployment).optional(),
1863
1904
  config: ValConfig,
1864
1905
  profileId: zod.z.string().nullable(),
1865
- mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")])
1906
+ mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")]),
1907
+ publishRefusal: PublishRefusal.optional()
1866
1908
  })])
1867
1909
  })])
1868
1910
  }
@@ -2383,7 +2425,19 @@ var Api = {
2383
2425
  json: GenericError
2384
2426
  }), zod.z.object({
2385
2427
  status: zod.z.literal(409),
2386
- json: zod.z.union([/** A group that has already shipped. See the 403 above. */
2428
+ json: zod.z.union([
2429
+ /*
2430
+ * This deployment cannot publish this project at all.
2431
+ *
2432
+ * First in the union so it is read before the bare
2433
+ * `{ message }` above it would swallow it. `/stat` carries the
2434
+ * same refusal, so a Studio that is up to date never sends a save
2435
+ * that lands here -- this is for the one that is a poll behind.
2436
+ */
2437
+ zod.z.object({
2438
+ message: zod.z.string(),
2439
+ publishRefusal: PublishRefusal
2440
+ }), /** A group that has already shipped. See the 403 above. */
2387
2441
  zod.z.object({
2388
2442
  message: zod.z.string()
2389
2443
  }), zod.z.object({
@@ -4610,6 +4664,18 @@ function clampDateTimeString(value, options) {
4610
4664
  * validation then reports, rather than wrong ones.
4611
4665
  */
4612
4666
 
4667
+ /**
4668
+ * The empty value for a schema — the thing a "create this" affordance writes.
4669
+ *
4670
+ * Returns `JSONValue`, the MUTABLE shape, rather than `Json`. Every value here
4671
+ * is built fresh on the way out — `Object.fromEntries`, a new array, a
4672
+ * primitive — and none of it aliases anything a caller could then mutate out
4673
+ * from under someone. Saying `Json` instead made the type readonly, which is
4674
+ * a claim about sharing that is not true of anything this returns, and left
4675
+ * every caller asserting `as JSONValue` at the point of writing a patch. A
4676
+ * `JSONValue` is still assignable to `Json`, so a reader of the value loses
4677
+ * nothing.
4678
+ */
4613
4679
  function emptyOf(schema, context) {
4614
4680
  if (schema.type === "object") {
4615
4681
  return Object.fromEntries(Object.keys(schema.items).map(function (key) {
@@ -1220,8 +1220,16 @@ var SerializedSchema = zod.z.union([SerializedStringSchema, SerializedLiteralSch
1220
1220
 
1221
1221
  var ValCommit = zod.z.object({
1222
1222
  commitSha: zod.z.string(),
1223
- clientCommitSha: zod.z.string(),
1224
- parentCommitSha: zod.z.string(),
1223
+ /**
1224
+ * Where the publisher said it was, or `null` when it did not say.
1225
+ *
1226
+ * Nullable since the content service mints its own commit shas rather than
1227
+ * taking git's: most publishers no longer bake a commit at all, and a root
1228
+ * commit -- the first publish of a project with no repository -- has no
1229
+ * parent for the same reason.
1230
+ */
1231
+ clientCommitSha: zod.z.string().nullable(),
1232
+ parentCommitSha: zod.z.string().nullable(),
1225
1233
  branch: zod.z.string(),
1226
1234
  creator: zod.z.string(),
1227
1235
  createdAt: zod.z.string(),
@@ -1293,8 +1301,10 @@ var HistoryError = zod.z.discriminatedUnion("kind", [zod.z.object({
1293
1301
  })]);
1294
1302
  var HistoricalCommit = zod.z.object({
1295
1303
  commitSha: zod.z.string(),
1296
- parentCommitSha: zod.z.string(),
1297
- clientCommitSha: zod.z.string(),
1304
+ /** `null` for a root commit. See `ValCommit`. */
1305
+ parentCommitSha: zod.z.string().nullable(),
1306
+ /** `null` when the publisher did not say where it was. See `ValCommit`. */
1307
+ clientCommitSha: zod.z.string().nullable(),
1298
1308
  branch: zod.z.string(),
1299
1309
  createdBranch: zod.z.string().nullable(),
1300
1310
  creator: zod.z.string().nullable(),
@@ -1415,6 +1425,27 @@ var GenericError = zod.z.object({
1415
1425
  message: zod.z.string()
1416
1426
  });
1417
1427
 
1428
+ /**
1429
+ * Why a publish cannot happen, in a form the Studio can show.
1430
+ *
1431
+ * Carried on `/stat` so the action is disabled with the reason beside it, and
1432
+ * on `/save`'s 409 so the same answer is given to a client that asked anyway --
1433
+ * a stat can be a poll out of date, and nothing may reach the commit path on a
1434
+ * project this deployment cannot publish.
1435
+ *
1436
+ * `no-base` is the only code so far: there is nowhere for this publish's
1437
+ * commit to be based. Note which way round that is. A project whose content
1438
+ * service is the store of record always HAS a base -- the service's own chain,
1439
+ * which mints its own shas -- so this is not about a missing git repository.
1440
+ * It is about a deployment that cannot do what its project requires: one built
1441
+ * before the project attached a repository, now asked to mirror commits into
1442
+ * one it knows no commit for.
1443
+ */
1444
+ var PublishRefusal = zod.z.object({
1445
+ code: zod.z.literal("no-base"),
1446
+ message: zod.z.string()
1447
+ });
1448
+
1418
1449
  /**
1419
1450
  * A patch group: the set of patches one user has chosen to publish.
1420
1451
  *
@@ -1810,7 +1841,8 @@ var Api = {
1810
1841
  config: ValConfig,
1811
1842
  profileId: zod.z.string().nullable(),
1812
1843
  mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")]),
1813
- jsonEntriesSha: zod.z.string().optional()
1844
+ jsonEntriesSha: zod.z.string().optional(),
1845
+ publishRefusal: PublishRefusal.optional()
1814
1846
  }), zod.z.object({
1815
1847
  type: zod.z.literal("use-websocket"),
1816
1848
  url: zod.z.string(),
@@ -1818,7 +1850,16 @@ var Api = {
1818
1850
  baseSha: zod.z.string(),
1819
1851
  schemaSha: zod.z.string(),
1820
1852
  sourcesSha: zod.z.string(),
1821
- commitSha: zod.z.string(),
1853
+ /**
1854
+ * The commit this deployment was built from.
1855
+ *
1856
+ * Optional: a project whose content service is the store of
1857
+ * record has no repository and no commit baked into its build.
1858
+ * The Studio has always parsed this as optional -- see
1859
+ * `useStatus` -- and shows a branch and a commit only when there
1860
+ * is one.
1861
+ */
1862
+ commitSha: zod.z.string().optional(),
1822
1863
  patches: zod.z.array(PatchId),
1823
1864
  /**
1824
1865
  * Of `patches`, the ones that have already SHIPPED.
@@ -1862,7 +1903,8 @@ var Api = {
1862
1903
  deployments: zod.z.array(ValDeployment).optional(),
1863
1904
  config: ValConfig,
1864
1905
  profileId: zod.z.string().nullable(),
1865
- mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")])
1906
+ mode: zod.z.union([zod.z.literal("http"), zod.z.literal("fs")]),
1907
+ publishRefusal: PublishRefusal.optional()
1866
1908
  })])
1867
1909
  })])
1868
1910
  }
@@ -2383,7 +2425,19 @@ var Api = {
2383
2425
  json: GenericError
2384
2426
  }), zod.z.object({
2385
2427
  status: zod.z.literal(409),
2386
- json: zod.z.union([/** A group that has already shipped. See the 403 above. */
2428
+ json: zod.z.union([
2429
+ /*
2430
+ * This deployment cannot publish this project at all.
2431
+ *
2432
+ * First in the union so it is read before the bare
2433
+ * `{ message }` above it would swallow it. `/stat` carries the
2434
+ * same refusal, so a Studio that is up to date never sends a save
2435
+ * that lands here -- this is for the one that is a poll behind.
2436
+ */
2437
+ zod.z.object({
2438
+ message: zod.z.string(),
2439
+ publishRefusal: PublishRefusal
2440
+ }), /** A group that has already shipped. See the 403 above. */
2387
2441
  zod.z.object({
2388
2442
  message: zod.z.string()
2389
2443
  }), zod.z.object({
@@ -4610,6 +4664,18 @@ function clampDateTimeString(value, options) {
4610
4664
  * validation then reports, rather than wrong ones.
4611
4665
  */
4612
4666
 
4667
+ /**
4668
+ * The empty value for a schema — the thing a "create this" affordance writes.
4669
+ *
4670
+ * Returns `JSONValue`, the MUTABLE shape, rather than `Json`. Every value here
4671
+ * is built fresh on the way out — `Object.fromEntries`, a new array, a
4672
+ * primitive — and none of it aliases anything a caller could then mutate out
4673
+ * from under someone. Saying `Json` instead made the type readonly, which is
4674
+ * a claim about sharing that is not true of anything this returns, and left
4675
+ * every caller asserting `as JSONValue` at the point of writing a patch. A
4676
+ * `JSONValue` is still assignable to `Json`, so a reader of the value loses
4677
+ * nothing.
4678
+ */
4613
4679
  function emptyOf(schema, context) {
4614
4680
  if (schema.type === "object") {
4615
4681
  return Object.fromEntries(Object.keys(schema.items).map(function (key) {
@@ -1213,8 +1213,16 @@ var SerializedSchema = z.union([SerializedStringSchema, SerializedLiteralSchema,
1213
1213
 
1214
1214
  var ValCommit = z.object({
1215
1215
  commitSha: z.string(),
1216
- clientCommitSha: z.string(),
1217
- parentCommitSha: z.string(),
1216
+ /**
1217
+ * Where the publisher said it was, or `null` when it did not say.
1218
+ *
1219
+ * Nullable since the content service mints its own commit shas rather than
1220
+ * taking git's: most publishers no longer bake a commit at all, and a root
1221
+ * commit -- the first publish of a project with no repository -- has no
1222
+ * parent for the same reason.
1223
+ */
1224
+ clientCommitSha: z.string().nullable(),
1225
+ parentCommitSha: z.string().nullable(),
1218
1226
  branch: z.string(),
1219
1227
  creator: z.string(),
1220
1228
  createdAt: z.string(),
@@ -1286,8 +1294,10 @@ var HistoryError = z.discriminatedUnion("kind", [z.object({
1286
1294
  })]);
1287
1295
  var HistoricalCommit = z.object({
1288
1296
  commitSha: z.string(),
1289
- parentCommitSha: z.string(),
1290
- clientCommitSha: z.string(),
1297
+ /** `null` for a root commit. See `ValCommit`. */
1298
+ parentCommitSha: z.string().nullable(),
1299
+ /** `null` when the publisher did not say where it was. See `ValCommit`. */
1300
+ clientCommitSha: z.string().nullable(),
1291
1301
  branch: z.string(),
1292
1302
  createdBranch: z.string().nullable(),
1293
1303
  creator: z.string().nullable(),
@@ -1408,6 +1418,27 @@ var GenericError = z.object({
1408
1418
  message: z.string()
1409
1419
  });
1410
1420
 
1421
+ /**
1422
+ * Why a publish cannot happen, in a form the Studio can show.
1423
+ *
1424
+ * Carried on `/stat` so the action is disabled with the reason beside it, and
1425
+ * on `/save`'s 409 so the same answer is given to a client that asked anyway --
1426
+ * a stat can be a poll out of date, and nothing may reach the commit path on a
1427
+ * project this deployment cannot publish.
1428
+ *
1429
+ * `no-base` is the only code so far: there is nowhere for this publish's
1430
+ * commit to be based. Note which way round that is. A project whose content
1431
+ * service is the store of record always HAS a base -- the service's own chain,
1432
+ * which mints its own shas -- so this is not about a missing git repository.
1433
+ * It is about a deployment that cannot do what its project requires: one built
1434
+ * before the project attached a repository, now asked to mirror commits into
1435
+ * one it knows no commit for.
1436
+ */
1437
+ var PublishRefusal = z.object({
1438
+ code: z.literal("no-base"),
1439
+ message: z.string()
1440
+ });
1441
+
1411
1442
  /**
1412
1443
  * A patch group: the set of patches one user has chosen to publish.
1413
1444
  *
@@ -1803,7 +1834,8 @@ var Api = {
1803
1834
  config: ValConfig,
1804
1835
  profileId: z.string().nullable(),
1805
1836
  mode: z.union([z.literal("http"), z.literal("fs")]),
1806
- jsonEntriesSha: z.string().optional()
1837
+ jsonEntriesSha: z.string().optional(),
1838
+ publishRefusal: PublishRefusal.optional()
1807
1839
  }), z.object({
1808
1840
  type: z.literal("use-websocket"),
1809
1841
  url: z.string(),
@@ -1811,7 +1843,16 @@ var Api = {
1811
1843
  baseSha: z.string(),
1812
1844
  schemaSha: z.string(),
1813
1845
  sourcesSha: z.string(),
1814
- commitSha: z.string(),
1846
+ /**
1847
+ * The commit this deployment was built from.
1848
+ *
1849
+ * Optional: a project whose content service is the store of
1850
+ * record has no repository and no commit baked into its build.
1851
+ * The Studio has always parsed this as optional -- see
1852
+ * `useStatus` -- and shows a branch and a commit only when there
1853
+ * is one.
1854
+ */
1855
+ commitSha: z.string().optional(),
1815
1856
  patches: z.array(PatchId),
1816
1857
  /**
1817
1858
  * Of `patches`, the ones that have already SHIPPED.
@@ -1855,7 +1896,8 @@ var Api = {
1855
1896
  deployments: z.array(ValDeployment).optional(),
1856
1897
  config: ValConfig,
1857
1898
  profileId: z.string().nullable(),
1858
- mode: z.union([z.literal("http"), z.literal("fs")])
1899
+ mode: z.union([z.literal("http"), z.literal("fs")]),
1900
+ publishRefusal: PublishRefusal.optional()
1859
1901
  })])
1860
1902
  })])
1861
1903
  }
@@ -2376,7 +2418,19 @@ var Api = {
2376
2418
  json: GenericError
2377
2419
  }), z.object({
2378
2420
  status: z.literal(409),
2379
- json: z.union([/** A group that has already shipped. See the 403 above. */
2421
+ json: z.union([
2422
+ /*
2423
+ * This deployment cannot publish this project at all.
2424
+ *
2425
+ * First in the union so it is read before the bare
2426
+ * `{ message }` above it would swallow it. `/stat` carries the
2427
+ * same refusal, so a Studio that is up to date never sends a save
2428
+ * that lands here -- this is for the one that is a poll behind.
2429
+ */
2430
+ z.object({
2431
+ message: z.string(),
2432
+ publishRefusal: PublishRefusal
2433
+ }), /** A group that has already shipped. See the 403 above. */
2380
2434
  z.object({
2381
2435
  message: z.string()
2382
2436
  }), z.object({
@@ -4603,6 +4657,18 @@ function clampDateTimeString(value, options) {
4603
4657
  * validation then reports, rather than wrong ones.
4604
4658
  */
4605
4659
 
4660
+ /**
4661
+ * The empty value for a schema — the thing a "create this" affordance writes.
4662
+ *
4663
+ * Returns `JSONValue`, the MUTABLE shape, rather than `Json`. Every value here
4664
+ * is built fresh on the way out — `Object.fromEntries`, a new array, a
4665
+ * primitive — and none of it aliases anything a caller could then mutate out
4666
+ * from under someone. Saying `Json` instead made the type readonly, which is
4667
+ * a claim about sharing that is not true of anything this returns, and left
4668
+ * every caller asserting `as JSONValue` at the point of writing a patch. A
4669
+ * `JSONValue` is still assignable to `Json`, so a reader of the value loses
4670
+ * nothing.
4671
+ */
4606
4672
  function emptyOf(schema, context) {
4607
4673
  if (schema.type === "object") {
4608
4674
  return Object.fromEntries(Object.keys(schema.items).map(function (key) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valbuild/shared",
3
- "version": "0.129.0",
3
+ "version": "0.133.0",
4
4
  "private": false,
5
5
  "description": "Val shared types and utilities",
6
6
  "repository": {
@@ -37,7 +37,7 @@
37
37
  "flexsearch": "^0.8.212",
38
38
  "zod": "^4.4.3",
39
39
  "zod-validation-error": "^5.0.0",
40
- "@valbuild/core": "0.129.0"
40
+ "@valbuild/core": "0.130.0"
41
41
  },
42
42
  "files": [
43
43
  "CHANGELOG.md",