@valbuild/shared 0.118.0 → 0.121.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.
@@ -1123,25 +1123,24 @@ var SerializedImageSchema = zod.z.object({
1123
1123
  });
1124
1124
  var RichTextOptions = zod.z.lazy(function () {
1125
1125
  return zod.z.object({
1126
- style: zod.z.object({
1127
- bold: zod.z["boolean"]().optional(),
1128
- italic: zod.z["boolean"]().optional(),
1129
- lineThrough: zod.z["boolean"]().optional()
1130
- }).optional(),
1131
- block: zod.z.object({
1132
- h1: zod.z["boolean"]().optional(),
1133
- h2: zod.z["boolean"]().optional(),
1134
- h3: zod.z["boolean"]().optional(),
1135
- h4: zod.z["boolean"]().optional(),
1136
- h5: zod.z["boolean"]().optional(),
1137
- h6: zod.z["boolean"]().optional(),
1138
- ul: zod.z["boolean"]().optional(),
1139
- ol: zod.z["boolean"]().optional()
1140
- }).optional(),
1141
- inline: zod.z.object({
1142
- a: zod.z.union([zod.z["boolean"](), SerializedRouteSchema, SerializedStringSchema]).optional(),
1143
- img: zod.z.union([zod.z["boolean"](), SerializedImageSchema]).optional()
1144
- }).optional()
1126
+ // Set by `.maxLength()` / `.minLength()`, not by the options argument.
1127
+ // These z.objects strip unknown keys, so leaving them out drops the
1128
+ // length constraints in transit.
1129
+ maxLength: zod.z.number().optional(),
1130
+ minLength: zod.z.number().optional(),
1131
+ bold: zod.z["boolean"]().optional(),
1132
+ italic: zod.z["boolean"]().optional(),
1133
+ lineThrough: zod.z["boolean"]().optional(),
1134
+ h1: zod.z["boolean"]().optional(),
1135
+ h2: zod.z["boolean"]().optional(),
1136
+ h3: zod.z["boolean"]().optional(),
1137
+ h4: zod.z["boolean"]().optional(),
1138
+ h5: zod.z["boolean"]().optional(),
1139
+ h6: zod.z["boolean"]().optional(),
1140
+ ul: zod.z["boolean"]().optional(),
1141
+ ol: zod.z["boolean"]().optional(),
1142
+ a: zod.z.union([zod.z["boolean"](), SerializedRouteSchema, SerializedStringSchema]).optional(),
1143
+ img: zod.z.union([zod.z["boolean"](), SerializedImageSchema]).optional()
1145
1144
  });
1146
1145
  });
1147
1146
  var SerializedRichTextSchema = zod.z.object({
@@ -1334,6 +1333,21 @@ var notFoundResponse = zod.z.object({
1334
1333
  var GenericError = zod.z.object({
1335
1334
  message: zod.z.string()
1336
1335
  });
1336
+
1337
+ /**
1338
+ * A patch group: the set of patches one user has chosen to publish.
1339
+ *
1340
+ * Not a patch *set* — a patch set is computed from the schema and says which
1341
+ * patches must move together; a patch group is curated and says which ones this
1342
+ * user wants live. See `docs/independent-publish/PLAN.md`.
1343
+ */
1344
+ var PatchGroup = zod.z.object({
1345
+ patchGroupId: zod.z.string(),
1346
+ authorId: zod.z.string().nullable(),
1347
+ createdAt: zod.z.string(),
1348
+ publishedAt: zod.z.string().nullable(),
1349
+ patchIds: zod.z.array(PatchId)
1350
+ });
1337
1351
  var GenericPatchError = zod.z.union([zod.z.object({
1338
1352
  patchId: PatchId,
1339
1353
  message: zod.z.string()
@@ -1715,6 +1729,30 @@ var Api = {
1715
1729
  schemaSha: zod.z.string(),
1716
1730
  sourcesSha: zod.z.string(),
1717
1731
  patches: zod.z.array(PatchId),
1732
+ /**
1733
+ * Of `patches`, the ones that have already SHIPPED.
1734
+ *
1735
+ * A published patch stays in the chain with `appliedAt` set until
1736
+ * the next deployment moves the base, so the id list alone cannot
1737
+ * say which is which — and a client never re-fetches a record it
1738
+ * already holds, so it never finds out on its own. Another
1739
+ * author's publish then left that patch pending in your scope,
1740
+ * your prefix gate read a hole in front of it, and Publish
1741
+ * refused for a reason that had stopped being true.
1742
+ *
1743
+ * Optional: `fs` mode forgets published patches outright, and a
1744
+ * client that ignores this behaves exactly as it did before.
1745
+ */
1746
+ appliedPatches: zod.z.array(PatchId).optional(),
1747
+ /**
1748
+ * The newest commit, which is the PUBLISH head.
1749
+ *
1750
+ * Not `commitSha`: that is the commit this deployment serves and
1751
+ * moves only when a new build lands. This moves the instant
1752
+ * somebody publishes, which is what makes it the thing a client
1753
+ * carries back to `/save` to say which world it decided against.
1754
+ */
1755
+ headCommitSha: zod.z.string().optional(),
1718
1756
  /**
1719
1757
  * Unpublished changes the store threw away because it could not
1720
1758
  * read them.
@@ -1743,6 +1781,30 @@ var Api = {
1743
1781
  sourcesSha: zod.z.string(),
1744
1782
  commitSha: zod.z.string(),
1745
1783
  patches: zod.z.array(PatchId),
1784
+ /**
1785
+ * Of `patches`, the ones that have already SHIPPED.
1786
+ *
1787
+ * A published patch stays in the chain with `appliedAt` set until
1788
+ * the next deployment moves the base, so the id list alone cannot
1789
+ * say which is which — and a client never re-fetches a record it
1790
+ * already holds, so it never finds out on its own. Another
1791
+ * author's publish then left that patch pending in your scope,
1792
+ * your prefix gate read a hole in front of it, and Publish
1793
+ * refused for a reason that had stopped being true.
1794
+ *
1795
+ * Optional: `fs` mode forgets published patches outright, and a
1796
+ * client that ignores this behaves exactly as it did before.
1797
+ */
1798
+ appliedPatches: zod.z.array(PatchId).optional(),
1799
+ /**
1800
+ * The newest commit, which is the PUBLISH head.
1801
+ *
1802
+ * Not `commitSha`: that is the commit this deployment serves and
1803
+ * moves only when a new build lands. This moves the instant
1804
+ * somebody publishes, which is what makes it the thing a client
1805
+ * carries back to `/save` to say which world it decided against.
1806
+ */
1807
+ headCommitSha: zod.z.string().optional(),
1746
1808
  commits: zod.z.array(ValCommit),
1747
1809
  config: ValConfig,
1748
1810
  profileId: zod.z.string().nullable(),
@@ -1812,6 +1874,26 @@ var Api = {
1812
1874
  query: {
1813
1875
  id: zod.z.array(PatchId).min(1, "At least one patch id is required")
1814
1876
  },
1877
+ body: zod.z.object({
1878
+ /*
1879
+ * Patches that are NOT being deleted but must lose their patch
1880
+ * group membership because these are.
1881
+ *
1882
+ * Deleting a patch out of the middle of a patch set leaves every
1883
+ * group holding the rest with a non-prefix intersection, and a
1884
+ * prefix is the one invariant a group has. Which patches those are
1885
+ * needs the content schema, so the client computes the forward
1886
+ * closure and sends it; the content API drops the memberships
1887
+ * without touching the patches.
1888
+ *
1889
+ * In the BODY while the ids to delete are in the query, because
1890
+ * this list is unbounded in the same way and the query string is
1891
+ * already chunked to fit. Optional throughout: `fs` mode has no
1892
+ * groups, and a client that predates this is deleting correctly,
1893
+ * just not repairing.
1894
+ */
1895
+ unstagePatchIds: zod.z.array(PatchId).optional()
1896
+ }).optional(),
1815
1897
  cookies: {
1816
1898
  val_session: zod.z.string().optional()
1817
1899
  }
@@ -1836,7 +1918,16 @@ var Api = {
1836
1918
  path: ModuleFilePath,
1837
1919
  patchId: PatchId,
1838
1920
  patch: zod.z.any() // TODO: this should be Patch instead - we got a weird validation error: although input looks good, it still does not accept objects as values... Which it should do via the z.record(JSONValue) type
1839
- }))
1921
+ })),
1922
+ // Patch group membership. Sent with the patch itself, in one request, so
1923
+ // there is no window in which a patch exists but belongs to no group.
1924
+ // Absent means "the caller does not know about groups" - the server then
1925
+ // behaves exactly as before.
1926
+ patchGroupId: zod.z.string().nullish(),
1927
+ // What has to come with the patch: other patch ids that must join the
1928
+ // same group for it to stay applicable. Computed on the client, which
1929
+ // is the only side that has the schema needed to derive patch sets.
1930
+ withPatchIds: zod.z.array(PatchId).optional()
1840
1931
  }),
1841
1932
  cookies: {
1842
1933
  val_session: zod.z.string().optional()
@@ -1862,7 +1953,8 @@ var Api = {
1862
1953
  status: zod.z.literal(200),
1863
1954
  json: zod.z.object({
1864
1955
  newPatchIds: zod.z.array(PatchId),
1865
- parentRef: ParentRef
1956
+ parentRef: ParentRef,
1957
+ patchGroupId: zod.z.string().optional()
1866
1958
  })
1867
1959
  })])
1868
1960
  },
@@ -1870,7 +1962,16 @@ var Api = {
1870
1962
  req: {
1871
1963
  query: {
1872
1964
  patch_id: zod.z.array(PatchId).optional(),
1873
- exclude_patch_ops: onlyOneBooleanQueryParam.optional()
1965
+ exclude_patch_ops: onlyOneBooleanQueryParam.optional(),
1966
+ /**
1967
+ * Include the patch group annotation in the answer.
1968
+ *
1969
+ * Off by default because the client CHUNKS this request — a long
1970
+ * chain is several round trips — and the groups are a property of the
1971
+ * branch, identical in every chunk. Asking on each one is N identical
1972
+ * lookups against the content API to learn the same thing.
1973
+ */
1974
+ include_patch_groups: onlyOneBooleanQueryParam.optional()
1874
1975
  },
1875
1976
  cookies: {
1876
1977
  val_session: zod.z.string().optional()
@@ -1899,8 +2000,12 @@ var Api = {
1899
2000
  authorId: zod.z.string().nullable(),
1900
2001
  appliedAt: zod.z.object({
1901
2002
  commitSha: zod.z.string()
1902
- }).nullable()
2003
+ }).nullable(),
2004
+ // Which patch groups hold this patch. Optional so that FS mode, and
2005
+ // any server that predates patch groups, can leave it out.
2006
+ patchGroupIds: zod.z.array(zod.z.string()).optional()
1903
2007
  })),
2008
+ patchGroups: zod.z.array(PatchGroup).optional(),
1904
2009
  baseSha: zod.z.string(),
1905
2010
  error: GenericError.optional(),
1906
2011
  errors: zod.z.record(PatchId, GenericError).optional()
@@ -1908,6 +2013,91 @@ var Api = {
1908
2013
  })])
1909
2014
  }
1910
2015
  },
2016
+ // Patch group membership. Both are idempotent set operations, and both take an
2017
+ // already-closed set: the client computes the prefix closure (staging) or the
2018
+ // forward closure (unstaging) because only the client has the schema needed to
2019
+ // derive patch sets. See `docs/independent-publish/PLAN.md`.
2020
+ "/patch-groups/~/patches": {
2021
+ PUT: {
2022
+ req: {
2023
+ body: zod.z.object({
2024
+ patchGroupId: zod.z.string(),
2025
+ /** What the user asked to stage. */
2026
+ patchIds: zod.z.array(PatchId),
2027
+ /*
2028
+ * What has to come with it, because the staged patches are written on
2029
+ * top of it.
2030
+ *
2031
+ * The content API records membership as `explicit` or `dependency`
2032
+ * and reads what it is not told about as a dependency — so folding
2033
+ * the two halves into `patchIds` files the patch someone clicked as
2034
+ * something the closure dragged in.
2035
+ */
2036
+ withPatchIds: zod.z.array(PatchId).optional()
2037
+ }).refine(function (body) {
2038
+ var _body$withPatchIds$le, _body$withPatchIds;
2039
+ return body.patchIds.length > 0 || ((_body$withPatchIds$le = (_body$withPatchIds = body.withPatchIds) === null || _body$withPatchIds === void 0 ? void 0 : _body$withPatchIds.length) !== null && _body$withPatchIds$le !== void 0 ? _body$withPatchIds$le : 0) > 0;
2040
+ },
2041
+ // Either half alone is a real request: a stage of only what came with
2042
+ // it happens when the deferred queue's scope filter drops everything
2043
+ // the user originally clicked.
2044
+ "At least one patch id is required"),
2045
+ cookies: {
2046
+ val_session: zod.z.string().optional()
2047
+ }
2048
+ },
2049
+ res: zod.z.union([unauthorizedResponse, zod.z.object({
2050
+ status: zod.z.literal(403),
2051
+ json: GenericError
2052
+ }), zod.z.object({
2053
+ // The group has already been published, so its membership is frozen.
2054
+ status: zod.z.literal(409),
2055
+ json: GenericError
2056
+ }), zod.z.object({
2057
+ status: zod.z.literal(500),
2058
+ json: GenericError
2059
+ }), zod.z.object({
2060
+ status: zod.z.literal(200),
2061
+ json: zod.z.object({
2062
+ patchGroupId: zod.z.string(),
2063
+ patchIds: zod.z.array(PatchId)
2064
+ })
2065
+ })])
2066
+ },
2067
+ DELETE: {
2068
+ req: {
2069
+ body: zod.z.object({
2070
+ patchGroupId: zod.z.string(),
2071
+ /** What the user asked to unstage. */
2072
+ patchIds: zod.z.array(PatchId),
2073
+ /** What has to go with it: everything built on top of it. */
2074
+ withPatchIds: zod.z.array(PatchId).optional()
2075
+ }).refine(function (body) {
2076
+ var _body$withPatchIds$le2, _body$withPatchIds2;
2077
+ return body.patchIds.length > 0 || ((_body$withPatchIds$le2 = (_body$withPatchIds2 = body.withPatchIds) === null || _body$withPatchIds2 === void 0 ? void 0 : _body$withPatchIds2.length) !== null && _body$withPatchIds$le2 !== void 0 ? _body$withPatchIds$le2 : 0) > 0;
2078
+ }, "At least one patch id is required"),
2079
+ cookies: {
2080
+ val_session: zod.z.string().optional()
2081
+ }
2082
+ },
2083
+ res: zod.z.union([unauthorizedResponse, zod.z.object({
2084
+ status: zod.z.literal(403),
2085
+ json: GenericError
2086
+ }), zod.z.object({
2087
+ status: zod.z.literal(409),
2088
+ json: GenericError
2089
+ }), zod.z.object({
2090
+ status: zod.z.literal(500),
2091
+ json: GenericError
2092
+ }), zod.z.object({
2093
+ status: zod.z.literal(200),
2094
+ json: zod.z.object({
2095
+ patchGroupId: zod.z.string(),
2096
+ patchIds: zod.z.array(PatchId)
2097
+ })
2098
+ })])
2099
+ }
2100
+ },
1911
2101
  "/schema": {
1912
2102
  GET: {
1913
2103
  req: {
@@ -1944,7 +2134,34 @@ var Api = {
1944
2134
  validate_sources: onlyOneBooleanQueryParam.optional(),
1945
2135
  validate_binary_files: onlyOneBooleanQueryParam.optional(),
1946
2136
  exclude_patches: onlyOneBooleanQueryParam.optional(),
1947
- apply_patches: onlyOneBooleanQueryParam.optional()
2137
+ apply_patches: onlyOneBooleanQueryParam.optional(),
2138
+ /**
2139
+ * Apply ONLY these patches, rather than everything pending.
2140
+ *
2141
+ * This is what makes a draft-mode render match the caller's staged
2142
+ * view. Without it `/sources/~` replays every pending patch on the
2143
+ * branch, so a server-rendered preview shows other people's
2144
+ * unpublished work — the one thing independent publish is for.
2145
+ *
2146
+ * Omitted means everything pending, which is the behaviour every
2147
+ * existing caller has and must keep. An EMPTY array is a different
2148
+ * answer and is honoured as one: a group holding nothing renders
2149
+ * base.
2150
+ */
2151
+ patch_id: zod.z.array(PatchId).optional(),
2152
+ /**
2153
+ * Apply only the patches in the CALLER'S OWN patch groups.
2154
+ *
2155
+ * The server resolves them from the session, so a draft render does
2156
+ * not have to know its own group ids — which it cannot, since it has
2157
+ * no client state. This is what makes `fetchVal` in draft mode show
2158
+ * the person their own staged work rather than everybody's.
2159
+ *
2160
+ * Ignored when `patch_id` is given: an explicit list is a caller that
2161
+ * already knows what it wants, and silently intersecting the two
2162
+ * would make the narrower one invisible.
2163
+ */
2164
+ own_patch_groups_only: onlyOneBooleanQueryParam.optional()
1948
2165
  },
1949
2166
  cookies: {
1950
2167
  val_session: zod.z.string().optional()
@@ -2023,7 +2240,35 @@ var Api = {
2023
2240
  req: {
2024
2241
  body: zod.z.object({
2025
2242
  message: zod.z.string().optional(),
2026
- patchIds: zod.z.array(PatchId)
2243
+ patchIds: zod.z.array(PatchId),
2244
+ /*
2245
+ * The patch group this commit EMPTIES, if it empties one.
2246
+ *
2247
+ * Forwarded to the content API, which closes the group it names —
2248
+ * and closes it unconditionally, without checking that the commit
2249
+ * shipped all of it. So the client sends it only when the publish
2250
+ * accounts for every patch the group still holds; a partial publish
2251
+ * omits it and the group stays open with the rest in it.
2252
+ *
2253
+ * Optional, and absent in `fs` mode, which has no groups.
2254
+ */
2255
+ patchGroupId: zod.z.string().optional(),
2256
+ /*
2257
+ * The newest commit this client knew about when it decided.
2258
+ *
2259
+ * Answered with 409 when the server sees a newer one, because then
2260
+ * somebody else published between the review screen being read and
2261
+ * Save being clicked, and what is about to ship was decided against a
2262
+ * world that no longer exists.
2263
+ *
2264
+ * Git's own not-fast-forward guard does not cover this: `ValServer`
2265
+ * fetches the chain and commits fresh at publish time, so the parent
2266
+ * commit it sends is always the server's current one.
2267
+ *
2268
+ * Optional, so a client that does not track it publishes exactly as
2269
+ * it did before.
2270
+ */
2271
+ expectedHeadCommitSha: zod.z.string().optional()
2027
2272
  }),
2028
2273
  cookies: {
2029
2274
  val_session: zod.z.string().optional()
@@ -2032,6 +2277,24 @@ var Api = {
2032
2277
  res: zod.z.union([unauthorizedResponse, zod.z.object({
2033
2278
  status: zod.z.literal(200),
2034
2279
  json: zod.z.object({
2280
+ /**
2281
+ * The commit this publish just made, which becomes the new publish
2282
+ * head.
2283
+ *
2284
+ * Sent back because nothing else tells this client about its own
2285
+ * commit in time. `headCommitSha` moves on a `/stat` response, and
2286
+ * until the next poll lands the client still believes the
2287
+ * PRE-publish head — so a second publish inside that window carried
2288
+ * a stale `expectedHeadCommitSha`, the server compared it against
2289
+ * the commit this same client had just made, and answered 409
2290
+ * "someone else published". With auto-publish, which publishes on
2291
+ * every pause in typing, that window is hit routinely and the
2292
+ * message blames a colleague for the user's own commit.
2293
+ *
2294
+ * Optional: `fs` mode has no publish head, and neither does a
2295
+ * server that predates this.
2296
+ */
2297
+ commitSha: zod.z.string().optional(),
2035
2298
  /**
2036
2299
  * Unpublished changes the save threw away because they could not be
2037
2300
  * applied.
@@ -2048,12 +2311,61 @@ var Api = {
2048
2311
  message: zod.z.string()
2049
2312
  })).optional()
2050
2313
  })
2314
+ }),
2315
+ /*
2316
+ * The named patch group is not this caller's, or the lookup that would
2317
+ * say so failed.
2318
+ *
2319
+ * Publishing may CLOSE a group, and closing somebody else's leaves
2320
+ * their pending work in a closed group and in no open one. So `/save`
2321
+ * runs the same ownership check stage and unstage do, and refuses the
2322
+ * same way — see `refuseUnlessOwn`.
2323
+ */
2324
+ zod.z.object({
2325
+ status: zod.z.literal(403),
2326
+ json: GenericError
2327
+ }), zod.z.object({
2328
+ status: zod.z.literal(500),
2329
+ json: GenericError
2051
2330
  }), zod.z.object({
2052
2331
  status: zod.z.literal(409),
2053
- json: zod.z.object({
2332
+ json: zod.z.union([/** A group that has already shipped. See the 403 above. */
2333
+ zod.z.object({
2334
+ message: zod.z.string()
2335
+ }), zod.z.object({
2054
2336
  message: zod.z.string(),
2055
2337
  isNotFastForward: zod.z.literal(true)
2056
- })
2338
+ }),
2339
+ /*
2340
+ * Somebody else published between this being decided and Save.
2341
+ *
2342
+ * Its own shape rather than another `isNotFastForward`, because
2343
+ * they happen at different moments and mean different things to the
2344
+ * user: that one is git refusing a commit, this one is refused
2345
+ * BEFORE a commit is attempted, on a head the client named. Nothing
2346
+ * was written either way, but only this one is answered by looking
2347
+ * at the review screen again.
2348
+ */
2349
+ zod.z.object({
2350
+ message: zod.z.string(),
2351
+ headMoved: zod.z.literal(true)
2352
+ }),
2353
+ /**
2354
+ * The group this commit named has already been published.
2355
+ *
2356
+ * A third thing sharing 409, and flagged for the same reason
2357
+ * `headMoved` is: read as a plain not-fast-forward it looks
2358
+ * retryable, and it is the opposite — the id will never be writable
2359
+ * again, so retrying reproduces it forever.
2360
+ *
2361
+ * It means this author published from somewhere else. The client
2362
+ * forgets the id and falls back to the annotation, which is the
2363
+ * same recovery a 409 on stage or unstage triggers.
2364
+ */
2365
+ zod.z.object({
2366
+ message: zod.z.string(),
2367
+ patchGroupPublished: zod.z.literal(true)
2368
+ })])
2057
2369
  }), zod.z.object({
2058
2370
  status: zod.z.literal(400),
2059
2371
  json: zod.z.union([zod.z.object({
@@ -2134,7 +2446,16 @@ var Api = {
2134
2446
  limit: onlyOneIntQueryParam.refine(function (arg) {
2135
2447
  return arg > 0 && arg <= JSON_ENTRIES_BATCH_MAX;
2136
2448
  }, "limit must be between 1 and ".concat(JSON_ENTRIES_BATCH_MAX)).optional(),
2137
- apply_patches: onlyOneBooleanQueryParam.optional()
2449
+ apply_patches: onlyOneBooleanQueryParam.optional(),
2450
+ /**
2451
+ * The caller's own staged work, and nobody else's.
2452
+ *
2453
+ * The same flag `/sources/~` takes, and for the same reason — a draft
2454
+ * page renders both, so a scoped module beside an unscoped
2455
+ * `jsonValues` entry showed one person's view and everybody's pending
2456
+ * work on the same screen.
2457
+ */
2458
+ own_patch_groups_only: onlyOneBooleanQueryParam.optional()
2138
2459
  },
2139
2460
  cookies: _defineProperty({}, VAL_SESSION_COOKIE, zod.z.string().optional())
2140
2461
  },
@@ -2708,6 +3029,44 @@ var urlOf = function urlOf() {
2708
3029
  return "".concat(route);
2709
3030
  };
2710
3031
 
3032
+ /**
3033
+ * The newest commit the client has been told about, or `null` if none.
3034
+ *
3035
+ * This is the **publish head**: unlike `baseSha`, which only moves once a
3036
+ * deployment lands, it moves the instant somebody publishes. That makes it the
3037
+ * one thing a client can carry to `/save` to say "this is the world I decided
3038
+ * against" — and the only way the server can tell that somebody else has
3039
+ * published since the review screen was read.
3040
+ *
3041
+ * In `shared` because both sides need the SAME answer: the client names a head
3042
+ * and the server compares against one, so two implementations of "newest" is two
3043
+ * ways to disagree about whether a publish is stale.
3044
+ *
3045
+ * By `createdAt` rather than by position, because the list's order is the
3046
+ * content API's business and nothing here should depend on it. Ties keep the
3047
+ * first seen, which is arbitrary and does not matter: two commits sharing a
3048
+ * timestamp are equally "since", and either answer refuses the same publishes.
3049
+ */
3050
+ function newestCommitSha(commits) {
3051
+ var _newest$commitSha, _newest;
3052
+ var newest = null;
3053
+ var _iterator = _createForOfIteratorHelper(commits !== null && commits !== void 0 ? commits : []),
3054
+ _step;
3055
+ try {
3056
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
3057
+ var commit = _step.value;
3058
+ if (newest === null || commit.createdAt > newest.createdAt) {
3059
+ newest = commit;
3060
+ }
3061
+ }
3062
+ } catch (err) {
3063
+ _iterator.e(err);
3064
+ } finally {
3065
+ _iterator.f();
3066
+ }
3067
+ return (_newest$commitSha = (_newest = newest) === null || _newest === void 0 ? void 0 : _newest.commitSha) !== null && _newest$commitSha !== void 0 ? _newest$commitSha : null;
3068
+ }
3069
+
2711
3070
  var VAL_CONFIG_SESSION_STORAGE_KEY = "val-config";
2712
3071
  var VAL_THEME_SESSION_STORAGE_KEY = "val-theme";
2713
3072
  var VAL_AI_SESSION_STORAGE_KEY = "val-ai-session";
@@ -2723,14 +3082,6 @@ var SharedValConfig = zod.z.object({
2723
3082
  ai: zod.z.object({
2724
3083
  commitMessages: zod.z.object({
2725
3084
  disabled: zod.z["boolean"]().optional()
2726
- }).optional(),
2727
- chat: zod.z.object({
2728
- experimental: zod.z.object({
2729
- enable: zod.z["boolean"]().optional()
2730
- }).optional(),
2731
- suggestions: zod.z.array(zod.z.string()).optional(),
2732
- title: zod.z.string().optional(),
2733
- description: zod.z.string().optional()
2734
3085
  }).optional()
2735
3086
  }).optional()
2736
3087
  });
@@ -3515,6 +3866,11 @@ function emptyOf(schema) {
3515
3866
  return [];
3516
3867
  } else if (schema.type === "record") {
3517
3868
  return {};
3869
+ } else if (schema.type === "settings") {
3870
+ // Not an object of empty sections: every settings key is optional, and
3871
+ // absent IS the empty value. Filling the sections in would write a shape
3872
+ // the project never asked for.
3873
+ return {};
3518
3874
  } else if (schema.opt) {
3519
3875
  return null;
3520
3876
  } else if (schema.type === "richtext") {
@@ -3667,7 +4023,7 @@ function getInlineImgInfo(richtextSchema) {
3667
4023
  if (richtextSchema.type !== "richtext") return {
3668
4024
  kind: "not-allowed"
3669
4025
  };
3670
- var img = (_richtextSchema$optio = richtextSchema.options) === null || _richtextSchema$optio === void 0 || (_richtextSchema$optio = _richtextSchema$optio.inline) === null || _richtextSchema$optio === void 0 ? void 0 : _richtextSchema$optio.img;
4026
+ var img = (_richtextSchema$optio = richtextSchema.options) === null || _richtextSchema$optio === void 0 ? void 0 : _richtextSchema$optio.img;
3671
4027
  if (!img) return {
3672
4028
  kind: "not-allowed"
3673
4029
  };
@@ -4229,6 +4585,7 @@ exports.JSON_ENTRIES_BATCH_MAX = JSON_ENTRIES_BATCH_MAX;
4229
4585
  exports.ParentRef = ParentRef$1;
4230
4586
  exports.Patch = Patch;
4231
4587
  exports.PatchBlock = PatchBlock;
4588
+ exports.PatchGroup = PatchGroup;
4232
4589
  exports.PatchId = PatchId;
4233
4590
  exports.PatchJSON = PatchJSON;
4234
4591
  exports.RemirrorBr = RemirrorBr;
@@ -4297,6 +4654,7 @@ exports.isValCanvasFrame = isValCanvasFrame;
4297
4654
  exports.isValCanvasPageMessage = isValCanvasPageMessage;
4298
4655
  exports.isValCanvasStudioMessage = isValCanvasStudioMessage;
4299
4656
  exports.levenshtein = levenshtein;
4657
+ exports.newestCommitSha = newestCommitSha;
4300
4658
  exports.parseRoutePattern = parseRoutePattern;
4301
4659
  exports.partitionValidationErrors = partitionValidationErrors;
4302
4660
  exports.remirrorToRichTextSource = remirrorToRichTextSource;